From 239a60096c220388f2c85840e7f8c5bbd353ee12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=82ngelo=20Tadeucci?= Date: Tue, 22 Apr 2025 21:26:02 -0300 Subject: [PATCH 1/9] KMS2 Changes --- .gitignore | 2 + .../Mapper/ServerTableMapper.cs | 2 +- Maple2.Model/Game/Emote.cs | 5 +- Maple2.Model/Game/Item/Item.cs | 6 +- Maple2.Model/Game/Item/ItemEnchant.cs | 4 +- Maple2.Model/Game/Item/ItemOption.cs | 18 +- Maple2.Model/Game/Item/ItemStats.cs | 16 +- Maple2.Model/Game/Item/ItemTransfer.cs | 4 +- Maple2.Model/Game/Sync/StateSync.cs | 78 +-- Maple2.Model/Game/User/SkillPoint.cs | 1 + Maple2.Server.Core/Constants/RecvOp.cs | 264 +++++----- Maple2.Server.Core/Constants/SendOp.cs | 494 +++++++++--------- Maple2.Server.Core/Network/Session.cs | 4 +- .../PacketHandlers/ResponseVersionHandler.cs | 2 +- .../Packets/CharacterListPacket.cs | 2 - .../Packets/Helper/EquipPacketHelper.cs | 2 +- .../Packets/ServerListPacket.cs | 1 + .../Manager/Config/SkillInfo.cs | 17 +- .../Manager/ItemMergeManager.cs | 20 +- .../Model/Field/Actor/FieldPlayer.cs | 10 +- .../Model/Field/Entity/FieldNpcSpawnPoint.cs | 2 +- Maple2.Server.Game/Model/Stats.cs | 6 +- .../PacketHandlers/GuildHandler.cs | 2 + .../PacketHandlers/ItemEquipHandler.cs | 8 +- .../PacketHandlers/SkillHandler.cs | 2 +- .../PacketHandlers/UserSyncHandler.cs | 5 +- Maple2.Server.Game/Packets/AdminPacket.cs | 16 + .../Packets/DungeonRoomPacket.cs | 2 + Maple2.Server.Game/Packets/EmotePacket.cs | 5 +- .../Packets/EnchantScrollPacket.cs | 3 - Maple2.Server.Game/Packets/EquipPacket.cs | 2 +- Maple2.Server.Game/Packets/FieldPacket.cs | 4 - .../Packets/ItemInventoryPacket.cs | 22 +- .../Packets/ServerEnterPacket.cs | 4 +- Maple2.Server.Game/Session/GameSession.cs | 2 + .../Util/ItemStatsCalculator.cs | 6 +- Maple2.Server.Game/appsettings.json | 2 +- .../CharacterManagementHandler.cs | 5 +- .../PacketHandlers/ResponseKeyHandler.cs | 4 + Maple2.Server.Login/appsettings.json | 2 +- .../Containers/BlackMarketLookup.cs | 6 +- 41 files changed, 527 insertions(+), 535 deletions(-) create mode 100644 Maple2.Server.Game/Packets/AdminPacket.cs diff --git a/.gitignore b/.gitignore index 86a6c2eb1..c0c6fce1a 100644 --- a/.gitignore +++ b/.gitignore @@ -375,3 +375,5 @@ FodyWeavers.xsd /Maple2.File.Ingest/Navmeshes/*.navmesh .idea/.idea.Maple2/.idea/sqldialects.xml .idea/.idea.Maple2/.idea/dataSources.xml +.idea/.idea.Maple2/.idea/dataSources.xml +.idea/.idea.Maple2/.idea/sqldialects.xml diff --git a/Maple2.File.Ingest/Mapper/ServerTableMapper.cs b/Maple2.File.Ingest/Mapper/ServerTableMapper.cs index 57f9c11f9..e0fe0f070 100644 --- a/Maple2.File.Ingest/Mapper/ServerTableMapper.cs +++ b/Maple2.File.Ingest/Mapper/ServerTableMapper.cs @@ -1397,7 +1397,7 @@ private ShopItemTable ParseShopItems() { restrictedBuyData = new RestrictedBuyData { Days = item.dayOfWeek.Length == 0 ? [] : Array.ConvertAll(item.dayOfWeek, day => (ShopBuyDay) day), TimeRanges = buyTimeOfDays, - StartTime = string.IsNullOrEmpty(item.startDate) ? 0 : DateTime.ParseExact(item.startDate, "yyyy-MM-dd-HH-mm", CultureInfo.InvariantCulture).ToEpochSeconds(), + StartTime = string.IsNullOrEmpty(item.startDate) ? 0 : DateTime.ParseExact(item.startDate, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds(), EndTime = string.IsNullOrEmpty(item.endDate) ? 0 : DateTime.ParseExact(item.endDate, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds() }; } diff --git a/Maple2.Model/Game/Emote.cs b/Maple2.Model/Game/Emote.cs index 138d121e9..4ee2b77d9 100644 --- a/Maple2.Model/Game/Emote.cs +++ b/Maple2.Model/Game/Emote.cs @@ -2,11 +2,14 @@ namespace Maple2.Model.Game; -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 16)] +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 26)] public readonly struct Emote { public readonly int Id; public readonly int Level = 1; + public readonly bool Unknown1; public readonly long ExpiryTime; + public readonly long UnknownTime; + public readonly bool Unknown2; public Emote(int id, long expiryTime = 0) { Id = id; diff --git a/Maple2.Model/Game/Item/Item.cs b/Maple2.Model/Game/Item/Item.cs index cd55958a2..840d68bf5 100644 --- a/Maple2.Model/Game/Item/Item.cs +++ b/Maple2.Model/Game/Item/Item.cs @@ -197,7 +197,6 @@ private long GetExpiryTime() { public void WriteTo(IByteWriter writer) { writer.WriteInt(Amount); - writer.WriteInt(); writer.WriteInt(-1); writer.WriteLong(CreationTime); writer.WriteLong(ExpiryTime); @@ -223,19 +222,18 @@ public void WriteTo(IByteWriter writer) { } else if (Music != null) { writer.WriteClass(Music); } else if (Badge != null) { - writer.WriteClass(Badge); + writer.WriteClass(Badge); // TODO } writer.WriteClass(Transfer ?? ItemTransfer.Default); writer.WriteClass(Socket ?? ItemSocket.Default); writer.WriteClass(CoupleInfo ?? ItemCoupleInfo.Default); - writer.WriteClass(Binding ?? ItemBinding.Default); + writer.WriteClass(Transfer?.Binding ?? ItemBinding.Default); } public void ReadFrom(IByteReader reader) { Amount = reader.ReadInt(); reader.ReadInt(); - reader.ReadInt(); CreationTime = reader.ReadLong(); ExpiryTime = reader.ReadLong(); reader.ReadLong(); diff --git a/Maple2.Model/Game/Item/ItemEnchant.cs b/Maple2.Model/Game/Item/ItemEnchant.cs index a6aafe2f4..7a21b492e 100644 --- a/Maple2.Model/Game/Item/ItemEnchant.cs +++ b/Maple2.Model/Game/Item/ItemEnchant.cs @@ -35,7 +35,9 @@ public void WriteTo(IByteWriter writer) { writer.WriteInt(Enchants); writer.WriteInt(EnchantExp); writer.WriteByte(EnchantCharges); - writer.WriteLong(); // Destabilized timestamp + + writer.WriteLong(); + writer.WriteInt(); writer.WriteInt(); writer.WriteInt();// Enchantment attempts writer.WriteBool(Tradeable); diff --git a/Maple2.Model/Game/Item/ItemOption.cs b/Maple2.Model/Game/Item/ItemOption.cs index 98cfb766c..d5ad932ba 100644 --- a/Maple2.Model/Game/Item/ItemOption.cs +++ b/Maple2.Model/Game/Item/ItemOption.cs @@ -4,27 +4,27 @@ namespace Maple2.Model.Game; -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] -public readonly record struct BasicOption(int Value, float Rate = 0) { - public BasicOption(float percent) : this(0, percent) { } +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 4)] +public readonly record struct BasicOption(int Value) { + public BasicOption(float percent) : this((int) percent * 100) { } public static BasicOption operator +(BasicOption self, BasicOption other) { - return new BasicOption(self.Value + other.Value, self.Rate + other.Rate); + return new BasicOption(self.Value + other.Value); } public static BasicOption operator -(BasicOption self, BasicOption other) { - return new BasicOption(Math.Max(self.Value - other.Value, 0), Math.Max(self.Rate - other.Rate, 0)); + return new BasicOption(Math.Max(self.Value - other.Value, 0)); } } -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] -public readonly record struct SpecialOption(float Rate, float Value = 0) { +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 4)] +public readonly record struct SpecialOption(float Rate) { public static SpecialOption operator +(SpecialOption self, SpecialOption other) { - return new SpecialOption(self.Rate + other.Rate, self.Value + other.Value); + return new SpecialOption(self.Rate + other.Rate); } public static SpecialOption operator -(SpecialOption self, SpecialOption other) { - return new SpecialOption(Math.Max(self.Rate - other.Rate, 0), Math.Max(self.Value - other.Value, 0)); + return new SpecialOption(Math.Max(self.Rate - other.Rate, 0)); } } diff --git a/Maple2.Model/Game/Item/ItemStats.cs b/Maple2.Model/Game/Item/ItemStats.cs index 0be743414..b37624339 100644 --- a/Maple2.Model/Game/Item/ItemStats.cs +++ b/Maple2.Model/Game/Item/ItemStats.cs @@ -10,18 +10,14 @@ namespace Maple2.Model.Game; public sealed class ItemStats : IByteSerializable, IByteDeserializable { public static readonly ItemStats Default = new ItemStats(); - public const int TYPE_COUNT = 9; + public const int TYPE_COUNT = 5; public enum Type { Constant = 0, Static = 1, Random = 2, Title = 3, - Empowerment1 = 4, - Empowerment2 = 5, - Empowerment3 = 6, - Empowerment4 = 7, - Empowerment5 = 8, + Empowerment = 4, } private readonly Option[] options; @@ -59,7 +55,6 @@ public Option this[Type type] { } public void WriteTo(IByteWriter writer) { - writer.WriteByte(); for (int i = 0; i < TYPE_COUNT; i++) { Option option = options[i]; writer.WriteShort((short) option.Basic.Count); @@ -72,13 +67,12 @@ public void WriteTo(IByteWriter writer) { writer.WriteShort((short) type); writer.Write(specialOption); } - - writer.WriteInt(); } + + // writer.WriteInt(); // this version of kms2 does not have this? } public void ReadFrom(IByteReader reader) { - reader.ReadByte(); for (int i = 0; i < TYPE_COUNT; i++) { Option option = options[i]; short basicCount = reader.ReadShort(); @@ -92,7 +86,7 @@ public void ReadFrom(IByteReader reader) { option.Special[type] = reader.Read(); } - reader.ReadInt(); + // reader.ReadInt(); // this version of kms2 does not have this? } } diff --git a/Maple2.Model/Game/Item/ItemTransfer.cs b/Maple2.Model/Game/Item/ItemTransfer.cs index 34e0de26c..13b8b9f51 100644 --- a/Maple2.Model/Game/Item/ItemTransfer.cs +++ b/Maple2.Model/Game/Item/ItemTransfer.cs @@ -46,7 +46,7 @@ public void WriteTo(IByteWriter writer) { writer.WriteBool(false); // CItemTransfer[9] *bit-1* writer.WriteInt(RemainTrades); // CItemTransfer[10] writer.WriteInt(RepackageCount); // CItemTransfer[11] - writer.WriteByte(); // CItemTransfer[12] + writer.WriteBool(false); // CItemTransfer[12] writer.WriteBool(true); // CItemTransfer[9] *bit-10* (socketTransfer?) // CharBound means untradable, unsellable, bound to char (ignores TransferFlag) @@ -61,7 +61,7 @@ public void ReadFrom(IByteReader reader) { reader.ReadByte(); RemainTrades = reader.ReadInt(); RepackageCount = reader.ReadInt(); - reader.ReadByte(); + reader.ReadBool(); reader.ReadBool(); bool isBound = reader.ReadBool(); if (isBound) { diff --git a/Maple2.Model/Game/Sync/StateSync.cs b/Maple2.Model/Game/Sync/StateSync.cs index 8d430b663..da279a594 100644 --- a/Maple2.Model/Game/Sync/StateSync.cs +++ b/Maple2.Model/Game/Sync/StateSync.cs @@ -36,18 +36,15 @@ public enum Flag : byte { public int SyncNumber; #region Flag1 - public int EmotionId; // Skill ID for emotes + flying mounts (not ground) - public short Flag1Unknown2; // Goes to 1 for flying mounts + public int Flag1Unknown; #endregion #region Flag2 - public Vector3 Flag2Unknown1; - public string? Flag2Unknown2; + public int Flag2Unknown; #endregion #region Flag3 - public int Flag3Unknown1; - public string? Flag3Unknown2; + public short Flag3Unknown; #endregion #region AnimationFlag @@ -55,33 +52,23 @@ public enum Flag : byte { #endregion #region Flag5 - public int Flag5Unknown1; + public Vector3 Flag5Unknown1; public string? Flag5Unknown2; #endregion #region Flag6 public int Flag6Unknown1; - public int Flag6Unknown2; - public byte Flag6Unknown3; - public Vector3 Flag6Position; - public Vector3 Flag6Rotation; + public string? Flag6Unknown2; #endregion public virtual void WriteTo(IByteWriter writer) { writer.Write(State); writer.Write(SubState); - writer.Write(Flags); - - if (Flags.HasFlag(Flag.Flag1)) { - writer.WriteInt(EmotionId); - writer.WriteShort(Flag1Unknown2); - } - writer.Write(Position); writer.WriteShort(Rotation); writer.WriteByte(Animation); - if (Animation > 127) { + if (Animation == 128) { writer.WriteFloat(UnknownFloat1); writer.WriteFloat(UnknownFloat2); } @@ -90,46 +77,39 @@ public virtual void WriteTo(IByteWriter writer) { writer.WriteByte(Unknown1); writer.WriteShort(Rotation2); writer.WriteShort(Unknown3); + + writer.Write(Flags); + if (Flags.HasFlag(Flag.Flag1)) { + writer.WriteInt(Flag1Unknown); + } if (Flags.HasFlag(Flag.Flag2)) { - writer.Write(Flag2Unknown1); - writer.WriteUnicodeString(Flag2Unknown2 ?? ""); + writer.WriteInt(Flag2Unknown); } if (Flags.HasFlag(Flag.Flag3)) { - writer.WriteInt(Flag3Unknown1); - writer.WriteUnicodeString(Flag3Unknown2 ?? ""); + writer.WriteShort(Flag3Unknown); } if (Flags.HasFlag(Flag.Animation)) { writer.WriteUnicodeString(AnimationName ?? ""); } if (Flags.HasFlag(Flag.Flag5)) { - writer.WriteInt(Flag5Unknown1); + writer.Write(Flag5Unknown1); writer.WriteUnicodeString(Flag5Unknown2 ?? ""); } if (Flags.HasFlag(Flag.Flag6)) { writer.WriteInt(Flag6Unknown1); - writer.WriteInt(Flag6Unknown2); - writer.WriteByte(Flag6Unknown3); - writer.Write(Flag6Position); - writer.Write(Flag6Rotation); + writer.WriteUnicodeString(Flag6Unknown2 ?? ""); } - writer.WriteInt(SyncNumber); } public virtual void ReadFrom(IByteReader reader) { State = reader.Read(); SubState = reader.Read(); - Flags = reader.Read(); - - if (Flags.HasFlag(Flag.Flag1)) { - EmotionId = reader.ReadInt(); - Flag1Unknown2 = reader.ReadShort(); - } Position = reader.Read(); Rotation = reader.ReadShort(); // CoordS / 10 (Rotation?) Animation = reader.ReadByte(); - if (Animation > 127) { // if animation < 0 (signed) + if (Animation == 128) { UnknownFloat1 = reader.ReadFloat(); UnknownFloat2 = reader.ReadFloat(); } @@ -138,27 +118,26 @@ public virtual void ReadFrom(IByteReader reader) { Rotation2 = reader.ReadShort(); // CoordS / 10 Unknown3 = reader.ReadShort(); // CoordS / 1000 + Flags = reader.Read(); + if (Flags.HasFlag(Flag.Flag1)) { + Flag1Unknown = reader.ReadInt(); + } if (Flags.HasFlag(Flag.Flag2)) { - Flag2Unknown1 = reader.Read(); - Flag2Unknown2 = reader.ReadUnicodeString(); + Flag2Unknown = reader.ReadInt(); } if (Flags.HasFlag(Flag.Flag3)) { - Flag3Unknown1 = reader.ReadInt(); - Flag3Unknown2 = reader.ReadUnicodeString(); + Flag3Unknown = reader.ReadShort(); } if (Flags.HasFlag(Flag.Animation)) { AnimationName = reader.ReadUnicodeString(); } if (Flags.HasFlag(Flag.Flag5)) { - Flag5Unknown1 = reader.ReadInt(); + Flag5Unknown1 = reader.Read(); Flag5Unknown2 = reader.ReadUnicodeString(); } if (Flags.HasFlag(Flag.Flag6)) { Flag6Unknown1 = reader.ReadInt(); - Flag6Unknown2 = reader.ReadInt(); - Flag6Unknown3 = reader.ReadByte(); - Flag6Position = reader.Read(); - Flag6Rotation = reader.Read(); + Flag6Unknown2 = reader.ReadUnicodeString(); } SyncNumber = reader.ReadInt(); @@ -170,13 +149,13 @@ public override string ToString() { builder.AppendLine($" Position:{Position}, Rotation:{Rotation}, Speed:{Speed}"); builder.AppendLine($" Animation:{Animation} ({UnknownFloat1}, {UnknownFloat2}), Unknown1:{Unknown1}, Rotation2:{Rotation2}, Unknown3:{Unknown3}"); if (Flags.HasFlag(Flag.Flag1)) { - builder.Append($"Flag1: {EmotionId}, {Flag1Unknown2}"); + builder.Append($"Flag1: {Flag1Unknown}"); } if (Flags.HasFlag(Flag.Flag2)) { - builder.Append($"Flag2: {Flag2Unknown1}, {Flag2Unknown2}"); + builder.Append($"Flag2: {Flag2Unknown}"); } if (Flags.HasFlag(Flag.Flag3)) { - builder.Append($"Flag3: {Flag3Unknown1}, {Flag3Unknown2}"); + builder.Append($"Flag3: {Flag3Unknown}"); } if (Flags.HasFlag(Flag.Animation)) { builder.Append($"Animation: {AnimationName}"); @@ -185,8 +164,7 @@ public override string ToString() { builder.Append($"Flag5: {Flag5Unknown1}, {Flag5Unknown2}"); } if (Flags.HasFlag(Flag.Flag6)) { - builder.Append($"Flag6: {Flag6Unknown1}, {Flag6Unknown2}, {Flag6Unknown3}"); - builder.Append($"- Position:{Flag6Position}, Rotation:{Flag6Rotation}"); + builder.Append($"Flag6: {Flag6Unknown1}, {Flag6Unknown2}"); } return builder.ToString(); diff --git a/Maple2.Model/Game/User/SkillPoint.cs b/Maple2.Model/Game/User/SkillPoint.cs index 89a7c9377..44e9f162b 100644 --- a/Maple2.Model/Game/User/SkillPoint.cs +++ b/Maple2.Model/Game/User/SkillPoint.cs @@ -36,6 +36,7 @@ public void WriteTo(IByteWriter writer) { writer.Write(source); writer.WriteClass(point); } + writer.WriteInt(); } public class PointRank : IByteSerializable { diff --git a/Maple2.Server.Core/Constants/RecvOp.cs b/Maple2.Server.Core/Constants/RecvOp.cs index 3eeb859b3..840774d7d 100644 --- a/Maple2.Server.Core/Constants/RecvOp.cs +++ b/Maple2.Server.Core/Constants/RecvOp.cs @@ -56,136 +56,136 @@ public enum RecvOp : ushort { GuideObjectSync = 0x0035, RequestSetCraftMode = 0x0036, RequestCube = 0x0037, - RequestApartment = 0x0038, - Ugc = 0x0039, - MeretMarket = 0x003A, - KeyTable = 0x003B, - Channel = 0x003C, - Liftable = 0x003D, - MyInfo = 0x003E, - RelocateWorld = 0x003F, - ItemMerge = 0x0040, - RequestRide = 0x0041, - RideSync = 0x0042, - FittingDoll = 0x0043, - BonusGame = 0x0044, - ResolvePenalty = 0x0045, - TakeBoat = 0x0046, - Achieve = 0x0047, - BadgeEquip = 0x0048, - RequestTaxi = 0x0049, - Trade = 0x004A, - RequestWorldmap = 0x004B, - Guild = 0x004C, - GroupChat = 0x004D, - RequestHomeBank = 0x004E, - RequestHomeDoctor = 0x004F, - Ah = 0x0050, - SetDebugMode = 0x0051, - RequestHome = 0x0052, - RequestReport = 0x0053, - FurnishingStorage = 0x0054, - RequestMoveEventField = 0x0055, - LogSend = 0x0056, - DpsMode = 0x0057, - GuideRecord = 0x0058, - Rank = 0x0059, - RequestSkipTutorial = 0x005A, - ItemDismantle = 0x005B, - RideConsumeEp = 0x005C, - RequestAdditionalEffect = 0x005D, - RecallUser = 0x005E, - RequestItemEnchant = 0x005F, - BlackMarket = 0x0060, - Gamble = 0x0061, - Pvp = 0x0062, - Maid = 0x0063, - NewsNotification = 0x0064, - SmartRecommendBilling = 0x0065, - SystemShop = 0x0066, - Attendance = 0x0067, - PcBangBonus = 0x0068, - MaidCraftItem = 0x0069, - RequestUserEnv = 0x006A, - Cash = 0x006B, - Insignia = 0x006C, - RequestMoveField = 0x006D, - WaitingTicket = 0x006E, - PartySearch = 0x006F, - RecallScroll = 0x0070, - PotentialAbility = 0x0071, - EnchantScroll = 0x0072, - GlobalPortal = 0x0073, - Fishing = 0x0074, - PlayInstrument = 0x0075, - ChangeAttributes = 0x0076, - ChangeAttributesScroll = 0x0077, - TransferEnchant = 0x0078, - RequestPet = 0x0079, - RequestPetInventory = 0x007A, - NoticeDialog = 0x007B, - SkillMacro = 0x007C, - Banword = 0x007D, - CheckCharName = 0x007E, - PlatformProtectPacket = 0x007F, - PlatformAccountSafe = 0x0080, - State = 0x0081, - MesoMarket = 0x0082, - GlobalFactor = 0x0083, - SmartPush = 0x0084, - PlayArcade = 0x0085, - EnterEventField = 0x0086, - CardReverseGame = 0x0087, - RequestItemLock = 0x0088, - ItemSocketSystem = 0x0089, - CharacterAbility = 0x008A, - Tutorial = 0x008B, - ItemSocketScroll = 0x008C, - ItemRepack = 0x008D, - Mapleopoly = 0x008E, - PremiumClub = 0x008F, - HomeAction = 0x0091, - Mastery = 0x0093, - ConstructShop = 0x0094, - Club = 0x0096, - NexonArena = 0x0097, - EventReward = 0x0098, - NameChange = 0x0099, - AttributePoint = 0x009A, - ItemRepeat = 0x009B, - ItemExchangeScroll = 0x009C, - RequestTutorialItem = 0x009D, - DungeonReward = 0x009E, - ItemBillboard = 0x009F, - TreasureMap = 0x00A0, - ItemExtractionScroll = 0x00A1, // GlamourAnvil - Mentor = 0x00A2, - RequestSkillBookTree = 0x00A3, - BuddyEmote = 0x00A4, - BuddyBadge = 0x00A5, - SyncWorld = 0x00A6, - BirthdayCard = 0x00A7, - SuperWorldChat = 0x00A8, - Microgame = 0x00A9, - StateReact = 0x00AA, - TreeWatering = 0x00AB, - Survival = 0x00AC, - SpectateMushtopia = 0x00AD, - Prestige = 0x00AE, - Lapenshard = 0x00AF, - Ugd = 0x00B0, - WorldChampion = 0x00B1, - ServerEnter = 0x00B2, - MinimapPing = 0x00B3, - // 0x00B4, CClientPlatformNexonAmerica sub_7F3AE0 - MsgBox = 0x00B5, - Wardrobe = 0x00B6, - SystemInfo = 0x00B7, - SidePopup = 0x00B8, - ChatSticker = 0x00B9, - WebScreenshot = 0x00BA, - FileHash = 0x00BB, - Wedding = 0x00BC, - WeddingBillboard = 0x00BD, - LimitBreak = 0x00BE, + // -1 Offset for KMS2 + Ugc = 0x0038, + MeretMarket = 0x0039, + KeyTable = 0x003A, + Channel = 0x003B, + Liftable = 0x003C, + MyInfo = 0x003D, + RelocateWorld = 0x003E, + ItemMerge = 0x003F, + RequestRide = 0x0040, + RideSync = 0x0041, + FittingDoll = 0x0042, + BonusGame = 0x0043, + ResolvePenalty = 0x0044, + TakeBoat = 0x0045, + Achieve = 0x0046, + BadgeEquip = 0x0047, + RequestTaxi = 0x0048, + Trade = 0x0049, + RequestWorldmap = 0x004A, + Guild = 0x004B, + GroupChat = 0x004C, + RequestHomeBank = 0x004D, + RequestHomeDoctor = 0x004E, + Ah = 0x004F, + SetDebugMode = 0x0050, + RequestHome = 0x0051, + RequestReport = 0x0052, + FurnishingStorage = 0x0053, + RequestMoveEventField = 0x0054, + LogSend = 0x0055, + DpsMode = 0x0056, + GuideRecord = 0x0057, + Rank = 0x0058, + RequestSkipTutorial = 0x0059, + ItemDismantle = 0x005A, + RideConsumeEp = 0x005B, + RequestAdditionalEffect = 0x005C, + RecallUser = 0x005D, + RequestItemEnchant = 0x005E, + BlackMarket = 0x005F, + Gamble = 0x0060, + Pvp = 0x0061, + Maid = 0x0062, + NewsNotification = 0x0063, + SmartRecommendBilling = 0x0064, + SystemShop = 0x0065, + Attendance = 0x0066, + PcBangBonus = 0x0067, + MaidCraftItem = 0x0068, + RequestUserEnv = 0x0069, + Cash = 0x006A, + Insignia = 0x006B, + RequestMoveField = 0x006C, + WaitingTicket = 0x006D, + PartySearch = 0x006E, + RecallScroll = 0x006F, + PotentialAbility = 0x0070, + EnchantScroll = 0x0071, + GlobalPortal = 0x0072, + Fishing = 0x0073, + PlayInstrument = 0x0074, + ChangeAttributes = 0x0075, + ChangeAttributesScroll = 0x0076, + TransferEnchant = 0x0077, + RequestPet = 0x0078, + RequestPetInventory = 0x0079, + NoticeDialog = 0x007A, + SkillMacro = 0x007B, + Banword = 0x007C, + CheckCharName = 0x007D, + PlatformProtectPacket = 0x007E, + PlatformAccountSafe = 0x007F, + State = 0x0080, + MesoMarket = 0x0081, + GlobalFactor = 0x0082, + SmartPush = 0x0083, + PlayArcade = 0x0084, + EnterEventField = 0x0085, + CardReverseGame = 0x0086, + RequestItemLock = 0x0087, + ItemSocketSystem = 0x0088, + CharacterAbility = 0x0089, + Tutorial = 0x008A, + ItemSocketScroll = 0x008B, + ItemRepack = 0x008C, + Mapleopoly = 0x008D, + PremiumClub = 0x008E, + HomeAction = 0x0090, + Mastery = 0x0092, + ConstructShop = 0x0093, + Club = 0x0095, + NexonArena = 0x0096, + EventReward = 0x0097, + NameChange = 0x0098, + AttributePoint = 0x0099, + ItemRepeat = 0x009A, + ItemExchangeScroll = 0x009B, + RequestTutorialItem = 0x009C, + DungeonReward = 0x009D, + ItemBillboard = 0x009E, + TreasureMap = 0x009F, + ItemExtractionScroll = 0x00A0, // GlamourAnvil + Mentor = 0x00A1, + RequestSkillBookTree = 0x00A2, + BuddyEmote = 0x00A3, + BuddyBadge = 0x00A4, + SyncWorld = 0x00A5, + BirthdayCard = 0x00A6, + SuperWorldChat = 0x00A7, + Microgame = 0x00A8, + StateReact = 0x00A9, + TreeWatering = 0x00AA, + Survival = 0x00AB, + SpectateMushtopia = 0x00AC, + Prestige = 0x00AD, + Lapenshard = 0x00AE, + Ugd = 0x00AF, + WorldChampion = 0x00B0, + ServerEnter = 0x00B1, + MinimapPing = 0x00B2, + // 0x00B3, CClientPlatformNexonAmerica sub_7F3AE0 + MsgBox = 0x00B4, + Wardrobe = 0x00B5, + SystemInfo = 0x00B6, + SidePopup = 0x00B7, + ChatSticker = 0x00B8, + WebScreenshot = 0x00B9, + FileHash = 0x00BA, + Wedding = 0x00BB, + WeddingBillboard = 0x00BC, + LimitBreak = 0x00BD, } diff --git a/Maple2.Server.Core/Constants/SendOp.cs b/Maple2.Server.Core/Constants/SendOp.cs index 7b0c410f0..7cc759a7f 100644 --- a/Maple2.Server.Core/Constants/SendOp.cs +++ b/Maple2.Server.Core/Constants/SendOp.cs @@ -46,250 +46,252 @@ public enum SendOp : ushort { FieldAddItem = 0x002B, FieldRemoveItem = 0x002C, FieldPickupItem = 0x002D, - FieldMutateItem = 0x002E, - Stat = 0x002F, - UserBattle = 0x0030, - UserSkinColor = 0x0031, - Beauty = 0x0032, - AdventurerBar = 0x0033, - RevivalConfirm = 0x0034, - Revival = 0x0035, - RevivalCount = 0x0036, - UserState = 0x0037, - ExpUp = 0x0038, - LevelUp = 0x0039, - Meso = 0x003A, - Meret = 0x003B, - CurrencyToken = 0x003C, - SkillUse = 0x003D, - SkillDamage = 0x003E, - SkillSync = 0x003F, - SkillCancel = 0x0040, - SkillUseFailed = 0x0041, - StateSkill = 0x0042, - SkillCooldown = 0x0043, - SkillResetCooldown = 0x0044, - SkillPoint = 0x0045, - AttributePoint = 0x0046, - CharacterCreate = 0x0047, - Buff = 0x0048, - FieldPortal = 0x0049, - Job = 0x004A, - NpcMonologue = 0x004B, - NpcTalk = 0x004C, - RegionSkill = 0x004D, - FunctionCube = 0x004E, - Trigger = 0x004F, - Breakable = 0x0050, - RoomTimer = 0x0051, - Shop = 0x0052, - Quest = 0x0053, - Party = 0x0054, - Mail = 0x0055, - FieldAddNpc = 0x0056, - FieldRemoveNpc = 0x0057, - FieldDeadNpc = 0x0058, - NpcControl = 0x0059, - InteractNpc = 0x005A, - FieldAddPet = 0x005B, - FieldRemovePet = 0x005C, - SyncPetTamingPoint = 0x005D, - Tombstone = 0x005E, - Achieve = 0x005F, - UserMoveByPortal = 0x0060, - ItemTitle = 0x0061, - MassiveEvent = 0x0062, - Buddy = 0x0063, - AdminBlock = 0x0064, - InteractObject = 0x0065, - StateConsumeEp = 0x0066, - StateFallDamage = 0x0067, - Cinematic = 0x0068, - Admin = 0x0069, - SetCraftMode = 0x006A, - ResponseCube = 0x006B, - LoadCubes = 0x006C, - Ugc = 0x006D, - MeretMarket = 0x006E, - Gvg = 0x006F, - GuideObject = 0x0070, - KeyTable = 0x0071, - FollowNpc = 0x0072, - Notice = 0x0073, - RelocateWorld = 0x0074, - Liftable = 0x0075, - ItemMerge = 0x0076, - Vibrate = 0x0077, - HideVibrate = 0x0078, - ShowVibrate = 0x0079, - CharacterInfo = 0x007A, - ResponseRide = 0x007B, - RideSync = 0x007C, - FittingDoll = 0x007D, - BonusGame = 0x007E, - LoadUgcMap = 0x007F, - ProxyGameObj = 0x0080, - BadgeEquip = 0x0081, - Taxi = 0x0082, - FindFields = 0x0083, - Trade = 0x0084, - InvincibleEffect = 0x0085, - Worldmap = 0x0086, - MoveEventField = 0x0087, - DpsStat = 0x0088, - DebugMode = 0x0089, - StoryBook = 0x008A, - GuideRecord = 0x008B, - Guild = 0x008C, - GroupChat = 0x008D, - RecallUser = 0x008E, - Rank = 0x008F, - AppendMessageCommon = 0x0090, - AppendMessageString = 0x0091, - AppendMessageKillBoss = 0x0092, - AppendClientLog = 0x0093, - AppendMessageAssistBonus = 0x0094, - Ah = 0x0095, - UiText = 0x0096, - PlayNpcSound = 0x0097, - ItemDismantle = 0x0098, - ItemEnchant = 0x0099, - BlackMarket = 0x009A, - MesoMarket = 0x009B, - TeamPvp = 0x009C, - WebOpen = 0x009D, - Gamble = 0x009E, - FieldMaid = 0x009F, - UserMaid = 0x00A0, - NewsNotification = 0x00A1, - SmartRecommendBilling = 0x00A2, - SystemShop = 0x00A3, - AutoRevive = 0x00A4, - PlayerKillNotice = 0x00A5, - Attendance = 0x00A6, - PcBangBonus = 0x00A7, - DeadUser = 0x00A8, - DynamicChannel = 0x00A9, - UserEnv = 0x00AA, - MaidCraftItem = 0x00AB, - EnterUgcMap = 0x00AC, - ItemUse = 0x00AD, - ItemBox = 0x00AE, - Cash = 0x00AF, - MyInfo = 0x00B0, - //UNKNOWN = 0x00B1, // nullsub - WorldShareInfo = 0x00B2, - Insignia = 0x00B3, - GameEvent = 0x00B4, - BannerList = 0x00B5, - WaitingTicketUpdate = 0x00B6, - SetPcBang = 0x00B7, - Pvp = 0x00B8, - HomeCommand = 0x00B9, - CharMaxCount = 0x00BA, - World = 0x00BB, - ItemDropNotice = 0x00BC, - PartySearch = 0x00BD, - RecallScroll = 0x00BE, - UserConditionEvent = 0x00BF, - PotentialAbility = 0x00C0, - EnchantScroll = 0x00C1, - BossRanking = 0x00C2, - GlobalPortal = 0x00C3, - QuizEvent = 0x00C4, - PlaySystemSound = 0x00C5, - Fishing = 0x00C6, - DarkStream = 0x00C7, - NpsInfo = 0x00C8, - PlayInstrument = 0x00C9, - ChangeAttributes = 0x00CA, - ChangeAttributesScroll = 0x00CB, - FieldProperty = 0x00CC, - GameEventUserValue = 0x00CD, - ResponsePet = 0x00CE, - Mastery = 0x00CF, - PetInventory = 0x00D0, - NoticeDialog = 0x00D1, - AaErr = 0x00D2, - SkillMacro = 0x00D3, - //BANWORD = 0x00D4, // nullsub - CheckCharNameResult = 0x00D5, - PlatformProtectPacket = 0x00D6, - //PLATFORM_ACCOUNT_SAFE = 0x00D7, // nullsub - GlobalFactor = 0x00D8, - SmartPush = 0x00D9, - PlayArcade = 0x00DA, - DebugState = 0x00DB, - CardReverseGame = 0x00DC, - ItemLock = 0x00DD, - HomeBank = 0x00DE, - HomeDoctor = 0x00DF, - ItemSocketSystem = 0x00E0, - CharacterAbility = 0x00E1, - ShadowBuff = 0x00E2, - ShadowExpedition = 0x00E3, - ItemSocketScroll = 0x00E5, - ItemRepackage = 0x00E6, - Mapleopoly = 0x00E7, - BypassKey = 0x00E8, - NpcNotice = 0x00EA, - LocalCamera = 0x00EB, - HomeAction = 0x00EC, - PremiumClub = 0x00ED, - SteamCashShop = 0x00EF, - InGameRank = 0x00F0, - DungeonMatch = 0x00F1, - DungeonWaiting = 0x00F2, - OneTimeEffect = 0x00F3, - CameraInterpolation = 0x00F4, - TimeScale = 0x00F5, - LegionBattle = 0x00F6, - ItemScript = 0x00F7, - Club = 0x00F8, - EventReward = 0x00F9, - HomeInvite = 0x00FA, - TaskEvent = 0x00FB, - StateFishingData = 0x00FC, - DungeonReviveCount = 0x00FD, - ChangeBackground = 0x0100, - RoomStageDungeon = 0x0101, - DungeonMission = 0x0102, - ItemExchange = 0x0104, - BindItem = 0x0105, - TransferEnchant = 0x0106, - PlayerHost = 0x0107, - GmCommand = 0x0108, - GlamourAnvil = 0x0109, - PetSkinBadge = 0x010A, - DungeonHelp = 0x010B, - Mentor = 0x010C, - SkillBookTree = 0x010E, - BuddyEmote = 0x010F, - BuddyBadge = 0x0110, - SyncWorld = 0x0112, - BirthdayCard = 0x0113, - SuperWorldChat = 0x0114, - Microgame = 0x0116, - Reactor = 0x0117, - SurvivalEvent = 0x0118, - MeretUse = 0x0119, - HalloweenEvent = 0x011A, - Survival = 0x011B, - SpectateMushtopia = 0x011C, - Lapenshard = 0x011D, - Prestige = 0x011E, - Ugd = 0x011F, - WorldChampion = 0x0121, - SyncValue = 0x0122, - MinimapPing = 0x0123, - MsgBox = 0x0124, - Wardrobe = 0x0125, - DynamicBanword = 0x0126, - RequestSystemInfo = 0x0127, - ChatStamp = 0x0128, - Wedding = 0x012A, - WeddingBillboard = 0x012C, - HideAndSeek = 0x012D, - MessengerBrowserStamp = 0x012E, - LimitBreak = 0x0130, + // FieldMutateItem = 0x002E, + // -1 Offset for KMS2 + Stat = 0x002E, + UserBattle = 0x002F, + UserSkinColor = 0x0030, + Beauty = 0x0031, + AdventurerBar = 0x0032, + RevivalConfirm = 0x0033, + Revival = 0x0034, + RevivalCount = 0x0035, + UserState = 0x0036, + ExpUp = 0x0037, + LevelUp = 0x0038, + Meso = 0x0039, + Meret = 0x003A, + CurrencyToken = 0x003B, + SkillUse = 0x003C, + SkillDamage = 0x003D, + SkillSync = 0x003E, + SkillCancel = 0x003F, + SkillUseFailed = 0x0040, + StateSkill = 0x0041, + SkillCooldown = 0x0042, + SkillResetCooldown = 0x0043, + SkillPoint = 0x0044, + AttributePoint = 0x0045, + CharacterCreate = 0x0046, + Buff = 0x0047, + FieldPortal = 0x0048, + Job = 0x0049, + NpcMonologue = 0x004A, + NpcTalk = 0x004B, + RegionSkill = 0x004C, + FunctionCube = 0x004D, + Trigger = 0x004E, + Breakable = 0x004F, + RoomTimer = 0x0050, + Shop = 0x0051, + Quest = 0x0052, + Party = 0x0053, + Mail = 0x0054, + FieldAddNpc = 0x0055, + FieldRemoveNpc = 0x0056, + FieldDeadNpc = 0x0057, + NpcControl = 0x0058, + InteractNpc = 0x0059, + FieldAddPet = 0x005A, + FieldRemovePet = 0x005B, + SyncPetTamingPoint = 0x005C, + Tombstone = 0x005D, + Achieve = 0x005E, + UserMoveByPortal = 0x005F, + // -2 Offset for KMS2 + MassiveEvent = 0x0060, + Buddy = 0x0061, + AdminBlock = 0x0062, + InteractObject = 0x0063, + StateConsumeEp = 0x0064, + StateFallDamage = 0x0065, + Cinematic = 0x0066, + Admin = 0x0067, + SetCraftMode = 0x0068, + ResponseCube = 0x0069, + LoadCubes = 0x006A, + Ugc = 0x006B, + MeretMarket = 0x006C, + Gvg = 0x006D, + GuideObject = 0x006E, + KeyTable = 0x006F, + FollowNpc = 0x0070, + Notice = 0x0071, + RelocateWorld = 0x0072, + Liftable = 0x0073, + ItemMerge = 0x0074, + Vibrate = 0x0075, + HideVibrate = 0x0076, + ShowVibrate = 0x0077, + CharacterInfo = 0x0078, + ResponseRide = 0x0079, + RideSync = 0x007A, + FittingDoll = 0x007B, + BonusGame = 0x007C, + LoadUgcMap = 0x007D, + ProxyGameObj = 0x007E, + BadgeEquip = 0x007F, + Taxi = 0x0080, + FindFields = 0x0081, + Trade = 0x0082, + InvincibleEffect = 0x0083, + Worldmap = 0x0084, + MoveEventField = 0x0085, + DpsStat = 0x0086, + DebugMode = 0x0087, + StoryBook = 0x0088, + GuideRecord = 0x0089, + Guild = 0x008A, + GroupChat = 0x008B, + RecallUser = 0x008C, + Rank = 0x008D, + AppendMessageCommon = 0x008E, + AppendMessageString = 0x008F, + AppendMessageKillBoss = 0x0090, + AppendClientLog = 0x0091, + AppendMessageAssistBonus = 0x0092, + Ah = 0x0093, + UiText = 0x0094, + PlayNpcSound = 0x0095, + ItemDismantle = 0x0096, + ItemEnchant = 0x0097, + BlackMarket = 0x0098, + MesoMarket = 0x0099, + TeamPvp = 0x009A, + WebOpen = 0x009B, + Gamble = 0x009C, + FieldMaid = 0x009D, + UserMaid = 0x009E, + NewsNotification = 0x009F, + SmartRecommendBilling = 0x00A0, + SystemShop = 0x00A1, + AutoRevive = 0x00A2, + PlayerKillNotice = 0x00A3, + Attendance = 0x00A4, + PcBangBonus = 0x00A5, + DeadUser = 0x00A6, + DynamicChannel = 0x00A7, + UserEnv = 0x00A8, + MaidCraftItem = 0x00A9, + EnterUgcMap = 0x00AA, + ItemUse = 0x00AB, + ItemBox = 0x00AC, + Cash = 0x00AD, + MyInfo = 0x00AE, + //UNKNOWN = 0x00AF, // nullsub + WorldShareInfo = 0x00B0, + Insignia = 0x00B1, + GameEvent = 0x00B2, + BannerList = 0x00B3, + WaitingTicketUpdate = 0x00B4, + SetPcBang = 0x00B5, + Pvp = 0x00B6, + HomeCommand = 0x00B7, + CharMaxCount = 0x00B8, + World = 0x00B9, + ItemDropNotice = 0x00BA, + PartySearch = 0x00BB, + RecallScroll = 0x00BC, + UserConditionEvent = 0x00BD, + PotentialAbility = 0x00BE, + EnchantScroll = 0x00BF, + BossRanking = 0x00C0, + GlobalPortal = 0x00C1, + QuizEvent = 0x00C2, + PlaySystemSound = 0x00C3, + Fishing = 0x00C4, + DarkStream = 0x00C5, + NpsInfo = 0x00C6, + PlayInstrument = 0x00C7, + ChangeAttributes = 0x00C8, + ChangeAttributesScroll = 0x00C9, + FieldProperty = 0x00CA, + GameEventUserValue = 0x00CB, + ResponsePet = 0x00CC, + Mastery = 0x00CD, + PetInventory = 0x00CE, + NoticeDialog = 0x00CF, + AaErr = 0x00D0, + SkillMacro = 0x00D1, + //BANWORD = 0x00D2, // nullsub + CheckCharNameResult = 0x00D3, + PlatformProtectPacket = 0x00D4, + //PLATFORM_ACCOUNT_SAFE = 0x00D5, // nullsub + GlobalFactor = 0x00D6, + SmartPush = 0x00D7, + PlayArcade = 0x00D8, + DebugState = 0x00D9, + CardReverseGame = 0x00DA, + ItemLock = 0x00DB, + HomeBank = 0x00DC, + HomeDoctor = 0x00DD, + ItemSocketSystem = 0x00DE, + CharacterAbility = 0x00DF, + ShadowBuff = 0x00E0, + ShadowExpedition = 0x00E1, + ItemSocketScroll = 0x00E3, + ItemRepackage = 0x00E4, + Mapleopoly = 0x00E5, + BypassKey = 0x00E6, + NpcNotice = 0x00E8, + LocalCamera = 0x00E9, + HomeAction = 0x00EA, + PremiumClub = 0x00EB, + SteamCashShop = 0x00ED, + InGameRank = 0x00EE, + DungeonMatch = 0x00EF, + DungeonWaiting = 0x00F0, + OneTimeEffect = 0x00F1, + CameraInterpolation = 0x00F2, + TimeScale = 0x00F3, + LegionBattle = 0x00F4, + ItemScript = 0x00F5, + Club = 0x00F6, + EventReward = 0x00F7, + HomeInvite = 0x00F8, + TaskEvent = 0x00F9, + StateFishingData = 0x00FA, + DungeonReviveCount = 0x00FB, + ChangeBackground = 0x00FE, + RoomStageDungeon = 0x00FF, + DungeonMission = 0x0100, + ItemExchange = 0x0102, + BindItem = 0x0103, + TransferEnchant = 0x0104, + PlayerHost = 0x0105, + GmCommand = 0x0106, + GlamourAnvil = 0x0107, + PetSkinBadge = 0x0108, + DungeonHelp = 0x0109, + Mentor = 0x010A, + SkillBookTree = 0x010C, + BuddyEmote = 0x010D, + BuddyBadge = 0x010E, + SyncWorld = 0x0110, + BirthdayCard = 0x0111, + SuperWorldChat = 0x0112, + Microgame = 0x0114, + Reactor = 0x0115, + SurvivalEvent = 0x0116, + MeretUse = 0x0117, + HalloweenEvent = 0x0118, + Survival = 0x0119, + SpectateMushtopia = 0x011A, + Lapenshard = 0x011B, + Prestige = 0x011C, + Ugd = 0x011D, + WorldChampion = 0x011F, + SyncValue = 0x0120, + MinimapPing = 0x0121, + MsgBox = 0x0122, + Wardrobe = 0x0123, + DynamicBanword = 0x0124, + RequestSystemInfo = 0x0125, + ChatStamp = 0x0126, + Wedding = 0x0128, + WeddingBillboard = 0x012A, + HideAndSeek = 0x012B, + MessengerBrowserStamp = 0x012C, + LimitBreak = 0x012E, + } diff --git a/Maple2.Server.Core/Network/Session.cs b/Maple2.Server.Core/Network/Session.cs index 1a660d807..2c2079548 100644 --- a/Maple2.Server.Core/Network/Session.cs +++ b/Maple2.Server.Core/Network/Session.cs @@ -21,8 +21,8 @@ public enum PatchType : byte { } public abstract class Session : IDisposable { - public const uint VERSION = 12; - private const uint BLOCK_IV = 12; // TODO: should this be variable + public const uint VERSION = 2525; // KMS2 2022-04-13 + private const uint BLOCK_IV = 23; // TODO: should this be variable private const int HANDSHAKE_SIZE = 19; private const int STOP_TIMEOUT = 2000; diff --git a/Maple2.Server.Core/PacketHandlers/ResponseVersionHandler.cs b/Maple2.Server.Core/PacketHandlers/ResponseVersionHandler.cs index 0c18058a9..0a1175a30 100644 --- a/Maple2.Server.Core/PacketHandlers/ResponseVersionHandler.cs +++ b/Maple2.Server.Core/PacketHandlers/ResponseVersionHandler.cs @@ -13,7 +13,7 @@ public override void Handle(T session, IByteReader packet) { packet.ReadShort(); // 47 var locale = packet.Read(); - if (version != Session.VERSION || locale != Locale.NA) { + if (version != Session.VERSION /*|| locale != Locale.NA */) { session.Disconnect(); } } diff --git a/Maple2.Server.Core/Packets/CharacterListPacket.cs b/Maple2.Server.Core/Packets/CharacterListPacket.cs index 3bfa03266..2d353baee 100644 --- a/Maple2.Server.Core/Packets/CharacterListPacket.cs +++ b/Maple2.Server.Core/Packets/CharacterListPacket.cs @@ -174,8 +174,6 @@ private static void WriteCharacter(this IByteWriter writer, Account account, Cha #region Unknown writer.WriteUnicodeString(); writer.WriteLong(); - writer.WriteLong(); - writer.WriteLong(); #endregion writer.WriteInt(); // Unknown Count writer.WriteByte(); diff --git a/Maple2.Server.Core/Packets/Helper/EquipPacketHelper.cs b/Maple2.Server.Core/Packets/Helper/EquipPacketHelper.cs index 79cceff44..ec24c9d2c 100644 --- a/Maple2.Server.Core/Packets/Helper/EquipPacketHelper.cs +++ b/Maple2.Server.Core/Packets/Helper/EquipPacketHelper.cs @@ -11,7 +11,7 @@ public static class EquipPacketHelper { public static void WriteEquip(this IByteWriter writer, Item equip) { writer.WriteInt(equip.Id); writer.WriteLong(equip.Uid); - writer.WriteUnicodeString(equip.EquipSlot().ToString()); + writer.Write(equip.EquipSlot()); writer.WriteInt(equip.Rarity); writer.WriteClass(equip); } diff --git a/Maple2.Server.Core/Packets/ServerListPacket.cs b/Maple2.Server.Core/Packets/ServerListPacket.cs index ae74ae395..43e2e7404 100644 --- a/Maple2.Server.Core/Packets/ServerListPacket.cs +++ b/Maple2.Server.Core/Packets/ServerListPacket.cs @@ -20,6 +20,7 @@ public static ByteWriter Error() { public static ByteWriter Load(string serverName, IList serverIps, ICollection channels) { var pWriter = Packet.Of(SendOp.ServerList); + pWriter.WriteString("dev"); // env (Live, Staging, qa, dev) pWriter.Write(Command.Load); pWriter.WriteInt(1); // Unknown pWriter.WriteUnicodeString(serverName); diff --git a/Maple2.Server.Game/Manager/Config/SkillInfo.cs b/Maple2.Server.Game/Manager/Config/SkillInfo.cs index c937bc291..4c1b64697 100644 --- a/Maple2.Server.Game/Manager/Config/SkillInfo.cs +++ b/Maple2.Server.Game/Manager/Config/SkillInfo.cs @@ -193,17 +193,16 @@ public IEnumerable GetSkills(SkillType type, SkillRank rank) { } public void WriteTo(IByteWriter writer) { - writer.WriteInt((int) Job); - writer.WriteByte(1); // Count - + writer.WriteInt(1); writer.WriteInt((int) Job.Code()); + int count = 0; for (int i = 0; i < SKILL_TYPES; i++) { - int count = 0; for (int j = 0; j < SKILL_RANKS; j++) { count += Skills[i, j].Count + SubSkills[i, j].Count; } - writer.WriteByte((byte) count); - + } + writer.WriteByte((byte) count); + for (int i = 0; i < SKILL_TYPES; i++) { for (int j = 0; j < SKILL_RANKS; j++) { foreach (Skill skill in Skills[i, j].Values) { writer.WriteClass(skill); @@ -251,11 +250,11 @@ public void SetLevel(short level, bool notify = true) { } public void WriteTo(IByteWriter writer) { - writer.WriteBool(Notify); - writer.WriteBool(Level > 0); writer.WriteInt(Id); writer.WriteInt(Math.Max((int) Level, 1)); - writer.WriteByte(); + writer.WriteBool(false); + writer.WriteBool(Level > 0); + writer.WriteBool(Notify); Notify = false; } diff --git a/Maple2.Server.Game/Manager/ItemMergeManager.cs b/Maple2.Server.Game/Manager/ItemMergeManager.cs index 9d2ba918f..2ca809f40 100644 --- a/Maple2.Server.Game/Manager/ItemMergeManager.cs +++ b/Maple2.Server.Game/Manager/ItemMergeManager.cs @@ -139,20 +139,20 @@ public void Empower(long itemUid, long catalystUid) { (BasicAttribute attribute, ItemMergeTable.Option mergeOption) = mergeSlot.BasicOptions.ElementAt(selectedIndex); (int value, float rate) = Roll(mergeOption); - upgradeItem.Stats![ItemStats.Type.Empowerment1] = new ItemStats.Option { - Basic = { - [attribute] = new BasicOption(value, rate), - }, - }; + // upgradeItem.Stats![ItemStats.Type.Empowerment1] = new ItemStats.Option { + // Basic = { + // [attribute] = new BasicOption(value, rate), + // }, + // }; } else { (SpecialAttribute attribute, ItemMergeTable.Option mergeOption) = mergeSlot.SpecialOptions.ElementAt(selectedIndex - mergeSlot.BasicOptions.Count); (int value, float rate) = Roll(mergeOption); - upgradeItem.Stats![ItemStats.Type.Empowerment1] = new ItemStats.Option { - Special = { - [attribute] = new SpecialOption(rate, value), - }, - }; + // upgradeItem.Stats![ItemStats.Type.Empowerment1] = new ItemStats.Option { + // Special = { + // [attribute] = new SpecialOption(rate, value), + // }, + // }; } session.Send(ItemMergePacket.Empower(upgradeItem)); diff --git a/Maple2.Server.Game/Model/Field/Actor/FieldPlayer.cs b/Maple2.Server.Game/Model/Field/Actor/FieldPlayer.cs index b71483f14..61a7a4e9a 100644 --- a/Maple2.Server.Game/Model/Field/Actor/FieldPlayer.cs +++ b/Maple2.Server.Game/Model/Field/Actor/FieldPlayer.cs @@ -303,11 +303,11 @@ bool UpdateStateSyncTimeTracking() { break; case ActorState.EmotionIdle: if (UpdateStateSyncTimeTracking()) { - Field.SkillMetadata.TryGet(stateSync.EmotionId, 1, out var emote); - if (emote == null) { - break; - } - Session.ConditionUpdate(ConditionType.emotiontime, codeString: emote.Property.Emotion); + // Field.SkillMetadata.TryGet(stateSync.EmotionId, 1, out var emote); + // if (emote == null) { + // break; + // } + // Session.ConditionUpdate(ConditionType.emotiontime, codeString: emote.Property.Emotion); } break; // TODO: Any more condition states? diff --git a/Maple2.Server.Game/Model/Field/Entity/FieldNpcSpawnPoint.cs b/Maple2.Server.Game/Model/Field/Entity/FieldNpcSpawnPoint.cs index 0fafe4a0b..63654b576 100644 --- a/Maple2.Server.Game/Model/Field/Entity/FieldNpcSpawnPoint.cs +++ b/Maple2.Server.Game/Model/Field/Entity/FieldNpcSpawnPoint.cs @@ -40,7 +40,7 @@ public void TriggerSpawn() { IEnumerable npcs = Field.GetActorsBySpawnId(SpawnId).OfType().ToList(); // Only get npcs foreach (SpawnPointNPCListEntry spawn in Value.NpcList) { if (!Field.NpcMetadata.TryGet(spawn.NpcId, out NpcMetadata? npcMetadata)) { - Log.Logger.Warning("Npc {NpcId} failed to load for map {MapId}", spawn.NpcId, Field.MapId); + // Log.Logger.Warning("Npc {NpcId} failed to load for map {MapId}", spawn.NpcId, Field.MapId); continue; } diff --git a/Maple2.Server.Game/Model/Stats.cs b/Maple2.Server.Game/Model/Stats.cs index dd4df362d..252f0f83e 100644 --- a/Maple2.Server.Game/Model/Stats.cs +++ b/Maple2.Server.Game/Model/Stats.cs @@ -179,12 +179,12 @@ public void AddTotal(long amount) { public void AddTotal(BasicOption option) { AddTotal(option.Value); - Rate += option.Rate; + // Rate += option.Value; } public void AddTotal(SpecialOption option) { - AddTotal((int) option.Value); - Rate += option.Rate; + AddTotal((int) option.Rate); + // Rate += option.Rate; } public void AddRate(float rate) { diff --git a/Maple2.Server.Game/PacketHandlers/GuildHandler.cs b/Maple2.Server.Game/PacketHandlers/GuildHandler.cs index 65b435ab2..5880a0f52 100644 --- a/Maple2.Server.Game/PacketHandlers/GuildHandler.cs +++ b/Maple2.Server.Game/PacketHandlers/GuildHandler.cs @@ -71,6 +71,8 @@ private enum Command : byte { public override void Handle(GameSession session, IByteReader packet) { var command = packet.Read(); + + return; switch (command) { case Command.Create: HandleCreate(session, packet); diff --git a/Maple2.Server.Game/PacketHandlers/ItemEquipHandler.cs b/Maple2.Server.Game/PacketHandlers/ItemEquipHandler.cs index 10e74b7f5..5011ff4ee 100644 --- a/Maple2.Server.Game/PacketHandlers/ItemEquipHandler.cs +++ b/Maple2.Server.Game/PacketHandlers/ItemEquipHandler.cs @@ -31,13 +31,9 @@ public override void Handle(GameSession session, IByteReader packet) { private void HandleEquip(GameSession session, IByteReader packet) { long itemUid = packet.ReadLong(); - string equipSlotStr = packet.ReadUnicodeString(); + var equipSlot = packet.Read(); bool isSkin = packet.ReadBool(); - if (!Enum.TryParse(equipSlotStr, out EquipSlot equipSlot)) { - return; - } - // Disconnect if this fails to avoid bad state. if (session.Item.Equips.Equip(itemUid, equipSlot, isSkin)) { session.Stats.Refresh(); @@ -57,6 +53,6 @@ private void HandleUnequip(GameSession session, IByteReader packet) { // This is probably for Skin2 private void HandleEquip2(GameSession session, IByteReader packet) { long itemUid = packet.ReadLong(); - string equipSlotStr = packet.ReadUnicodeString(); + var equipSlot = packet.Read(); } } diff --git a/Maple2.Server.Game/PacketHandlers/SkillHandler.cs b/Maple2.Server.Game/PacketHandlers/SkillHandler.cs index 84f9914b6..1c050ce19 100644 --- a/Maple2.Server.Game/PacketHandlers/SkillHandler.cs +++ b/Maple2.Server.Game/PacketHandlers/SkillHandler.cs @@ -109,7 +109,7 @@ private void HandleUse(GameSession session, IByteReader packet) { record.IsHold = packet.ReadBool(); if (record.IsHold) { record.HoldInt = packet.ReadInt(); - record.HoldString = packet.ReadUnicodeString(); + record.HoldString = packet.ReadUnicodeString().Trim('\0'); if (session.Player.DebugSkills) { session.Send(NoticePacket.Message($"Skill.Use: {skillId}, {skillUid}; IsHold: true; HoldInt: {record.HoldInt}; HoldString: {record.HoldString}; UnkBool: {record.Unknown}")); diff --git a/Maple2.Server.Game/PacketHandlers/UserSyncHandler.cs b/Maple2.Server.Game/PacketHandlers/UserSyncHandler.cs index 31b654021..2e5c01e42 100644 --- a/Maple2.Server.Game/PacketHandlers/UserSyncHandler.cs +++ b/Maple2.Server.Game/PacketHandlers/UserSyncHandler.cs @@ -32,16 +32,13 @@ public override void Handle(GameSession session, IByteReader packet) { ActorState.MicroGameRps => new StateSyncRps(), ActorState.MicroGameCoupleDance => new StateSyncCoupleDance(), ActorState.WeddingEmotion => new StateSyncWeddingEmotion(), - _ => new StateSync(), + _ => new StateSync() }; stateSync.ReadFrom(packet); stateSyncs[i] = stateSync; packet.ReadInt(); // ClientTicks - if (playerState != ActorState.WeddingEmotion) { - packet.ReadInt(); // ServerTicks - } } using (var buffer = new PoolByteWriter()) { diff --git a/Maple2.Server.Game/Packets/AdminPacket.cs b/Maple2.Server.Game/Packets/AdminPacket.cs new file mode 100644 index 000000000..109cffdd9 --- /dev/null +++ b/Maple2.Server.Game/Packets/AdminPacket.cs @@ -0,0 +1,16 @@ +using Maple2.PacketLib.Tools; +using Maple2.Server.Core.Constants; +using Maple2.Server.Core.Packets; + +namespace Maple2.Server.Game.Packets; + +public static class AdminPacket { + + public static ByteWriter Enable() { + ByteWriter pWriter = Packet.Of(SendOp.Admin); + pWriter.WriteByte(); + pWriter.WriteByte(255); + + return pWriter; + } +} diff --git a/Maple2.Server.Game/Packets/DungeonRoomPacket.cs b/Maple2.Server.Game/Packets/DungeonRoomPacket.cs index 3c67a3786..d893c3c28 100644 --- a/Maple2.Server.Game/Packets/DungeonRoomPacket.cs +++ b/Maple2.Server.Game/Packets/DungeonRoomPacket.cs @@ -79,6 +79,8 @@ public static ByteWriter RankRewards(IDictionary rankRew pWriter.WriteInt(id); pWriter.WriteClass(reward); } + pWriter.WriteInt(); + pWriter.WriteLong(); return pWriter; } diff --git a/Maple2.Server.Game/Packets/EmotePacket.cs b/Maple2.Server.Game/Packets/EmotePacket.cs index 407cc908b..bfdc31212 100644 --- a/Maple2.Server.Game/Packets/EmotePacket.cs +++ b/Maple2.Server.Game/Packets/EmotePacket.cs @@ -27,7 +27,10 @@ public static ByteWriter Load(IList emotes) { public static ByteWriter Learn(Emote emote) { var pWriter = Packet.Of(SendOp.Emote); pWriter.Write(Command.Learn); - pWriter.Write(emote); + pWriter.WriteInt(emote.Id); + pWriter.WriteInt(emote.Level); + pWriter.WriteLong(emote.ExpiryTime); + pWriter.WriteLong(); return pWriter; } diff --git a/Maple2.Server.Game/Packets/EnchantScrollPacket.cs b/Maple2.Server.Game/Packets/EnchantScrollPacket.cs index 56ae72a42..0f3b21d7a 100644 --- a/Maple2.Server.Game/Packets/EnchantScrollPacket.cs +++ b/Maple2.Server.Game/Packets/EnchantScrollPacket.cs @@ -55,7 +55,6 @@ public static ByteWriter Preview(Item item, EnchantScrollType scrollType, IDicti pWriter.WriteInt(minOptions.Count); foreach ((BasicAttribute attribute, BasicOption delta) in minOptions) { pWriter.WriteShort((short) attribute); - pWriter.WriteFloat(delta.Rate); pWriter.WriteInt(delta.Value); } break; @@ -63,13 +62,11 @@ public static ByteWriter Preview(Item item, EnchantScrollType scrollType, IDicti pWriter.WriteInt(maxOptions.Count); foreach ((BasicAttribute attribute, BasicOption delta) in maxOptions) { pWriter.WriteShort((short) attribute); - pWriter.WriteFloat(delta.Rate); pWriter.WriteInt(delta.Value); } pWriter.WriteInt(minOptions.Count); foreach ((BasicAttribute attribute, BasicOption delta) in minOptions) { pWriter.WriteShort((short) attribute); - pWriter.WriteFloat(delta.Rate); pWriter.WriteInt(delta.Value); } break; diff --git a/Maple2.Server.Game/Packets/EquipPacket.cs b/Maple2.Server.Game/Packets/EquipPacket.cs index 89c5bfbff..1158f5494 100644 --- a/Maple2.Server.Game/Packets/EquipPacket.cs +++ b/Maple2.Server.Game/Packets/EquipPacket.cs @@ -15,7 +15,7 @@ public static ByteWriter EquipItem(IActor player, Item item, byte type) pWriter.WriteInt(player.ObjectId); pWriter.WriteInt(item.Id); pWriter.WriteLong(item.Uid); - pWriter.WriteUnicodeString(item.EquipSlot().ToString()); + pWriter.Write(item.EquipSlot()); pWriter.WriteInt(item.Rarity); pWriter.WriteByte(type); // 0, 1, 2 (rest invalid) pWriter.WriteClass(item); diff --git a/Maple2.Server.Game/Packets/FieldPacket.cs b/Maple2.Server.Game/Packets/FieldPacket.cs index 98c32ff40..0bcb7d8f7 100644 --- a/Maple2.Server.Game/Packets/FieldPacket.cs +++ b/Maple2.Server.Game/Packets/FieldPacket.cs @@ -121,8 +121,6 @@ public static ByteWriter AddPlayer(GameSession session) { pWriter.WriteLong(player.Account.PremiumTime); pWriter.WriteInt(); pWriter.WriteByte(); - pWriter.WriteInt(); // Tail - pWriter.WriteInt(); pWriter.WriteShort(); return pWriter; @@ -330,8 +328,6 @@ private static void WriteCharacter(this IByteWriter writer, GameSession session) writer.WriteClass(character.Mastery); #region Unknown writer.WriteUnicodeString(); // Login username - writer.WriteLong(); // Session Id - writer.WriteLong(); writer.WriteLong(); #endregion writer.WriteInt(); // Unknown Count diff --git a/Maple2.Server.Game/Packets/ItemInventoryPacket.cs b/Maple2.Server.Game/Packets/ItemInventoryPacket.cs index 27affe189..7e34545ae 100644 --- a/Maple2.Server.Game/Packets/ItemInventoryPacket.cs +++ b/Maple2.Server.Game/Packets/ItemInventoryPacket.cs @@ -1,4 +1,5 @@ -using Maple2.Model.Enum; +using Maple2.Model; +using Maple2.Model.Enum; using Maple2.Model.Error; using Maple2.Model.Game; using Maple2.PacketLib.Tools; @@ -14,14 +15,14 @@ private enum Command : byte { Remove = 1, UpdateAmount = 2, Move = 3, - Load = 7, - NotifyNew = 8, - LoadTab = 10, - ExpandComplete = 12, - Reset = 13, - ExpandCount = 14, - Error = 15, - UpdateItem = 16, + Load = 6, + NotifyNew = 7, + LoadTab = 8, + ExpandComplete = 10, + Reset = 11, + ExpandCount = 12, + Error = 13, + UpdateItem = 14, } public static ByteWriter Add(Item item) { @@ -31,7 +32,6 @@ public static ByteWriter Add(Item item) { pWriter.WriteLong(item.Uid); pWriter.WriteShort(item.Slot); pWriter.WriteInt(item.Rarity); - pWriter.WriteUnicodeString(); // EquipSlot pWriter.WriteClass(item); return pWriter; @@ -85,7 +85,7 @@ public static ByteWriter NotifyNew(long uid, int amount) { pWriter.Write(Command.NotifyNew); pWriter.WriteLong(uid); pWriter.WriteInt(amount); - pWriter.WriteUnicodeString(); // EquipSlot + pWriter.WriteUnicodeString(); return pWriter; } diff --git a/Maple2.Server.Game/Packets/ServerEnterPacket.cs b/Maple2.Server.Game/Packets/ServerEnterPacket.cs index af43b2597..bc316ba5a 100644 --- a/Maple2.Server.Game/Packets/ServerEnterPacket.cs +++ b/Maple2.Server.Game/Packets/ServerEnterPacket.cs @@ -3,6 +3,7 @@ using Maple2.Server.Core.Constants; using Maple2.Server.Core.Packets; using Maple2.Server.Game.Model; +using Maple2.Tools.Extensions; namespace Maple2.Server.Game.Packets; @@ -34,9 +35,9 @@ public static ByteWriter Request(IActor fieldPlayer) { pWriter.WriteLong(player.Currency.MenteeToken); pWriter.WriteLong(player.Currency.StarPoint); pWriter.WriteLong(player.Currency.MesoToken); + pWriter.WriteUnicodeString(player.Character.Picture); pWriter.WriteByte(); - pWriter.WriteByte(); pWriter.WriteShort((short) player.Unlock.Maps.Count); foreach (int mapId in player.Unlock.Maps) { pWriter.WriteInt(mapId); @@ -50,7 +51,6 @@ public static ByteWriter Request(IActor fieldPlayer) { pWriter.WriteUnicodeString("http://127.0.0.1:8080"); pWriter.WriteUnicodeString(); pWriter.WriteUnicodeString("^https?://127\\.0\\.0\\.1(:\\d+)?"); - pWriter.WriteUnicodeString(); return pWriter; } diff --git a/Maple2.Server.Game/Session/GameSession.cs b/Maple2.Server.Game/Session/GameSession.cs index f16ad6b67..6b270c71f 100644 --- a/Maple2.Server.Game/Session/GameSession.cs +++ b/Maple2.Server.Game/Session/GameSession.cs @@ -433,6 +433,8 @@ public bool EnterField() { Send(StatsPacket.Init(Player)); Field.Broadcast(StatsPacket.Update(Player), Player.Session); + Send(AdminPacket.Enable()); + //TODO: Save current hp/sp/ep in memory. This will help determine if player is dead upon login. var pWriter = Packet.Of(SendOp.UserState); pWriter.WriteInt(Player.ObjectId); diff --git a/Maple2.Server.Game/Util/ItemStatsCalculator.cs b/Maple2.Server.Game/Util/ItemStatsCalculator.cs index f835aa398..01a3ce62b 100644 --- a/Maple2.Server.Game/Util/ItemStatsCalculator.cs +++ b/Maple2.Server.Game/Util/ItemStatsCalculator.cs @@ -209,7 +209,7 @@ public bool RandomizeValues(Item item, ItemOption itemOptionMetadata, ref ItemSt foreach (SpecialAttribute attribute in option.Special.Keys) { if (table.SpecialValues.TryGetValue(attribute, out ItemEquipVariationTable.Set[]? values)) { int value = GetValue(specialAttribute: attribute, tableValues: values, rollMax: rollMax); - option.Special[attribute] = new SpecialOption(0f, value * option.MultiplyFactor); + option.Special[attribute] = new SpecialOption(value * option.MultiplyFactor); } else if (table.SpecialRates.TryGetValue(attribute, out ItemEquipVariationTable.Set[]? rates)) { int rateInt = GetValue(specialAttribute: attribute, tableRates: rates, rollMax: rollMax); option.Special[attribute] = new SpecialOption((rateInt / 1000f) * option.MultiplyFactor); @@ -498,7 +498,7 @@ private static ItemStats.Option ConstantItemOption(ItemOptionConstant option) { statResult.Add(attribute, new BasicOption(rate)); } foreach ((SpecialAttribute attribute, int value) in option.SpecialValues) { - specialResult.Add(attribute, new SpecialOption(0f, value)); + specialResult.Add(attribute, new SpecialOption(value)); } foreach ((SpecialAttribute attribute, float rate) in option.SpecialRates) { specialResult.Add(attribute, new SpecialOption(rate)); @@ -566,7 +566,7 @@ bool AddResult(ItemOption.Entry entry, IDictionary if (specialDict.ContainsKey(attribute)) return true; // Cannot add duplicate values, retry. if (entry.Values != null) { - specialDict.Add(attribute, new SpecialOption(0f, Random.Shared.Next(entry.Values.Value.Min, entry.Values.Value.Max + 1))); + specialDict.Add(attribute, new SpecialOption(Random.Shared.Next(entry.Values.Value.Min, entry.Values.Value.Max + 1))); } else if (entry.Rates != null) { float delta = entry.Rates.Value.Max - entry.Rates.Value.Min; specialDict.Add(attribute, new SpecialOption(Random.Shared.NextSingle() * delta + entry.Rates.Value.Min)); diff --git a/Maple2.Server.Game/appsettings.json b/Maple2.Server.Game/appsettings.json index e107d4d6c..ed1a6a6b7 100644 --- a/Maple2.Server.Game/appsettings.json +++ b/Maple2.Server.Game/appsettings.json @@ -25,7 +25,7 @@ "template": "{@t:HH:mm:ss.fff} {Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1),12} [{@l:u3}] <{ThreadId}> {@m}\n{@x}", "theme": "Serilog.Templates.Themes.TemplateTheme::Literate, Serilog.Expressions" }, - "restrictedToMinimumLevel": "Debug" + "restrictedToMinimumLevel": "Verbose" } }, { diff --git a/Maple2.Server.Login/PacketHandlers/CharacterManagementHandler.cs b/Maple2.Server.Login/PacketHandlers/CharacterManagementHandler.cs index 7ae9da7c9..3cb13b9ba 100644 --- a/Maple2.Server.Login/PacketHandlers/CharacterManagementHandler.cs +++ b/Maple2.Server.Login/PacketHandlers/CharacterManagementHandler.cs @@ -136,8 +136,9 @@ private void HandleCreate(LoginSession session, IByteReader packet) { int equipCount = packet.ReadByte(); for (int i = 0; i < equipCount; i++) { int id = packet.ReadInt(); - string slotStr = packet.ReadUnicodeString(); - if (!Enum.TryParse(slotStr, out EquipSlot slot) || slot is SK or OH or Unknown) { + byte slotByte = packet.ReadByte(); + EquipSlot slot = (EquipSlot) slotByte; + if (slot is SK or OH or Unknown) { session.Send(CharacterListPacket.CreateError(s_char_err_invalid_def_item)); return; } diff --git a/Maple2.Server.Login/PacketHandlers/ResponseKeyHandler.cs b/Maple2.Server.Login/PacketHandlers/ResponseKeyHandler.cs index f48e7507e..919997332 100644 --- a/Maple2.Server.Login/PacketHandlers/ResponseKeyHandler.cs +++ b/Maple2.Server.Login/PacketHandlers/ResponseKeyHandler.cs @@ -37,6 +37,10 @@ public override void Handle(LoginSession session, IByteReader packet) { MigrateInResponse response = World.MigrateIn(request); session.Init(accountId, machineId); session.Send(MigrationPacket.MoveResult(ok)); + + // KMS never sends ServerEnter so this needs to be included + session.ListServers(); + session.ListCharacters(); } catch (Exception ex) when (ex is RpcException or InvalidOperationException) { session.Send(MigrationPacket.MoveResult(s_move_err_default)); session.Disconnect(); diff --git a/Maple2.Server.Login/appsettings.json b/Maple2.Server.Login/appsettings.json index e19b1b35e..109c7872b 100644 --- a/Maple2.Server.Login/appsettings.json +++ b/Maple2.Server.Login/appsettings.json @@ -19,7 +19,7 @@ "template": "{@t:HH:mm:ss.fff} {Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1),12} [{@l:u3}] <{ThreadId}> {@m}\n{@x}", "theme": "Serilog.Templates.Themes.TemplateTheme::Literate, Serilog.Expressions" }, - "restrictedToMinimumLevel": "Debug" + "restrictedToMinimumLevel": "Verbose" } }, { diff --git a/Maple2.Server.World/Containers/BlackMarketLookup.cs b/Maple2.Server.World/Containers/BlackMarketLookup.cs index a49b82b26..b550cc36a 100644 --- a/Maple2.Server.World/Containers/BlackMarketLookup.cs +++ b/Maple2.Server.World/Containers/BlackMarketLookup.cs @@ -110,12 +110,11 @@ private bool ItemStatCheck(ItemStats stats, Dictionary Date: Thu, 3 Jul 2025 22:32:41 +0000 Subject: [PATCH 2/9] Initial plan From d879be7383cfd346522cd8d26614583f258228b3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 3 Jul 2025 22:42:52 +0000 Subject: [PATCH 3/9] Implement comprehensive character name validation with tests Co-authored-by: AngeloTadeucci <15664821+AngeloTadeucci@users.noreply.github.com> --- .../Validators/CharacterNameValidator.cs | 88 +++++++++++++++ .../CheckCharacterNameHandler.cs | 13 ++- .../CharacterManagementHandler.cs | 13 ++- .../Validators/CharacterNameValidatorTests.cs | 102 ++++++++++++++++++ 4 files changed, 202 insertions(+), 14 deletions(-) create mode 100644 Maple2.Model/Validators/CharacterNameValidator.cs create mode 100644 Maple2.Server.Tests/Validators/CharacterNameValidatorTests.cs diff --git a/Maple2.Model/Validators/CharacterNameValidator.cs b/Maple2.Model/Validators/CharacterNameValidator.cs new file mode 100644 index 000000000..9957ccdb8 --- /dev/null +++ b/Maple2.Model/Validators/CharacterNameValidator.cs @@ -0,0 +1,88 @@ +using System.Text.RegularExpressions; +using Maple2.Model.Error; +using Maple2.Model.Metadata; + +namespace Maple2.Model.Validators; + +public static class CharacterNameValidator { + // Common forbidden words that should not be allowed in character names + private static readonly HashSet ForbiddenWords = new(StringComparer.OrdinalIgnoreCase) { + "admin", "moderator", "gm", "gamemaster", "staff", "bot", "system", "server", + "maple", "nexon", "maplestory", "maple2", "administrator", "support", "helper", + "fuck", "shit", "bitch", "damn", "ass", "hell", "crap", "piss" + }; + + // Names that are completely banned + private static readonly HashSet BannedNames = new(StringComparer.OrdinalIgnoreCase) { + "admin", "administrator", "moderator", "gm", "gamemaster", "staff", "system", "server", + "maple", "nexon", "maplestory", "maple2", "support", "helper", "bot", "null", "undefined" + }; + + // Regex pattern for valid character names (letters, numbers, spaces, and some special characters) + private static readonly Regex ValidNamePattern = new(@"^[a-zA-Z0-9\s\-_]+$", RegexOptions.Compiled); + + /// + /// Validates a character name according to all rules. + /// + /// The character name to validate + /// CharacterCreateError code if invalid, null if valid + public static CharacterCreateError? ValidateName(string name) { + if (string.IsNullOrWhiteSpace(name)) { + return CharacterCreateError.s_char_err_name; + } + + // Trim whitespace for validation + string trimmedName = name.Trim(); + + // Check length constraints + if (trimmedName.Length < Constant.CharacterNameLengthMin) { + return CharacterCreateError.s_char_err_name; + } + + if (trimmedName.Length > Constant.CharacterNameLengthMax) { + return CharacterCreateError.s_char_err_system; + } + + // Check if the name is completely banned + if (BannedNames.Contains(trimmedName)) { + return CharacterCreateError.s_char_err_ban_all; + } + + // Check for forbidden words + if (ContainsForbiddenWord(trimmedName)) { + return CharacterCreateError.s_char_err_ban_any; + } + + // Check character pattern (letters, numbers, spaces, hyphens, underscores only) + if (!ValidNamePattern.IsMatch(trimmedName)) { + return CharacterCreateError.s_char_err_name; + } + + // Check for names that are only whitespace/special characters + if (trimmedName.All(c => !char.IsLetterOrDigit(c))) { + return CharacterCreateError.s_char_err_name; + } + + return null; // Valid name + } + + /// + /// Checks if the name contains any forbidden words. + /// + /// The name to check + /// True if it contains forbidden words + private static bool ContainsForbiddenWord(string name) { + string lowerName = name.ToLowerInvariant(); + return ForbiddenWords.Any(word => lowerName.Contains(word)); + } + + /// + /// Gets the forbidden word that was found in the name (for error messages). + /// + /// The name to check + /// The forbidden word found, or null if none + public static string? GetForbiddenWord(string name) { + string lowerName = name.ToLowerInvariant(); + return ForbiddenWords.FirstOrDefault(word => lowerName.Contains(word)); + } +} \ No newline at end of file diff --git a/Maple2.Server.Game/PacketHandlers/CheckCharacterNameHandler.cs b/Maple2.Server.Game/PacketHandlers/CheckCharacterNameHandler.cs index 87b449e6e..473f0cecf 100644 --- a/Maple2.Server.Game/PacketHandlers/CheckCharacterNameHandler.cs +++ b/Maple2.Server.Game/PacketHandlers/CheckCharacterNameHandler.cs @@ -1,5 +1,7 @@ using Maple2.Database.Storage; +using Maple2.Model.Error; using Maple2.Model.Metadata; +using Maple2.Model.Validators; using Maple2.PacketLib.Tools; using Maple2.Server.Core.Constants; using Maple2.Server.Game.PacketHandlers.Field; @@ -23,13 +25,10 @@ public override void Handle(GameSession session, IByteReader packet) { string characterName = packet.ReadUnicodeString(); long itemUid = packet.ReadLong(); - if (characterName.Length < Constant.CharacterNameLengthMin) { - session.Send(CharacterListPacket.CreateError(s_char_err_name)); - return; - } - - if (characterName.Length > Constant.CharacterNameLengthMax) { - session.Send(CharacterListPacket.CreateError(s_char_err_system)); + // Validate character name using comprehensive validator + var nameError = CharacterNameValidator.ValidateName(characterName); + if (nameError != null) { + session.Send(CharacterListPacket.CreateError(nameError.Value)); return; } diff --git a/Maple2.Server.Login/PacketHandlers/CharacterManagementHandler.cs b/Maple2.Server.Login/PacketHandlers/CharacterManagementHandler.cs index a57530bc3..0ce99810c 100644 --- a/Maple2.Server.Login/PacketHandlers/CharacterManagementHandler.cs +++ b/Maple2.Server.Login/PacketHandlers/CharacterManagementHandler.cs @@ -7,8 +7,10 @@ using Maple2.Database.Storage; using Maple2.Model.Common; using Maple2.Model.Enum; +using Maple2.Model.Error; using Maple2.Model.Game; using Maple2.Model.Metadata; +using Maple2.Model.Validators; using Maple2.PacketLib.Tools; using Maple2.Server.Core.Constants; using Maple2.Server.Core.PacketHandlers; @@ -113,13 +115,10 @@ private void HandleCreate(LoginSession session, IByteReader packet) { var job = (Job) ((int) jobCode * 10); string name = packet.ReadUnicodeString(); - if (name.Length < Constant.CharacterNameLengthMin) { - session.Send(CharacterListPacket.CreateError(s_char_err_name)); - return; - } - - if (name.Length > Constant.CharacterNameLengthMax) { - session.Send(CharacterListPacket.CreateError(s_char_err_system)); + // Validate character name using comprehensive validator + CharacterCreateError? nameError = CharacterNameValidator.ValidateName(name); + if (nameError != null) { + session.Send(CharacterListPacket.CreateError(nameError.Value)); return; } diff --git a/Maple2.Server.Tests/Validators/CharacterNameValidatorTests.cs b/Maple2.Server.Tests/Validators/CharacterNameValidatorTests.cs new file mode 100644 index 000000000..2d66116db --- /dev/null +++ b/Maple2.Server.Tests/Validators/CharacterNameValidatorTests.cs @@ -0,0 +1,102 @@ +using Maple2.Model.Error; +using Maple2.Model.Validators; + +namespace Maple2.Server.Tests.Validators; + +public class CharacterNameValidatorTests { + [Test] + public void ValidName_ShouldReturnNull() { + // Valid names should return null (no error) + Assert.That(CharacterNameValidator.ValidateName("ValidName"), Is.Null); + Assert.That(CharacterNameValidator.ValidateName("Test123"), Is.Null); + Assert.That(CharacterNameValidator.ValidateName("User_Name"), Is.Null); + Assert.That(CharacterNameValidator.ValidateName("Cool-Name"), Is.Null); + Assert.That(CharacterNameValidator.ValidateName("ab"), Is.Null); // minimum length + Assert.That(CharacterNameValidator.ValidateName("abcdefghijkl"), Is.Null); // maximum length + } + + [Test] + public void TooShortName_ShouldReturnNameError() { + // Names shorter than minimum should return name error + Assert.That(CharacterNameValidator.ValidateName("a"), Is.EqualTo(CharacterCreateError.s_char_err_name)); + Assert.That(CharacterNameValidator.ValidateName(""), Is.EqualTo(CharacterCreateError.s_char_err_name)); + Assert.That(CharacterNameValidator.ValidateName(" "), Is.EqualTo(CharacterCreateError.s_char_err_name)); + } + + [Test] + public void TooLongName_ShouldReturnSystemError() { + // Names longer than maximum should return system error + Assert.That(CharacterNameValidator.ValidateName("abcdefghijklm"), Is.EqualTo(CharacterCreateError.s_char_err_system)); + Assert.That(CharacterNameValidator.ValidateName("ThisNameIsTooLong"), Is.EqualTo(CharacterCreateError.s_char_err_system)); + } + + [Test] + public void BannedName_ShouldReturnBanAllError() { + // Completely banned names should return ban_all error + Assert.That(CharacterNameValidator.ValidateName("admin"), Is.EqualTo(CharacterCreateError.s_char_err_ban_all)); + Assert.That(CharacterNameValidator.ValidateName("ADMIN"), Is.EqualTo(CharacterCreateError.s_char_err_ban_all)); + Assert.That(CharacterNameValidator.ValidateName("moderator"), Is.EqualTo(CharacterCreateError.s_char_err_ban_all)); + Assert.That(CharacterNameValidator.ValidateName("gm"), Is.EqualTo(CharacterCreateError.s_char_err_ban_all)); + Assert.That(CharacterNameValidator.ValidateName("system"), Is.EqualTo(CharacterCreateError.s_char_err_ban_all)); + Assert.That(CharacterNameValidator.ValidateName("maple"), Is.EqualTo(CharacterCreateError.s_char_err_ban_all)); + } + + [Test] + public void ForbiddenWord_ShouldReturnBanAnyError() { + // Names containing forbidden words should return ban_any error + Assert.That(CharacterNameValidator.ValidateName("TestAdmin"), Is.EqualTo(CharacterCreateError.s_char_err_ban_any)); + Assert.That(CharacterNameValidator.ValidateName("MyStaff"), Is.EqualTo(CharacterCreateError.s_char_err_ban_any)); + Assert.That(CharacterNameValidator.ValidateName("CoolBot"), Is.EqualTo(CharacterCreateError.s_char_err_ban_any)); + Assert.That(CharacterNameValidator.ValidateName("fucked"), Is.EqualTo(CharacterCreateError.s_char_err_ban_any)); + Assert.That(CharacterNameValidator.ValidateName("shitty"), Is.EqualTo(CharacterCreateError.s_char_err_ban_any)); + } + + [Test] + public void InvalidCharacters_ShouldReturnNameError() { + // Names with invalid characters should return name error + Assert.That(CharacterNameValidator.ValidateName("test@name"), Is.EqualTo(CharacterCreateError.s_char_err_name)); + Assert.That(CharacterNameValidator.ValidateName("name#test"), Is.EqualTo(CharacterCreateError.s_char_err_name)); + Assert.That(CharacterNameValidator.ValidateName("test$name"), Is.EqualTo(CharacterCreateError.s_char_err_name)); + Assert.That(CharacterNameValidator.ValidateName("test%name"), Is.EqualTo(CharacterCreateError.s_char_err_name)); + Assert.That(CharacterNameValidator.ValidateName("test*name"), Is.EqualTo(CharacterCreateError.s_char_err_name)); + } + + [Test] + public void OnlySpecialCharacters_ShouldReturnNameError() { + // Names with only special characters should return name error + Assert.That(CharacterNameValidator.ValidateName("--"), Is.EqualTo(CharacterCreateError.s_char_err_name)); + Assert.That(CharacterNameValidator.ValidateName("__"), Is.EqualTo(CharacterCreateError.s_char_err_name)); + Assert.That(CharacterNameValidator.ValidateName(" "), Is.EqualTo(CharacterCreateError.s_char_err_name)); + Assert.That(CharacterNameValidator.ValidateName("-_-"), Is.EqualTo(CharacterCreateError.s_char_err_name)); + } + + [Test] + public void NullOrWhitespace_ShouldReturnNameError() { + // Null or whitespace names should return name error + Assert.That(CharacterNameValidator.ValidateName(null!), Is.EqualTo(CharacterCreateError.s_char_err_name)); + Assert.That(CharacterNameValidator.ValidateName(""), Is.EqualTo(CharacterCreateError.s_char_err_name)); + Assert.That(CharacterNameValidator.ValidateName(" "), Is.EqualTo(CharacterCreateError.s_char_err_name)); + } + + [Test] + public void GetForbiddenWord_ShouldReturnCorrectWord() { + // Should return the forbidden word found in the name + Assert.That(CharacterNameValidator.GetForbiddenWord("TestAdmin"), Is.EqualTo("admin")); + Assert.That(CharacterNameValidator.GetForbiddenWord("MyStaff"), Is.EqualTo("staff")); + Assert.That(CharacterNameValidator.GetForbiddenWord("CoolBot"), Is.EqualTo("bot")); + Assert.That(CharacterNameValidator.GetForbiddenWord("fucked"), Is.EqualTo("fuck")); + Assert.That(CharacterNameValidator.GetForbiddenWord("ValidName"), Is.Null); + } + + [Test] + public void CaseInsensitiveValidation_ShouldWork() { + // Validation should be case insensitive + Assert.That(CharacterNameValidator.ValidateName("ADMIN"), Is.EqualTo(CharacterCreateError.s_char_err_ban_all)); + Assert.That(CharacterNameValidator.ValidateName("Admin"), Is.EqualTo(CharacterCreateError.s_char_err_ban_all)); + Assert.That(CharacterNameValidator.ValidateName("aDmIn"), Is.EqualTo(CharacterCreateError.s_char_err_ban_all)); + + Assert.That(CharacterNameValidator.ValidateName("TESTADMIN"), Is.EqualTo(CharacterCreateError.s_char_err_ban_any)); + Assert.That(CharacterNameValidator.ValidateName("TestAdmin"), Is.EqualTo(CharacterCreateError.s_char_err_ban_any)); + Assert.That(CharacterNameValidator.ValidateName("testADMIN"), Is.EqualTo(CharacterCreateError.s_char_err_ban_any)); + } +} \ No newline at end of file From d684c389192e403d8644cf79fa2261ccac5dd926 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 3 Jul 2025 22:56:08 +0000 Subject: [PATCH 4/9] Apply dotnet format to entire solution Co-authored-by: AngeloTadeucci <15664821+AngeloTadeucci@users.noreply.github.com> --- Maple2.Database/Context/MetadataContext.cs | 526 +- Maple2.Database/Context/Ms2Context.cs | 196 +- Maple2.Database/Context/WebContext.cs | 26 +- .../Extensions/DateTimeExtensions.cs | 30 +- .../Extensions/DbContextExtensions.cs | 72 +- .../Extensions/PropertyBuilderExtensions.cs | 70 +- .../Extensions/Vector3Converter.cs | 46 +- Maple2.Database/Model/Account.cs | 304 +- Maple2.Database/Model/Achievement.cs | 112 +- Maple2.Database/Model/Buddy.cs | 116 +- Maple2.Database/Model/Character.cs | 346 +- Maple2.Database/Model/CharacterConfig.cs | 274 +- Maple2.Database/Model/CharacterUnlock.cs | 332 +- Maple2.Database/Model/Club.cs | 184 +- Maple2.Database/Model/DungeonRankReward.cs | 54 +- Maple2.Database/Model/DungeonRecord.cs | 140 +- Maple2.Database/Model/FishEntry.cs | 58 +- Maple2.Database/Model/GameEventUserValue.cs | 82 +- Maple2.Database/Model/Guild/Guild.cs | 208 +- .../Model/Guild/GuildApplication.cs | 56 +- Maple2.Database/Model/Guild/GuildMember.cs | 108 +- Maple2.Database/Model/Item/Item.cs | 324 +- Maple2.Database/Model/Item/ItemAppearance.cs | 138 +- Maple2.Database/Model/Item/ItemInfo.cs | 84 +- Maple2.Database/Model/Item/ItemSocket.cs | 80 +- Maple2.Database/Model/Item/ItemStats.cs | 118 +- Maple2.Database/Model/Item/ItemSubType.cs | 270 +- Maple2.Database/Model/ItemStorage.cs | 38 +- Maple2.Database/Model/Mail.cs | 220 +- Maple2.Database/Model/Map/Home.cs | 222 +- Maple2.Database/Model/Map/HomeLayout.cs | 80 +- Maple2.Database/Model/Map/HomeLayoutCube.cs | 92 +- Maple2.Database/Model/Map/InteractCube.cs | 144 +- Maple2.Database/Model/Map/Nurturing.cs | 50 +- Maple2.Database/Model/Map/UgcBannerSlot.cs | 76 +- Maple2.Database/Model/Map/UgcMap.cs | 90 +- Maple2.Database/Model/Map/UgcMapCube.cs | 98 +- .../Model/Market/BlackMarketListing.cs | 112 +- Maple2.Database/Model/Market/MesoListing.cs | 186 +- .../Model/Market/SoldMeretMarketItem.cs | 64 +- .../Model/Market/SoldUgcMarketItem.cs | 106 +- Maple2.Database/Model/Market/UgcMarketItem.cs | 168 +- Maple2.Database/Model/Marriage.cs | 154 +- Maple2.Database/Model/Medal.cs | 78 +- .../Model/Metadata/SchemaVersion.cs | 32 +- .../Model/Metadata/TableChecksum.cs | 38 +- Maple2.Database/Model/PetConfig.cs | 74 +- Maple2.Database/Model/PlayerReport.cs | 84 +- Maple2.Database/Model/Quest.cs | 164 +- .../Model/Ranking/TrophyRankInfo.cs | 10 +- Maple2.Database/Model/ServerInfo.cs | 28 +- .../Model/Shop/CharacterShopData.cs | 80 +- .../Model/Shop/CharacterShopItemData.cs | 78 +- Maple2.Database/Model/SkillTab.cs | 104 +- Maple2.Database/Model/SystemBanner.cs | 76 +- Maple2.Database/Model/UgcResource.cs | 84 +- Maple2.Database/Model/WeddingHall.cs | 132 +- .../Storage/Game/DatabaseRequest.cs | 76 +- .../Storage/Game/GameStorage.Achievement.cs | 130 +- .../Storage/Game/GameStorage.Buddy.cs | 106 +- .../Storage/Game/GameStorage.Club.cs | 282 +- .../Storage/Game/GameStorage.Dungeon.cs | 68 +- .../Game/GameStorage.GameEventUserValue.cs | 120 +- .../Storage/Game/GameStorage.Guild.cs | 464 +- .../Storage/Game/GameStorage.HomeLayout.cs | 74 +- .../Storage/Game/GameStorage.Item.cs | 384 +- .../Storage/Game/GameStorage.Mail.cs | 282 +- .../Storage/Game/GameStorage.Map.cs | 674 +- .../Storage/Game/GameStorage.Market.cs | 634 +- .../Storage/Game/GameStorage.Medal.cs | 92 +- .../Storage/Game/GameStorage.Nurturing.cs | 112 +- .../Storage/Game/GameStorage.Quest.cs | 116 +- .../Storage/Game/GameStorage.Report.cs | 186 +- .../Storage/Game/GameStorage.ServerInfo.cs | 82 +- .../Storage/Game/GameStorage.Shop.cs | 206 +- .../Storage/Game/GameStorage.SystemBanner.cs | 28 +- .../Game/GameStorage.UgcBannerSlots.cs | 84 +- .../Storage/Game/GameStorage.User.cs | 1052 +-- .../Storage/Game/GameStorage.Web.cs | 248 +- .../Storage/Game/GameStorage.Wedding.cs | 322 +- Maple2.Database/Storage/Game/GameStorage.cs | 156 +- .../Storage/IPlayerInfoProvider.cs | 14 +- .../Metadata/AchievementMetadataStorage.cs | 150 +- .../Storage/Metadata/AiMetadataStorage.cs | 76 +- .../Metadata/FunctionCubeMetadataStorage.cs | 42 +- .../Storage/Metadata/ItemMetadataStorage.cs | 172 +- .../Storage/Metadata/MapDataStorage.cs | 94 +- .../Storage/Metadata/MapEntityStorage.cs | 290 +- .../Storage/Metadata/MapMetadataStorage.cs | 186 +- .../Storage/Metadata/MetadataStorage.cs | 46 +- .../Storage/Metadata/NpcMetadataStorage.cs | 176 +- .../Storage/Metadata/QuestMetadataStorage.cs | 142 +- .../Storage/Metadata/RideMetadataStorage.cs | 64 +- .../Storage/Metadata/ScriptMetadataStorage.cs | 64 +- .../Metadata/ServerTableMetadataStorage.cs | 222 +- .../Storage/Metadata/SkillMetadataStorage.cs | 158 +- .../Storage/Metadata/TableMetadataStorage.cs | 448 +- .../Storage/Metadata/TriggerScriptMetadata.cs | 64 +- .../Storage/Metadata/TriggerStorage.cs | 218 +- Maple2.Database/Storage/Web/WebStorage.cs | 126 +- Maple2.File.Ingest/Helpers/NifParserHelper.cs | 220 +- .../Mapper/AchievementMapper.cs | 124 +- .../Mapper/AdditionalEffectMapper.cs | 530 +- Maple2.File.Ingest/Mapper/AiMapper.cs | 1080 +-- Maple2.File.Ingest/Mapper/AnimationMapper.cs | 80 +- .../Mapper/FunctionCubeMapper.cs | 142 +- Maple2.File.Ingest/Mapper/ItemMapper.cs | 476 +- Maple2.File.Ingest/Mapper/MapDataMapper.cs | 752 +- Maple2.File.Ingest/Mapper/MapEntityMapper.cs | 632 +- Maple2.File.Ingest/Mapper/MapMapper.cs | 194 +- Maple2.File.Ingest/Mapper/NXSMeshMapper.cs | 24 +- Maple2.File.Ingest/Mapper/NavMeshMapper.cs | 1402 ++-- Maple2.File.Ingest/Mapper/NifMapper.cs | 96 +- Maple2.File.Ingest/Mapper/NpcMapper.cs | 310 +- Maple2.File.Ingest/Mapper/PetMapper.cs | 150 +- Maple2.File.Ingest/Mapper/QuestMapper.cs | 272 +- Maple2.File.Ingest/Mapper/RideMapper.cs | 86 +- Maple2.File.Ingest/Mapper/ScriptMapper.cs | 206 +- .../Mapper/ServerTableMapper.cs | 4218 +++++----- Maple2.File.Ingest/Mapper/SkillMapper.cs | 342 +- Maple2.File.Ingest/Mapper/TableMapper.cs | 3652 ++++----- Maple2.File.Ingest/Mapper/TriggerMapper.cs | 522 +- Maple2.File.Ingest/Mapper/TypeMapper.cs | 82 +- Maple2.File.Ingest/Mapper/UgcMapMapper.cs | 138 +- Maple2.File.Ingest/MapperExtensions.cs | 1076 +-- Maple2.File.Ingest/Program.cs | 566 +- Maple2.File.Ingest/SchemaVersionManager.cs | 146 +- Maple2.File.Ingest/Utils/AiTranslate.cs | 74 +- .../Utils/AttributeExtensions.cs | 480 +- Maple2.File.Ingest/Utils/DotRecast.cs | 258 +- Maple2.File.Ingest/Utils/NavmeshHash.cs | 82 +- Maple2.File.Ingest/Utils/StringTable.cs | 54 +- .../Utils/TriggerDefinitionOverride.cs | 2346 +++--- Maple2.File.Ingest/Utils/TriggerTranslate.cs | 256 +- Maple2.Model/Common/Byte3.cs | 22 +- Maple2.Model/Common/Color.cs | 114 +- Maple2.Model/Common/ServerTableNames.cs | 54 +- Maple2.Model/Common/TableNames.cs | 174 +- Maple2.Model/Common/Vector.cs | 174 +- Maple2.Model/Enum/Achievement.cs | 60 +- Maple2.Model/Enum/ActorState.cs | 946 +-- Maple2.Model/Enum/Admin.cs | 166 +- Maple2.Model/Enum/AllianceType.cs | 52 +- Maple2.Model/Enum/AttendGift.cs | 28 +- Maple2.Model/Enum/AttributePointSource.cs | 16 +- Maple2.Model/Enum/BadgeType.cs | 32 +- Maple2.Model/Enum/BasicAttribute.cs | 78 +- Maple2.Model/Enum/BeautyShop.cs | 34 +- Maple2.Model/Enum/BlackMarketSort.cs | 16 +- Maple2.Model/Enum/BlueMarbleSlotType.cs | 28 +- Maple2.Model/Enum/BlueprintType.cs | 12 +- Maple2.Model/Enum/BreakableState.cs | 18 +- Maple2.Model/Enum/BuddyType.cs | 18 +- Maple2.Model/Enum/Buff.cs | 196 +- Maple2.Model/Enum/CaughtFishType.cs | 24 +- Maple2.Model/Enum/ChatType.cs | 118 +- Maple2.Model/Enum/Club.cs | 26 +- Maple2.Model/Enum/CombineSpawnGroupType.cs | 14 +- Maple2.Model/Enum/CompareType.cs | 28 +- Maple2.Model/Enum/ConditionType.cs | 654 +- Maple2.Model/Enum/ConfigurableCubeType.cs | 16 +- Maple2.Model/Enum/CubePortalDestination.cs | 14 +- Maple2.Model/Enum/CurrencyType.cs | 40 +- Maple2.Model/Enum/DamageType.cs | 18 +- Maple2.Model/Enum/Death.cs | 14 +- Maple2.Model/Enum/DropType.cs | 14 +- Maple2.Model/Enum/Dungeon.cs | 330 +- Maple2.Model/Enum/EnchantResult.cs | 44 +- Maple2.Model/Enum/EnchantScrollType.cs | 18 +- Maple2.Model/Enum/EnchantType.cs | 26 +- Maple2.Model/Enum/EquipSlot.cs | 100 +- Maple2.Model/Enum/ExpMessageCode.cs | 88 +- Maple2.Model/Enum/ExpType.cs | 126 +- Maple2.Model/Enum/FieldProperty.cs | 52 +- Maple2.Model/Enum/FieldType.cs | 14 +- Maple2.Model/Enum/FishingItemType.cs | 16 +- Maple2.Model/Enum/FurnishingCurrencyType.cs | 12 +- Maple2.Model/Enum/GameEventType.cs | 238 +- Maple2.Model/Enum/GameEventUserValueType.cs | 72 +- Maple2.Model/Enum/GameRankingType.cs | 74 +- Maple2.Model/Enum/Gender.cs | 14 +- Maple2.Model/Enum/GuildFocus.cs | 48 +- Maple2.Model/Enum/GuildNpcType.cs | 40 +- Maple2.Model/Enum/GuildPermission.cs | 38 +- Maple2.Model/Enum/HomePermission.cs | 46 +- Maple2.Model/Enum/HomeSetting.cs | 88 +- Maple2.Model/Enum/HousingCategory.cs | 72 +- .../Enum/IndividualItemDropCategory.cs | 32 +- Maple2.Model/Enum/InsigniaConditionType.cs | 36 +- Maple2.Model/Enum/InstanceType.cs | 40 +- Maple2.Model/Enum/Instrument.cs | 88 +- Maple2.Model/Enum/Interact.cs | 146 +- Maple2.Model/Enum/InventoryType.cs | 44 +- Maple2.Model/Enum/ItemFunction.cs | 144 +- Maple2.Model/Enum/ItemGroup.cs | 40 +- Maple2.Model/Enum/ItemOptionMakeType.cs | 14 +- Maple2.Model/Enum/ItemTag.cs | 898 +-- Maple2.Model/Enum/JobGroup.cs | 94 +- Maple2.Model/Enum/LapenshardSlot.cs | 20 +- Maple2.Model/Enum/LiftableState.cs | 16 +- Maple2.Model/Enum/LiquidType.cs | 30 +- Maple2.Model/Enum/Locale.cs | 42 +- Maple2.Model/Enum/Maid.cs | 14 +- Maple2.Model/Enum/MailType.cs | 26 +- Maple2.Model/Enum/Map.cs | 110 +- Maple2.Model/Enum/MapAttribute.cs | 32 +- Maple2.Model/Enum/MasteryType.cs | 32 +- Maple2.Model/Enum/MedalType.cs | 14 +- Maple2.Model/Enum/Mentoring.cs | 26 +- Maple2.Model/Enum/MeretMarket.cs | 142 +- Maple2.Model/Enum/MigrationType.cs | 16 +- Maple2.Model/Enum/NpcAi.cs | 130 +- Maple2.Model/Enum/NpcTalk.cs | 262 +- Maple2.Model/Enum/NxShapeType.cs | 28 +- Maple2.Model/Enum/Party.cs | 60 +- Maple2.Model/Enum/PlayerObjectFlag.cs | 28 +- Maple2.Model/Enum/PlotState.cs | 14 +- Maple2.Model/Enum/Portal.cs | 38 +- Maple2.Model/Enum/Prestige.cs | 14 +- Maple2.Model/Enum/Quest.cs | 88 +- Maple2.Model/Enum/Reputation.cs | 28 +- Maple2.Model/Enum/ResetType.cs | 18 +- Maple2.Model/Enum/RideType.cs | 76 +- Maple2.Model/Enum/RoomTimerType.cs | 12 +- Maple2.Model/Enum/ScriptType.cs | 28 +- Maple2.Model/Enum/SessionState.cs | 16 +- Maple2.Model/Enum/Shop.cs | 90 +- Maple2.Model/Enum/Skill.cs | 436 +- Maple2.Model/Enum/SkillPointSource.cs | 14 +- Maple2.Model/Enum/SmartPushType.cs | 14 +- Maple2.Model/Enum/SpecialAttribute.cs | 730 +- Maple2.Model/Enum/StringCode.cs | 6910 ++++++++--------- Maple2.Model/Enum/SystemBanner.cs | 46 +- Maple2.Model/Enum/TimeEventType.cs | 22 +- Maple2.Model/Enum/TransferFlag.cs | 42 +- Maple2.Model/Enum/Ugc.cs | 36 +- Maple2.Model/Enum/UgcMarketHomeCategory.cs | 28 +- Maple2.Model/Enum/Wedding.cs | 82 +- Maple2.Model/Enum/Widget.cs | 16 +- Maple2.Model/Error/AttendanceError.cs | 36 +- Maple2.Model/Error/BeautyError.cs | 58 +- Maple2.Model/Error/BlackMarketError.cs | 146 +- Maple2.Model/Error/BuddyError.cs | 60 +- Maple2.Model/Error/ChangeAttributesError.cs | 50 +- .../Error/ChangeAttributesScrollError.cs | 84 +- Maple2.Model/Error/CharacterCreateError.cs | 58 +- Maple2.Model/Error/CharacterDeleteError.cs | 70 +- Maple2.Model/Error/ChatStickerError.cs | 24 +- Maple2.Model/Error/ClubError.cs | 104 +- Maple2.Model/Error/DungeonMatchError.cs | 54 +- Maple2.Model/Error/DungeonRoomError.cs | 146 +- Maple2.Model/Error/EmoteError.cs | 76 +- Maple2.Model/Error/EnchantScrollError.cs | 52 +- Maple2.Model/Error/FishingError.cs | 50 +- Maple2.Model/Error/FunctionCubeError.cs | 30 +- Maple2.Model/Error/GroupChatError.cs | 34 +- Maple2.Model/Error/GuildError.cs | 196 +- Maple2.Model/Error/ItemBoxError.cs | 26 +- Maple2.Model/Error/ItemEnchantError.cs | 40 +- Maple2.Model/Error/ItemExchangeScrollError.cs | 52 +- Maple2.Model/Error/ItemInventoryError.cs | 60 +- Maple2.Model/Error/ItemMergeError.cs | 46 +- Maple2.Model/Error/ItemRepackError.cs | 46 +- Maple2.Model/Error/ItemSocketError.cs | 72 +- Maple2.Model/Error/ItemSocketScrollError.cs | 68 +- Maple2.Model/Error/JobError.cs | 62 +- Maple2.Model/Error/LimitBreakError.cs | 22 +- Maple2.Model/Error/MailError.cs | 110 +- Maple2.Model/Error/MapleopolyError.cs | 40 +- Maple2.Model/Error/MasteryError.cs | 44 +- Maple2.Model/Error/MesoMarketError.cs | 100 +- Maple2.Model/Error/MigrationError.cs | 86 +- Maple2.Model/Error/MyInfoError.cs | 26 +- Maple2.Model/Error/PartyError.cs | 192 +- Maple2.Model/Error/PartySearchError.cs | 72 +- Maple2.Model/Error/PetError.cs | 46 +- Maple2.Model/Error/QuestError.cs | 42 +- Maple2.Model/Error/ShopError.cs | 88 +- Maple2.Model/Error/StorageInventoryError.cs | 70 +- Maple2.Model/Error/TradeError.cs | 106 +- Maple2.Model/Error/UgcMapError.cs | 454 +- Maple2.Model/Error/WeddingError.cs | 252 +- Maple2.Model/Game/Buddy.cs | 208 +- Maple2.Model/Game/ChatSticker.cs | 28 +- Maple2.Model/Game/Club/Club.cs | 98 +- Maple2.Model/Game/Club/ClubInvite.cs | 38 +- Maple2.Model/Game/Club/ClubMember.cs | 108 +- Maple2.Model/Game/Config/KeyTable.cs | 22 +- Maple2.Model/Game/Config/PetConfig.cs | 66 +- Maple2.Model/Game/Config/SkillMacro.cs | 78 +- Maple2.Model/Game/Config/Wardrobe.cs | 98 +- Maple2.Model/Game/Cube/ConfigurableCube.cs | 82 +- Maple2.Model/Game/Cube/GuideObject.cs | 138 +- Maple2.Model/Game/Cube/HeldCube.cs | 74 +- Maple2.Model/Game/Cube/InteractCube.cs | 110 +- Maple2.Model/Game/Cube/LiftableCube.cs | 26 +- Maple2.Model/Game/Cube/LiftupWeapon.cs | 34 +- Maple2.Model/Game/Cube/Nurturing.cs | 170 +- Maple2.Model/Game/Cube/PlotCube.cs | 50 +- Maple2.Model/Game/Dungeon/DungeonMission.cs | 68 +- .../Game/Dungeon/DungeonRankReward.cs | 38 +- Maple2.Model/Game/Dungeon/DungeonRecord.cs | 86 +- .../Game/Dungeon/DungeonRoomRecord.cs | 34 +- .../Game/Dungeon/DungeonUserRecord.cs | 180 +- .../Game/Dungeon/DungeonUserResult.cs | 36 +- .../Game/Dungeon/IUserContentRecord.cs | 20 +- .../Game/Dungeon/MiniGameUserRecord.cs | 106 +- Maple2.Model/Game/Emote.cs | 30 +- Maple2.Model/Game/EnchantRates.cs | 56 +- Maple2.Model/Game/Event/GameEvent.cs | 500 +- .../Game/Field/FieldAccelerationStructure.cs | 2024 ++--- Maple2.Model/Game/FishEntry.cs | 44 +- Maple2.Model/Game/GlobalPortal.cs | 30 +- Maple2.Model/Game/GroupChat/GroupChat.cs | 30 +- .../Game/GroupChat/GroupChatMember.cs | 30 +- Maple2.Model/Game/Guild/Guild.cs | 276 +- Maple2.Model/Game/Guild/GuildApplication.cs | 52 +- Maple2.Model/Game/Guild/GuildInvite.cs | 80 +- Maple2.Model/Game/Guild/GuildMember.cs | 156 +- Maple2.Model/Game/Guild/GuildPoster.cs | 38 +- Maple2.Model/Game/Guild/GuildRank.cs | 46 +- Maple2.Model/Game/IFieldProperty.cs | 300 +- Maple2.Model/Game/InteractObject.cs | 252 +- Maple2.Model/Game/InterfaceText.cs | 94 +- Maple2.Model/Game/Item/IngredientInfo.cs | 40 +- Maple2.Model/Game/Item/Item.cs | 538 +- Maple2.Model/Game/Item/ItemAppearance.cs | 292 +- Maple2.Model/Game/Item/ItemBadge.cs | 168 +- Maple2.Model/Game/Item/ItemBinding.cs | 80 +- Maple2.Model/Game/Item/ItemBlueprint.cs | 90 +- Maple2.Model/Game/Item/ItemComponent.cs | 18 +- Maple2.Model/Game/Item/ItemCoupleInfo.cs | 74 +- .../Game/Item/ItemCustomMusicScore.cs | 98 +- Maple2.Model/Game/Item/ItemEnchant.cs | 132 +- Maple2.Model/Game/Item/ItemLimitBreak.cs | 120 +- Maple2.Model/Game/Item/ItemOption.cs | 132 +- Maple2.Model/Game/Item/ItemPet.cs | 80 +- Maple2.Model/Game/Item/ItemSocket.cs | 220 +- Maple2.Model/Game/Item/ItemStats.cs | 250 +- Maple2.Model/Game/Item/ItemTransfer.cs | 160 +- Maple2.Model/Game/Item/ItemType.cs | 232 +- Maple2.Model/Game/Item/UgcItemLook.cs | 112 +- Maple2.Model/Game/Item/UgcItemMusicScore.cs | 64 +- Maple2.Model/Game/Mail.cs | 340 +- .../Game/Market/BlackMarketListing.cs | 74 +- Maple2.Model/Game/Market/MarketItem.cs | 40 +- Maple2.Model/Game/Market/MeretMarketSearch.cs | 56 +- Maple2.Model/Game/Market/MesoListing.cs | 58 +- Maple2.Model/Game/Market/PremiumMarketItem.cs | 144 +- .../Game/Market/PremiumMarketPromoData.cs | 40 +- Maple2.Model/Game/Market/SoldUgcMarketItem.cs | 60 +- Maple2.Model/Game/Market/UgcMarketItem.cs | 116 +- Maple2.Model/Game/Medal.cs | 42 +- Maple2.Model/Game/Npc/Npc.cs | 34 +- Maple2.Model/Game/Npc/NpcDialogue.cs | 34 +- Maple2.Model/Game/Npc/NpcTalkScript.cs | 10 +- Maple2.Model/Game/Party/Party.cs | 124 +- Maple2.Model/Game/Party/PartyMember.cs | 72 +- Maple2.Model/Game/Party/PartySearch.cs | 78 +- Maple2.Model/Game/Party/PartyVote.cs | 110 +- Maple2.Model/Game/Quest/PrestigeMission.cs | 40 +- Maple2.Model/Game/Quest/Quest.cs | 92 +- Maple2.Model/Game/RewardItem.cs | 94 +- Maple2.Model/Game/Ride/Ride.cs | 34 +- Maple2.Model/Game/Ride/RideOffAction.cs | 382 +- Maple2.Model/Game/Ride/RideOnAction.cs | 122 +- Maple2.Model/Game/Shop/BeautyShop.cs | 66 +- Maple2.Model/Game/Shop/BeautyShopCost.cs | 50 +- Maple2.Model/Game/Shop/BuyBackItem.cs | 38 +- Maple2.Model/Game/Shop/CharacterShopData.cs | 20 +- .../Game/Shop/CharacterShopItemData.cs | 16 +- Maple2.Model/Game/Shop/RestrictedBuyData.cs | 120 +- Maple2.Model/Game/Shop/Shop.cs | 100 +- Maple2.Model/Game/Shop/ShopCost.cs | 64 +- Maple2.Model/Game/Shop/ShopItem.cs | 120 +- Maple2.Model/Game/Shop/ShopRestock.cs | 54 +- Maple2.Model/Game/Sync/StateSync.cs | 386 +- .../Game/Sync/StateSyncCoupleDance.cs | 48 +- Maple2.Model/Game/Sync/StateSyncRps.cs | 54 +- .../Game/Sync/StateSyncWeddingEmotion.cs | 76 +- Maple2.Model/Game/SystemBanner.cs | 66 +- Maple2.Model/Game/TriggerObject.cs | 252 +- Maple2.Model/Game/Ugc/UgcBanner.cs | 124 +- Maple2.Model/Game/Ugc/UgcBannerReservation.cs | 26 +- Maple2.Model/Game/Ugc/UgcInfo.cs | 28 +- Maple2.Model/Game/Ugc/UgcResource.cs | 26 +- Maple2.Model/Game/User/Account.cs | 80 +- Maple2.Model/Game/User/Achievement.cs | 134 +- Maple2.Model/Game/User/Character.cs | 116 +- Maple2.Model/Game/User/GameEventUserValue.cs | 84 +- Maple2.Model/Game/User/Home.cs | 490 +- Maple2.Model/Game/User/HomeSurvey.cs | 106 +- Maple2.Model/Game/User/IPlayerInfo.cs | 86 +- Maple2.Model/Game/User/Marriage.cs | 182 +- Maple2.Model/Game/User/Mastery.cs | 70 +- Maple2.Model/Game/User/Player.cs | 114 +- Maple2.Model/Game/User/PlayerInfo.cs | 378 +- Maple2.Model/Game/User/PlotInfo.cs | 114 +- Maple2.Model/Game/User/SkillBook.cs | 28 +- Maple2.Model/Game/User/SkillCooldown.cs | 52 +- Maple2.Model/Game/User/SkillPoint.cs | 122 +- Maple2.Model/Game/User/SkillTab.cs | 78 +- Maple2.Model/Game/User/StatAttributes.cs | 196 +- Maple2.Model/Game/User/WeddingHall.cs | 164 +- Maple2.Model/Game/WorldMap.cs | 54 +- Maple2.Model/Metadata/AchievementMetadata.cs | 44 +- .../Metadata/AdditionalEffectMetadata.cs | 284 +- Maple2.Model/Metadata/AiMetadata.cs | 924 +-- Maple2.Model/Metadata/AnimationMetadata.cs | 14 +- Maple2.Model/Metadata/BeginCondition.cs | 110 +- Maple2.Model/Metadata/ConditionMetadata.cs | 40 +- Maple2.Model/Metadata/Constants.cs | 1922 ++--- .../Metadata/FieldEntity/FieldEntity.cs | 208 +- Maple2.Model/Metadata/FunctionCubeMetadata.cs | 48 +- Maple2.Model/Metadata/ISearchResult.cs | 12 +- Maple2.Model/Metadata/ItemMetadata.cs | 266 +- Maple2.Model/Metadata/MapDataMetadata.cs | 10 +- Maple2.Model/Metadata/MapEntity/Breakable.cs | 44 +- .../Metadata/MapEntity/InteractObject.cs | 100 +- Maple2.Model/Metadata/MapEntity/Liftable.cs | 50 +- Maple2.Model/Metadata/MapEntity/MapEntity.cs | 118 +- .../Metadata/MapEntity/Ms2Bounding.cs | 16 +- .../Metadata/MapEntity/Ms2RegionSpawn.cs | 20 +- Maple2.Model/Metadata/MapEntity/Ms2Trigger.cs | 148 +- .../Metadata/MapEntity/ObjectWeapon.cs | 30 +- Maple2.Model/Metadata/MapEntity/PatrolData.cs | 46 +- Maple2.Model/Metadata/MapEntity/Portal.cs | 40 +- .../Metadata/MapEntity/RegionSkill.cs | 22 +- Maple2.Model/Metadata/MapEntity/SpawnPoint.cs | 106 +- .../Metadata/MapEntity/TaxiStation.cs | 16 +- .../Metadata/MapEntity/TriggerModel.cs | 20 +- Maple2.Model/Metadata/MapEntityMetadata.cs | 90 +- Maple2.Model/Metadata/MapMetadata.cs | 244 +- Maple2.Model/Metadata/NifMetadata.cs | 134 +- Maple2.Model/Metadata/NpcMetadata.cs | 224 +- Maple2.Model/Metadata/PetMetadata.cs | 48 +- Maple2.Model/Metadata/QuestMetadata.cs | 192 +- Maple2.Model/Metadata/RideMetadata.cs | 50 +- Maple2.Model/Metadata/ScriptMetadata.cs | 56 +- .../Metadata/ServerTable/BeautyShopTable.cs | 76 +- .../Metadata/ServerTable/BonusGameTable.cs | 58 +- .../Metadata/ServerTable/CombineSpawnTable.cs | 66 +- .../ServerTable/EnchantOptionTable.cs | 30 +- .../Metadata/ServerTable/FishTable.cs | 142 +- .../Metadata/ServerTable/GameEventTable.cs | 598 +- .../ServerTable/GlobalDropItemBoxTable.cs | 68 +- .../ServerTable/IndividualDropItemTable.cs | 90 +- .../ServerTable/InstanceFieldTable.cs | 36 +- .../Metadata/ServerTable/ItemMergeTable.cs | 48 +- .../Metadata/ServerTable/JobConditionTable.cs | 58 +- .../Metadata/ServerTable/MeretMarketTable.cs | 70 +- .../Metadata/ServerTable/OxQuizTable.cs | 24 +- .../Metadata/ServerTable/PrestigeExpTable.cs | 14 +- .../ServerTable/PrestigeIdExpTable.cs | 26 +- .../Metadata/ServerTable/RoomRandomTable.cs | 54 +- .../ServerTable/ScriptConditionTable.cs | 86 +- .../ServerTable/ScriptEventConditionTable.cs | 34 +- .../ServerTable/ScriptFunctionTable.cs | 64 +- .../Metadata/ServerTable/ShopItemTable.cs | 98 +- .../Metadata/ServerTable/ShopTable.cs | 64 +- .../Metadata/ServerTable/TimeEventTable.cs | 40 +- .../UnlimitedEnchantOptionTable.cs | 24 +- .../Metadata/ServerTable/UserStatTable.cs | 14 +- Maple2.Model/Metadata/ServerTableMetadata.cs | 100 +- Maple2.Model/Metadata/SkillEffectMetadata.cs | 70 +- Maple2.Model/Metadata/SkillMetadata.cs | 342 +- .../Metadata/Table/AutoActionTable.cs | 20 +- Maple2.Model/Metadata/Table/BannerTable.cs | 16 +- .../Metadata/Table/BlackMarketTable.cs | 6 +- Maple2.Model/Metadata/Table/ChangeJobTable.cs | 22 +- .../Metadata/Table/ChapterBookTable.cs | 30 +- .../Metadata/Table/ChatStickerTable.cs | 10 +- .../Metadata/Table/ColorPaletteTable.cs | 24 +- .../Metadata/Table/DefaultItemsTable.cs | 14 +- .../Metadata/Table/DungeomRoomTable.cs | 122 +- .../Metadata/Table/DungeonConfigTable.cs | 34 +- .../Metadata/Table/DungeonMissionTable.cs | 32 +- .../Metadata/Table/DungeonRankRewardTable.cs | 28 +- Maple2.Model/Metadata/Table/ExpTable.cs | 28 +- .../Metadata/Table/FieldMissionTable.cs | 22 +- .../Metadata/Table/FishingRodTable.cs | 18 +- .../Metadata/Table/FurnishingShopTable.cs | 22 +- Maple2.Model/Metadata/Table/GachaInfoTable.cs | 20 +- .../Metadata/Table/GemstoneUpgradeTable.cs | 16 +- Maple2.Model/Metadata/Table/GuildTable.cs | 106 +- .../Metadata/Table/IndividualItemDropTable.cs | 34 +- Maple2.Model/Metadata/Table/InsigniaTable.cs | 22 +- .../Metadata/Table/InstrumentTable.cs | 10 +- .../Metadata/Table/InteractObjectTable.cs | 74 +- Maple2.Model/Metadata/Table/ItemBreakTable.cs | 10 +- .../Metadata/Table/ItemExtractionTable.cs | 18 +- .../Metadata/Table/ItemOptionTable.cs | 132 +- .../Metadata/Table/ItemSocketTable.cs | 10 +- Maple2.Model/Metadata/Table/JobTable.cs | 60 +- .../Metadata/Table/LapenshardUpgradeTable.cs | 16 +- .../Metadata/Table/LearningQuestTable.cs | 24 +- Maple2.Model/Metadata/Table/MagicPathTable.cs | 72 +- .../Metadata/Table/MasteryRecipeTable.cs | 46 +- .../Metadata/Table/MasteryRewardTable.cs | 14 +- .../Metadata/Table/MasteryUgcHousingTable.cs | 16 +- .../Table/MeretMarketCategoryTable.cs | 18 +- .../Metadata/Table/PremiumClubTable.cs | 50 +- .../Table/PrestigeLevelAbilityTable.cs | 24 +- .../Table/PrestigeLevelRewardTable.cs | 24 +- .../Metadata/Table/PrestigeMissionTable.cs | 20 +- .../Metadata/Table/RewardContentTable.cs | 64 +- Maple2.Model/Metadata/Table/ScrollTable.cs | 118 +- .../Metadata/Table/SeasonDataTable.cs | 34 +- Maple2.Model/Metadata/Table/SetItemTable.cs | 46 +- .../Metadata/Table/ShopBeautyCouponTable.cs | 6 +- Maple2.Model/Metadata/Table/SmartPushTable.cs | 28 +- .../Metadata/Table/SurvivalSkinInfoTable.cs | 10 +- Maple2.Model/Metadata/Table/UgcDesignTable.cs | 26 +- .../Table/UgcHousingPointRewardTable.cs | 14 +- Maple2.Model/Metadata/Table/WeddingTable.cs | 64 +- Maple2.Model/Metadata/Table/WorldMapTable.cs | 22 +- Maple2.Model/Metadata/TableMetadata.cs | 174 +- Maple2.Model/Metadata/TriggerMetadata.cs | 6 +- Maple2.Model/ModelExtensions.cs | 548 +- .../Validators/CharacterNameValidator.cs | 176 +- Maple2.Server.Core/Constants/RecvOp.cs | 382 +- Maple2.Server.Core/Constants/SendOp.cs | 590 +- Maple2.Server.Core/Constants/Target.cs | 154 +- Maple2.Server.Core/Formulas/AttackStat.cs | 96 +- Maple2.Server.Core/Formulas/BaseStat.cs | 630 +- Maple2.Server.Core/Formulas/BonusAttack.cs | 96 +- Maple2.Server.Core/Formulas/Damage.cs | 66 +- Maple2.Server.Core/Formulas/Enchant.cs | 404 +- Maple2.Server.Core/Formulas/ItemMerge.cs | 28 +- .../Formulas/ItemSocketSlots.cs | 32 +- Maple2.Server.Core/Formulas/LimitBreak.cs | 90 +- Maple2.Server.Core/Formulas/Shop.cs | 84 +- Maple2.Server.Core/Helpers/DebugByteWriter.cs | 196 +- .../Helpers/ErrorParserHelper.cs | 244 +- Maple2.Server.Core/Modules/DataDbModule.cs | 116 +- Maple2.Server.Core/Modules/GameDbModule.cs | 96 +- .../Modules/GrpcClientModule.cs | 42 +- Maple2.Server.Core/Modules/WebDbModule.cs | 96 +- .../Modules/WorldClientModule.cs | 36 +- Maple2.Server.Core/Network/PacketRouter.cs | 76 +- .../Network/QueuedPipeScheduler.cs | 52 +- Maple2.Server.Core/Network/Server.cs | 180 +- Maple2.Server.Core/Network/Session.cs | 608 +- .../PacketHandlers/LogSendHandler.cs | 130 +- .../PacketHandlers/PacketHandler.cs | 48 +- .../PacketHandlers/ResponseVersionHandler.cs | 40 +- .../PacketHandlers/SystemInfoHandler.cs | 28 +- .../PacketHandlers/TimeSyncHandler.cs | 32 +- .../Packets/BannerListPacket.cs | 36 +- .../Packets/CharacterListPacket.cs | 384 +- Maple2.Server.Core/Packets/GameEventPacket.cs | 118 +- .../Packets/Helper/EquipPacketHelper.cs | 58 +- .../Packets/LoginResultPacket.cs | 78 +- Maple2.Server.Core/Packets/MigrationPacket.cs | 140 +- Maple2.Server.Core/Packets/NoticePacket.cs | 152 +- Maple2.Server.Core/Packets/Packet.cs | 48 +- Maple2.Server.Core/Packets/RequestPacket.cs | 56 +- .../Packets/ServerListPacket.cs | 84 +- Maple2.Server.Core/Packets/TimeSyncPacket.cs | 112 +- Maple2.Server.Core/Packets/UgcPacket.cs | 462 +- .../Sync/PlayerInfoUpdateEvent.cs | 248 +- .../Sync/PlayerInfoUpdateExtensions.cs | 404 +- .../Graphics/Assets/CoreModels.cs | 338 +- .../Graphics/Data/Ms2MeshData.cs | 370 +- .../Graphics/DebugFieldRenderer.cs | 130 +- .../Graphics/DebugFieldWindow.cs | 424 +- .../Graphics/DebugGraphicsContext.cs | 946 +-- .../Graphics/ImGuiController.cs | 880 +-- .../Graphics/Resources/Mesh.cs | 236 +- .../Graphics/Resources/Shader.cs | 466 +- .../Graphics/Resources/Texture.cs | 248 +- .../Graphics/Scene/Camera.cs | 46 +- .../Graphics/Ui/FieldPropertiesWindow.cs | 210 +- .../Graphics/Ui/UiUtils.cs | 64 +- .../Graphics/Ui/Windows/FieldListWindow.cs | 240 +- .../Graphics/Ui/Windows/IUiWindow.cs | 28 +- .../Graphics/Ui/Windows/WindowListWindow.cs | 256 +- Maple2.Server.DebugGame/Program.cs | 302 +- .../Commands/AdminPermissionCommand.cs | 372 +- Maple2.Server.Game/Commands/AlertCommand.cs | 110 +- Maple2.Server.Game/Commands/BuffCommand.cs | 174 +- Maple2.Server.Game/Commands/CommandRouter.cs | 226 +- Maple2.Server.Game/Commands/CoordCommand.cs | 150 +- .../Commands/DailyResetCommand.cs | 38 +- Maple2.Server.Game/Commands/DebugCommand.cs | 868 +-- Maple2.Server.Game/Commands/FieldCommand.cs | 218 +- Maple2.Server.Game/Commands/FindCommand.cs | 142 +- Maple2.Server.Game/Commands/FreeCamCommand.cs | 60 +- Maple2.Server.Game/Commands/GameConsole.cs | 154 +- Maple2.Server.Game/Commands/HomeCommand.cs | 154 +- .../Commands/HomeCommands/AlarmCommand.cs | 86 +- .../Commands/HomeCommands/BallCommand.cs | 120 +- .../Commands/HomeCommands/GravityCommand.cs | 100 +- .../HomeCommands/RandomNumberCommand.cs | 82 +- .../Commands/HomeCommands/SurveyCommand.cs | 222 +- Maple2.Server.Game/Commands/ItemCommand.cs | 194 +- Maple2.Server.Game/Commands/KillCommand.cs | 338 +- Maple2.Server.Game/Commands/NpcCommand.cs | 228 +- Maple2.Server.Game/Commands/PetCommand.cs | 114 +- Maple2.Server.Game/Commands/PlayerCommand.cs | 1202 +-- Maple2.Server.Game/Commands/QuestCommand.cs | 130 +- .../Commands/StringBoardCommand.cs | 234 +- Maple2.Server.Game/Commands/TriggerCommand.cs | 370 +- .../Commands/TutorialCommand.cs | 162 +- Maple2.Server.Game/Commands/WarpCommand.cs | 410 +- .../DebugGraphics/HeadlessGraphicsContext.cs | 32 +- .../DebugGraphics/IFieldRenderer.cs | 12 +- .../DebugGraphics/IGraphicsContext.cs | 20 +- Maple2.Server.Game/GameServer.cs | 404 +- Maple2.Server.Game/LuaFunctions/Lua.cs | 6344 +++++++-------- .../Manager/AchievementManager.cs | 576 +- .../Manager/AnimationManager.cs | 800 +- Maple2.Server.Game/Manager/BeautyManager.cs | 248 +- .../Manager/BlackMarketManager.cs | 726 +- Maple2.Server.Game/Manager/BuddyManager.cs | 1060 +-- Maple2.Server.Game/Manager/BuffManager.cs | 1242 +-- Maple2.Server.Game/Manager/ClubManager.cs | 428 +- Maple2.Server.Game/Manager/Config/HotBar.cs | 152 +- .../Manager/Config/SkillInfo.cs | 526 +- Maple2.Server.Game/Manager/ConfigManager.cs | 1182 +-- Maple2.Server.Game/Manager/CurrencyManager.cs | 352 +- Maple2.Server.Game/Manager/DungeonManager.cs | 1232 +-- .../Manager/ExperienceManager.cs | 542 +- .../Manager/Field/AgentNavigation.cs | 688 +- Maple2.Server.Game/Manager/Field/AiManager.cs | 24 +- .../Field/FieldManager/DungeonFieldManager.cs | 142 +- .../FieldManager/FieldManager.Factory.cs | 820 +- .../Field/FieldManager/FieldManager.State.cs | 1800 ++--- .../FieldManager/FieldManager.Trigger.cs | 90 +- .../Field/FieldManager/FieldManager.Ugc.cs | 354 +- .../Field/FieldManager/FieldManager.cs | 1432 ++-- .../Field/FieldManager/HomeFieldManager.cs | 92 +- .../Manager/Field/FieldManager/IField.cs | 192 +- .../Manager/Field/Navigation.cs | 150 +- .../Manager/Field/PerformanceStageManager.cs | 68 +- .../Manager/Field/TriggerCollection.cs | 198 +- Maple2.Server.Game/Manager/FishingManager.cs | 762 +- .../Manager/GameEventManager.cs | 344 +- .../Manager/GroupChatManager.cs | 348 +- Maple2.Server.Game/Manager/GuildManager.cs | 772 +- Maple2.Server.Game/Manager/HousingManager.cs | 1484 ++-- Maple2.Server.Game/Manager/ItemBoxManager.cs | 692 +- .../Manager/ItemEnchantManager.cs | 1066 +-- .../Manager/ItemMergeManager.cs | 356 +- .../Manager/Items/EquipManager.cs | 712 +- .../Manager/Items/FurnishingManager.cs | 644 +- .../Manager/Items/InventoryManager.cs | 1508 ++-- .../Manager/Items/ItemCollection.cs | 670 +- .../Manager/Items/ItemDropManager.cs | 680 +- .../Manager/Items/ItemManager.cs | 186 +- .../Manager/Items/StorageManager.cs | 514 +- Maple2.Server.Game/Manager/MailManager.cs | 438 +- Maple2.Server.Game/Manager/MarriageManager.cs | 1298 ++-- Maple2.Server.Game/Manager/MasteryManager.cs | 406 +- .../Manager/MentoringManager.cs | 98 +- .../Manager/NpcScriptManager.cs | 964 +-- Maple2.Server.Game/Manager/PartyManager.cs | 766 +- Maple2.Server.Game/Manager/PetManager.cs | 414 +- Maple2.Server.Game/Manager/QuestManager.cs | 1316 ++-- Maple2.Server.Game/Manager/RideManager.cs | 246 +- Maple2.Server.Game/Manager/ShopManager.cs | 1330 ++-- Maple2.Server.Game/Manager/SkillManager.cs | 350 +- Maple2.Server.Game/Manager/StatsManager.cs | 458 +- Maple2.Server.Game/Manager/SurvivalManager.cs | 354 +- Maple2.Server.Game/Manager/TradeManager.cs | 656 +- .../Manager/UgcMarketManager.cs | 312 +- .../Model/Enum/AnimationType.cs | 14 +- .../Model/Enum/NpcTaskPriority.cs | 32 +- .../Model/Enum/NpcTaskStatus.cs | 16 +- Maple2.Server.Game/Model/Field/Actor/Actor.cs | 706 +- .../Actor/ActorStateComponent/AiState.cs | 1442 ++-- .../ActorStateComponent/AnimationRecord.cs | 122 +- .../Actor/ActorStateComponent/BattleState.cs | 570 +- .../ActorStateComponent/MovementState.cs | 648 +- .../MovementState.Debug.cs | 156 +- .../MovementState.Emote.cs | 66 +- .../MovementState.SkillCast.cs | 284 +- .../MovementStateStates/MovementState.Walk.cs | 402 +- .../MovementState.CleanupTask.cs | 104 +- .../MovementState.EmoteTask.cs | 116 +- .../MovementState.SkillCastTask.cs | 248 +- .../MovementState.StandbyTask.cs | 70 +- .../MovementState.TalkTask.cs | 48 +- .../MovementState.WalkTask.cs | 468 +- .../Actor/ActorStateComponent/SkillState.cs | 136 +- .../Actor/ActorStateComponent/TaskState.cs | 308 +- .../Model/Field/Actor/FieldActor.cs | 90 +- .../Model/Field/Actor/FieldNpc.cs | 988 +-- .../Model/Field/Actor/FieldPet.cs | 230 +- .../Model/Field/Actor/FieldPlayer.cs | 1376 ++-- .../Model/Field/Actor/IActor.cs | 76 +- .../Field/Actor/Routine/AnimateRoutine.cs | 60 +- .../Model/Field/Actor/Routine/JumpRoutine.cs | 146 +- .../Model/Field/Actor/Routine/MoveRoutine.cs | 164 +- .../Model/Field/Actor/Routine/NpcRoutine.cs | 78 +- .../Model/Field/Actor/Routine/WaitRoutine.cs | 38 +- .../Model/Field/Actor/State/NpcState.cs | 24 +- .../Model/Field/Actor/State/StateHitNpc.cs | 56 +- .../Model/Field/Actor/State/StateJumpNpc.cs | 114 +- .../Model/Field/Actor/State/StateSpawn.cs | 38 +- Maple2.Server.Game/Model/Field/Buff.cs | 672 +- .../Model/Field/Entity/FieldBreakable.cs | 148 +- .../Model/Field/Entity/FieldEntity.cs | 52 +- .../Field/Entity/FieldFunctionInteract.cs | 100 +- .../Model/Field/Entity/FieldGuideObject.cs | 42 +- .../Model/Field/Entity/FieldInstrument.cs | 26 +- .../Model/Field/Entity/FieldInteract.cs | 166 +- .../Model/Field/Entity/FieldItem.cs | 76 +- .../Model/Field/Entity/FieldLiftable.cs | 148 +- .../Model/Field/Entity/FieldMobSpawn.cs | 370 +- .../Model/Field/Entity/FieldNpcSpawnPoint.cs | 120 +- .../Field/Entity/FieldPlayerSpawnPoint.cs | 28 +- .../Model/Field/Entity/FieldPortal.cs | 58 +- .../Model/Field/Entity/FieldQuestPortal.cs | 24 +- .../Model/Field/Entity/FieldSkill.cs | 430 +- .../Model/Field/Entity/FieldSpawnGroup.cs | 270 +- .../Model/Field/Entity/FieldTrigger.cs | 204 +- .../Model/Field/Entity/IFieldEntity.cs | 28 +- Maple2.Server.Game/Model/Field/FieldObject.cs | 42 +- .../Model/Field/FieldUgcBanner.cs | 80 +- Maple2.Server.Game/Model/Field/FishingTile.cs | 66 +- Maple2.Server.Game/Model/Field/HongBao.cs | 176 +- .../Model/Field/IFieldObject.cs | 46 +- Maple2.Server.Game/Model/Field/IUpdatable.cs | 10 +- Maple2.Server.Game/Model/Field/RoomTimer.cs | 104 +- Maple2.Server.Game/Model/Field/TickTimer.cs | 50 +- Maple2.Server.Game/Model/Field/Tombstone.cs | 78 +- .../Model/Field/Widget/GuideWidget.cs | 36 +- .../Model/Field/Widget/IWidget.cs | 36 +- .../Model/Field/Widget/OxQuizWidget.cs | 218 +- .../Model/Field/Widget/SceneMovieWidget.cs | 54 +- .../Field/Widget/SurvivalContentsWidget.cs | 112 +- .../Model/Field/Widget/Widget.cs | 38 +- .../Model/Skill/DamagePropertyRecord.cs | 52 +- .../Model/Skill/DamageRecord.cs | 122 +- .../Model/Skill/DotDamageRecord.cs | 106 +- .../Model/Skill/HealDamageRecord.cs | 70 +- .../Model/Skill/InvokeRecord.cs | 32 +- .../Model/Skill/ReflectRecord.cs | 30 +- Maple2.Server.Game/Model/Skill/SkillQueue.cs | 110 +- Maple2.Server.Game/Model/Skill/SkillRecord.cs | 140 +- .../Model/Skill/TargetRecord.cs | 40 +- Maple2.Server.Game/Model/Stats.cs | 450 +- .../PacketHandlers/AchievementHandler.cs | 90 +- .../PacketHandlers/AttendanceHandler.cs | 344 +- .../PacketHandlers/AttributePointHandler.cs | 74 +- .../PacketHandlers/BadgeEquipHandler.cs | 140 +- .../PacketHandlers/BeautyHandler.cs | 1188 +-- .../PacketHandlers/BlackMarketHandler.cs | 322 +- .../PacketHandlers/BonusGameHandler.cs | 210 +- .../PacketHandlers/BreakableHandler.cs | 120 +- .../PacketHandlers/BuddyBadgeHandler.cs | 146 +- .../PacketHandlers/BuddyEmoteHandler.cs | 250 +- .../PacketHandlers/BuddyHandler.cs | 216 +- .../PacketHandlers/ChangeAttributesHandler.cs | 552 +- .../ChangeAttributesScrollHandler.cs | 506 +- .../PacketHandlers/ChannelHandler.cs | 104 +- .../PacketHandlers/CharacterInfoHandler.cs | 46 +- .../PacketHandlers/ChatStickerHandler.cs | 192 +- .../CheckCharacterNameHandler.cs | 78 +- .../PacketHandlers/ClubHandler.cs | 474 +- .../PacketHandlers/DungeonRoomHandler.cs | 132 +- .../PacketHandlers/EmoteHandler.cs | 140 +- .../PacketHandlers/EnchantScrollHandler.cs | 352 +- .../PacketHandlers/EnterEventFieldHandler.cs | 90 +- .../PacketHandlers/EventRewardHandler.cs | 550 +- .../PacketHandlers/FallDamageHandler.cs | 46 +- .../Field/FieldPacketHandler.cs | 58 +- .../PacketHandlers/FieldEnterHandler.cs | 34 +- .../PacketHandlers/FileHandler.cs | 36 +- .../PacketHandlers/FishingHandler.cs | 188 +- .../PacketHandlers/FunctionCubeHandler.cs | 502 +- .../FurnishingStorageHandler.cs | 56 +- .../PacketHandlers/GlobalPortalHandler.cs | 160 +- .../PacketHandlers/GroupChatHandler.cs | 298 +- .../PacketHandlers/GuideObjectSyncHandler.cs | 110 +- .../PacketHandlers/GuideRecordHandler.cs | 42 +- .../PacketHandlers/GuildHandler.cs | 1358 ++-- .../PacketHandlers/HomeActionHandler.cs | 432 +- .../PacketHandlers/HomeBankHandler.cs | 84 +- .../PacketHandlers/HomeDoctorHandler.cs | 90 +- .../PacketHandlers/HomeHandler.cs | 238 +- .../PacketHandlers/InsigniaHandler.cs | 140 +- .../PacketHandlers/InstrumentHandler.cs | 684 +- .../PacketHandlers/InteractObjectHandler.cs | 248 +- .../PacketHandlers/ItemBoxHandler.cs | 66 +- .../PacketHandlers/ItemDismantleHandler.cs | 482 +- .../PacketHandlers/ItemEnchantHandler.cs | 206 +- .../PacketHandlers/ItemEquipHandler.cs | 124 +- .../ItemExchangeScrollHandler.cs | 186 +- .../PacketHandlers/ItemExtractionHandler.cs | 132 +- .../PacketHandlers/ItemInventoryHandler.cs | 240 +- .../PacketHandlers/ItemLockHandler.cs | 204 +- .../PacketHandlers/ItemMergeHandler.cs | 104 +- .../PacketHandlers/ItemPickupHandler.cs | 140 +- .../PacketHandlers/ItemRepackHandler.cs | 180 +- .../PacketHandlers/ItemSocketHandler.cs | 830 +- .../PacketHandlers/ItemSocketScrollHandler.cs | 208 +- .../PacketHandlers/ItemUseHandler.cs | 1356 ++-- .../PacketHandlers/JobHandler.cs | 290 +- .../PacketHandlers/KeyTableHandler.cs | 192 +- .../PacketHandlers/LapenshardHandler.cs | 412 +- .../PacketHandlers/LiftableHandler.cs | 86 +- .../PacketHandlers/LimitBreakHandler.cs | 76 +- .../PacketHandlers/LoadUgcMapHandler.cs | 178 +- .../PacketHandlers/LogSendHandler.cs | 10 +- .../PacketHandlers/MailHandler.cs | 324 +- .../PacketHandlers/MapleopolyHandler.cs | 348 +- .../PacketHandlers/MasteryHandler.cs | 250 +- .../PacketHandlers/MentorHandler.cs | 136 +- .../PacketHandlers/MeretMarketHandler.cs | 1302 ++-- .../PacketHandlers/MesoMarketHandler.cs | 496 +- .../PacketHandlers/MesoPickupHandler.cs | 70 +- .../PacketHandlers/MoveFieldHandler.cs | 260 +- .../PacketHandlers/MyInfoHandler.cs | 90 +- .../PacketHandlers/NewsNotificationHandler.cs | 82 +- .../PacketHandlers/NpcTalkHandler.cs | 604 +- .../PacketHandlers/PartyHandler.cs | 724 +- .../PacketHandlers/PartySearchHandler.cs | 368 +- .../PacketHandlers/PetHandler.cs | 358 +- .../PacketHandlers/PetInventoryHandler.cs | 170 +- .../PacketHandlers/PlayerHostHandler.cs | 92 +- .../PacketHandlers/PremiumClubHandler.cs | 238 +- .../PacketHandlers/PrestigeHandler.cs | 184 +- .../PacketHandlers/QuestHandler.cs | 530 +- .../PacketHandlers/QuitHandler.cs | 108 +- .../PacketHandlers/RequestCubeHandler.cs | 1562 ++-- .../PacketHandlers/RequestReportHandler.cs | 136 +- .../ResolveDeathPenaltyHandler.cs | 70 +- .../ResponseHeartbeatHandler.cs | 80 +- .../PacketHandlers/ResponseKeyHandler.cs | 110 +- .../PacketHandlers/ResponseVersionHandler.cs | 28 +- .../PacketHandlers/RevivalHandler.cs | 164 +- .../PacketHandlers/RideHandler.cs | 340 +- .../PacketHandlers/RideSyncHandler.cs | 92 +- .../PacketHandlers/SetCraftModeHandler.cs | 124 +- .../PacketHandlers/ShopHandler.cs | 154 +- .../PacketHandlers/SkillBookHandler.cs | 182 +- .../PacketHandlers/SkillHandler.cs | 764 +- .../PacketHandlers/SkillMacroHandler.cs | 100 +- .../PacketHandlers/SmartPushHandler.cs | 272 +- .../PacketHandlers/StateHandler.cs | 56 +- .../PacketHandlers/StateSkillHandler.cs | 96 +- .../PacketHandlers/StorageInventoryHandler.cs | 250 +- .../PacketHandlers/SuperChatHandler.cs | 100 +- .../PacketHandlers/SurvivalHandler.cs | 64 +- .../PacketHandlers/SystemInfoHandler.cs | 10 +- .../PacketHandlers/SystemShopHandler.cs | 236 +- .../PacketHandlers/TakeBoatHandler.cs | 32 +- .../PacketHandlers/TaxiHandler.cs | 364 +- .../PacketHandlers/TimeSyncHandler.cs | 10 +- .../PacketHandlers/TombstoneHandler.cs | 50 +- .../PacketHandlers/TradeHandler.cs | 306 +- .../PacketHandlers/TriggerHandler.cs | 218 +- .../PacketHandlers/TutorialItemHandler.cs | 106 +- .../PacketHandlers/UgcHandler.cs | 1078 +-- .../PacketHandlers/UserChatHandler.cs | 686 +- .../PacketHandlers/UserEnvHandler.cs | 86 +- .../PacketHandlers/UserSyncHandler.cs | 108 +- .../PacketHandlers/VibrateHandler.cs | 118 +- .../PacketHandlers/WardrobeHandler.cs | 248 +- .../PacketHandlers/WeddingBillboardHandler.cs | 72 +- .../PacketHandlers/WeddingHandler.cs | 470 +- .../PacketHandlers/WorldMapHandler.cs | 88 +- .../Packets/AchievementPacket.cs | 110 +- .../Packets/AttendancePacket.cs | 102 +- .../Packets/AttributePointPacket.cs | 64 +- Maple2.Server.Game/Packets/BeautyPacket.cs | 348 +- .../Packets/BlackMarketPacket.cs | 196 +- Maple2.Server.Game/Packets/BonusGamePacket.cs | 84 +- Maple2.Server.Game/Packets/BreakablePacket.cs | 106 +- .../Packets/BuddyBadgePacket.cs | 56 +- .../Packets/BuddyEmotePacket.cs | 174 +- Maple2.Server.Game/Packets/BuddyPacket.cs | 426 +- Maple2.Server.Game/Packets/BuffPacket.cs | 108 +- Maple2.Server.Game/Packets/CameraPacket.cs | 46 +- .../Packets/ChangeAttributesPacket.cs | 86 +- .../Packets/ChangeAttributesScrollPacket.cs | 104 +- Maple2.Server.Game/Packets/ChannelPacket.cs | 78 +- Maple2.Server.Game/Packets/ChatPacket.cs | 296 +- .../Packets/ChatStickerPacket.cs | 176 +- .../Packets/CheckCharacterNamePacket.cs | 32 +- Maple2.Server.Game/Packets/CinematicPacket.cs | 390 +- Maple2.Server.Game/Packets/ClubPacket.cs | 506 +- Maple2.Server.Game/Packets/CubePacket.cs | 1086 +-- Maple2.Server.Game/Packets/CurrencyPacket.cs | 102 +- Maple2.Server.Game/Packets/DeadUserPacket.cs | 30 +- .../Packets/DungeonMissionPacket.cs | 90 +- .../Packets/DungeonRewardPacket.cs | 64 +- .../Packets/DungeonRoomPacket.cs | 170 +- .../Packets/DungeonWaitingPacket.cs | 30 +- Maple2.Server.Game/Packets/EmotePacket.cs | 84 +- .../Packets/EnchantScrollPacket.cs | 226 +- .../Packets/EnterUgcMapPacket.cs | 102 +- Maple2.Server.Game/Packets/EquipPacket.cs | 120 +- .../Packets/EventRewardPacket.cs | 208 +- .../Packets/ExperienceUpPacket.cs | 68 +- .../Packets/FallDamagePacket.cs | 30 +- .../Packets/FieldEnterPacket.cs | 70 +- .../Packets/FieldEntrancePacket.cs | 70 +- Maple2.Server.Game/Packets/FieldPacket.cs | 706 +- .../Packets/FieldPropertyPacket.cs | 122 +- Maple2.Server.Game/Packets/FishingPacket.cs | 312 +- Maple2.Server.Game/Packets/FollowNpcPacket.cs | 26 +- .../Packets/FunctionCubePacket.cs | 174 +- .../Packets/FurnishingInventoryPacket.cs | 112 +- .../Packets/FurnishingStoragePacket.cs | 158 +- .../Packets/GameEventUserValuePacket.cs | 70 +- .../Packets/GlobalPortalPacket.cs | 70 +- Maple2.Server.Game/Packets/GroupChatPacket.cs | 268 +- .../Packets/GuideObjectPacket.cs | 86 +- .../Packets/GuideRecordPacket.cs | 40 +- Maple2.Server.Game/Packets/GuildPacket.cs | 1548 ++-- .../Packets/HomeActionPacket.cs | 454 +- .../Packets/HomeCommandPacket.cs | 64 +- .../Packets/HomeInvitePacket.cs | 42 +- .../Packets/InGameRankPacket.cs | 34 +- Maple2.Server.Game/Packets/InsigniaPacket.cs | 34 +- .../Packets/InstrumentPacket.cs | 274 +- .../Packets/InteractObjectPacket.cs | 258 +- Maple2.Server.Game/Packets/ItemBoxPacket.cs | 36 +- .../Packets/ItemDismantlePacket.cs | 116 +- .../Packets/ItemDropNoticePacket.cs | 36 +- .../Packets/ItemEnchantPacket.cs | 322 +- .../Packets/ItemExchangeScrollPacket.cs | 74 +- .../Packets/ItemExtractionPacket.cs | 88 +- .../Packets/ItemInventoryPacket.cs | 300 +- Maple2.Server.Game/Packets/ItemLockPacket.cs | 114 +- Maple2.Server.Game/Packets/ItemMergePacket.cs | 194 +- .../Packets/ItemPickupPacket.cs | 74 +- .../Packets/ItemRepackPacket.cs | 86 +- .../Packets/ItemScriptPacket.cs | 100 +- .../Packets/ItemSocketPacket.cs | 352 +- .../Packets/ItemSocketScrollPacket.cs | 96 +- .../Packets/ItemUpdatePacket.cs | 36 +- Maple2.Server.Game/Packets/ItemUsePacket.cs | 138 +- Maple2.Server.Game/Packets/JobPacket.cs | 214 +- Maple2.Server.Game/Packets/KeyTablePacket.cs | 138 +- .../Packets/LapenshardPacket.cs | 128 +- Maple2.Server.Game/Packets/LevelUpPacket.cs | 34 +- Maple2.Server.Game/Packets/LiftablePacket.cs | 136 +- .../Packets/LimitBreakPacket.cs | 98 +- Maple2.Server.Game/Packets/LoadCubesPacket.cs | 158 +- .../Packets/LoadUgcMapPacket.cs | 56 +- Maple2.Server.Game/Packets/MailPacket.cs | 334 +- .../Packets/MapleopolyPacket.cs | 142 +- .../Packets/MassiveEventPacket.cs | 220 +- Maple2.Server.Game/Packets/MasteryPacket.cs | 122 +- Maple2.Server.Game/Packets/MentorPacket.cs | 414 +- .../Packets/MeretMarketPacket.cs | 572 +- .../Packets/MesoMarketPacket.cs | 220 +- .../Packets/MessengerBrowserPacket.cs | 46 +- Maple2.Server.Game/Packets/MyInfoPacket.cs | 78 +- .../Packets/NewsNotificationPacket.cs | 62 +- .../Packets/NpcControlPacket.cs | 200 +- Maple2.Server.Game/Packets/NpcNoticePacket.cs | 112 +- Maple2.Server.Game/Packets/NpcTalkPacket.cs | 258 +- Maple2.Server.Game/Packets/PartyPacket.cs | 682 +- .../Packets/PartySearchPacket.cs | 108 +- .../Packets/PetInventoryPacket.cs | 162 +- Maple2.Server.Game/Packets/PetPacket.cs | 490 +- .../Packets/PlayerHostPacket.cs | 164 +- .../Packets/PlayerInfoPacket.cs | 264 +- .../Packets/PlayerKillNoticePacket.cs | 32 +- Maple2.Server.Game/Packets/PortalPacket.cs | 192 +- .../Packets/PremiumCubPacket.cs | 118 +- Maple2.Server.Game/Packets/PrestigePacket.cs | 166 +- .../Packets/ProxyObjectPacket.cs | 302 +- Maple2.Server.Game/Packets/QuestPacket.cs | 490 +- Maple2.Server.Game/Packets/QuizEventPacket.cs | 66 +- .../Packets/RegionSkillPacket.cs | 82 +- Maple2.Server.Game/Packets/RevivalPacket.cs | 80 +- Maple2.Server.Game/Packets/RidePacket.cs | 146 +- .../Packets/RoomStageDungeonPacket.cs | 60 +- Maple2.Server.Game/Packets/RoomTimerPacket.cs | 72 +- .../Packets/ServerEnterPacket.cs | 114 +- .../Packets/SetCraftModePacket.cs | 86 +- Maple2.Server.Game/Packets/ShopPacket.cs | 226 +- Maple2.Server.Game/Packets/SkillBookPacket.cs | 118 +- .../Packets/SkillDamagePacket.cs | 302 +- .../Packets/SkillMacroPacket.cs | 76 +- Maple2.Server.Game/Packets/SkillPacket.cs | 226 +- .../Packets/SkillPointPacket.cs | 32 +- .../Packets/SkillUseFailedPacket.cs | 52 +- Maple2.Server.Game/Packets/SmartPushPacket.cs | 60 +- Maple2.Server.Game/Packets/SoundPacket.cs | 44 +- Maple2.Server.Game/Packets/StateSyncPacket.cs | 68 +- Maple2.Server.Game/Packets/StatsPacket.cs | 202 +- .../Packets/StorageInventoryPacket.cs | 298 +- Maple2.Server.Game/Packets/StoryBookPacket.cs | 30 +- Maple2.Server.Game/Packets/SuperChatPacket.cs | 58 +- .../Packets/SurvivalEventPacket.cs | 34 +- Maple2.Server.Game/Packets/SurvivalPacket.cs | 116 +- .../Packets/SystemShopPacket.cs | 110 +- Maple2.Server.Game/Packets/TaxiPacket.cs | 36 +- Maple2.Server.Game/Packets/TradePacket.cs | 284 +- Maple2.Server.Game/Packets/TriggerPacket.cs | 800 +- Maple2.Server.Game/Packets/UserEnvPacket.cs | 210 +- .../Packets/UserSkinColorPacket.cs | 36 +- Maple2.Server.Game/Packets/VibratePacket.cs | 110 +- Maple2.Server.Game/Packets/WardrobePacket.cs | 44 +- .../Packets/WeddingBillboardPacket.cs | 50 +- Maple2.Server.Game/Packets/WeddingPacket.cs | 310 +- Maple2.Server.Game/Packets/WorldMapPacket.cs | 120 +- Maple2.Server.Game/Program.cs | 344 +- .../Service/ChannelService.Admin.cs | 144 +- .../Service/ChannelService.BlackMarket.cs | 50 +- .../Service/ChannelService.Buddy.cs | 70 +- .../Service/ChannelService.Chat.cs | 248 +- .../Service/ChannelService.Club.cs | 472 +- .../Service/ChannelService.Field.cs | 94 +- .../Service/ChannelService.GameEvent.cs | 80 +- .../Service/ChannelService.GameReset.cs | 40 +- .../Service/ChannelService.GroupChat.cs | 254 +- .../Service/ChannelService.Guild.cs | 398 +- .../Service/ChannelService.Heartbeat.cs | 56 +- .../Service/ChannelService.Marriage.cs | 46 +- .../Service/ChannelService.Party.cs | 464 +- .../Service/ChannelService.PartySearch.cs | 144 +- .../Service/ChannelService.PlayerWarp.cs | 74 +- .../Service/ChannelService.Sync.cs | 96 +- .../Service/ChannelService.TimeEvent.cs | 124 +- Maple2.Server.Game/Service/ChannelService.cs | 46 +- .../Session/GameSession.State.cs | 112 +- Maple2.Server.Game/Session/GameSession.cs | 1694 ++-- .../Trigger/Helpers/ITriggerContext.cs | 518 +- .../Trigger/Helpers/Trigger.Actions.cs | 1634 ++-- .../Trigger/Helpers/Trigger.Conditions.cs | 744 +- Maple2.Server.Game/Trigger/Helpers/Trigger.cs | 116 +- .../Trigger/Helpers/TriggerEnums.cs | 42 +- .../Trigger/Helpers/TriggerFunctionMapping.cs | 746 +- .../Trigger/Helpers/TriggerState.cs | 188 +- .../Trigger/TriggerContext.Arcade.cs | 134 +- .../Trigger/TriggerContext.Cinematic.cs | 256 +- .../Trigger/TriggerContext.Dungeon.cs | 346 +- .../Trigger/TriggerContext.Field.cs | 1106 +-- .../Trigger/TriggerContext.Guild.cs | 72 +- .../Trigger/TriggerContext.Interface.cs | 424 +- .../Trigger/TriggerContext.MiniGame.cs | 368 +- .../Trigger/TriggerContext.Npc.cs | 620 +- .../Trigger/TriggerContext.Player.cs | 614 +- .../Trigger/TriggerContext.Wedding.cs | 74 +- Maple2.Server.Game/Trigger/TriggerContext.cs | 554 +- Maple2.Server.Game/Util/ChatUtil.cs | 62 +- Maple2.Server.Game/Util/ConditionUtil.cs | 668 +- Maple2.Server.Game/Util/DamageCalculator.cs | 296 +- .../Util/ItemStatsCalculator.cs | 1314 ++-- Maple2.Server.Game/Util/NpcTalkUtil.cs | 514 +- .../Util/PacketStructureResolver.cs | 316 +- Maple2.Server.Game/Util/SkillUtils.cs | 522 +- .../Util/Sync/PlayerInfoListener.cs | 30 +- .../Util/Sync/PlayerInfoStorage.cs | 252 +- Maple2.Server.Game/Util/TriggerStorage.cs | 322 +- .../Util/WorldMapGraphStorage.cs | 212 +- Maple2.Server.Game/Util/XmlParseUtil.cs | 58 +- Maple2.Server.Login/LoginServer.cs | 166 +- .../CharacterManagementHandler.cs | 600 +- .../PacketHandlers/LogSendHandler.cs | 16 +- .../PacketHandlers/LoginHandler.cs | 202 +- .../PacketHandlers/QuitHandler.cs | 28 +- .../ResponseHeartbeatHandler.cs | 64 +- .../PacketHandlers/ResponseKeyHandler.cs | 90 +- .../PacketHandlers/ResponseVersionHandler.cs | 32 +- .../PacketHandlers/ServerEnterHandler.cs | 46 +- .../PacketHandlers/SystemInfoHandler.cs | 16 +- .../PacketHandlers/TimeSyncHandler.cs | 16 +- .../PacketHandlers/UgcHandler.cs | 94 +- Maple2.Server.Login/Program.cs | 194 +- .../Service/LoginService.Heartbeat.cs | 30 +- Maple2.Server.Login/Service/LoginService.cs | 26 +- Maple2.Server.Login/Session/LoginSession.cs | 350 +- .../Game/Manager/Item/ItemCollectionTest.cs | 738 +- .../Game/Util/WorldMapGraphTest.cs | 640 +- Maple2.Server.Tests/Lua/LuaTests.cs | 328 +- .../Tools/Collision/CircleTests.cs | 158 +- .../Tools/Collision/HoleCircleTest.cs | 196 +- .../Tools/Collision/RectangleTests.cs | 102 +- .../Tools/Collision/TrapezoidTests.cs | 110 +- Maple2.Server.Tests/Tools/EventQueueTests.cs | 138 +- .../Tools/LimitedStackTests.cs | 126 +- Maple2.Server.Tests/Tools/ParseXmlTests.cs | 158 +- Maple2.Server.Tests/Tools/WeightedSetTests.cs | 118 +- Maple2.Server.Tests/Usings.cs | 2 +- .../Validators/CharacterNameValidatorTests.cs | 204 +- Maple2.Server.Tests/Vector/Vector3BTests.cs | 58 +- .../Controllers/HealthCheckController.cs | 24 +- .../Controllers/SystemController.cs | 42 +- .../Controllers/Ugc/BannerController.cs | 42 +- .../Controllers/Ugc/BlueprintController.cs | 42 +- .../Controllers/Ugc/GuildController.cs | 64 +- .../Controllers/Ugc/ItemController.cs | 42 +- .../Controllers/Ugc/ItemIconController.cs | 42 +- .../Controllers/Ugc/ProfileController.cs | 42 +- .../Controllers/WebController.cs | 660 +- Maple2.Server.Web/Packet/InGameRankPacket.cs | 576 +- Maple2.Server.Web/Packet/MentorPacket.cs | 56 +- Maple2.Server.Web/Program.cs | 148 +- .../Containers/BlackMarketLookup.cs | 366 +- .../Containers/ChannelClientLookup.cs | 570 +- Maple2.Server.World/Containers/ClubLookup.cs | 238 +- Maple2.Server.World/Containers/ClubManager.cs | 556 +- .../Containers/GlobalPortalLookup.cs | 94 +- .../Containers/GlobalPortalManager.cs | 226 +- .../Containers/GroupChatLookup.cs | 150 +- .../Containers/GroupChatManager.cs | 208 +- Maple2.Server.World/Containers/GuildLookup.cs | 226 +- .../Containers/GuildManager.cs | 756 +- Maple2.Server.World/Containers/PartyLookup.cs | 168 +- .../Containers/PartyManager.cs | 866 +-- .../Containers/PartySearchLookup.cs | 220 +- .../Containers/PartySearchManager.cs | 148 +- .../Containers/PlayerConfigLookUp.cs | 296 +- .../Containers/PlayerInfoLookup.cs | 316 +- .../20221027102305_InitialCreate.cs | 1016 +-- .../Migrations/20221028040609_PetConfig.cs | 68 +- .../20221029043947_PetCollection.cs | 46 +- .../Migrations/20221030212055_MesoMarket.cs | 226 +- .../Migrations/20221104071153_Mail.cs | 132 +- .../Migrations/20221106164324_ChatSticker.cs | 72 +- .../Migrations/20221107075015_Mastery.cs | 46 +- .../Migrations/20221108171412_ItemJson.cs | 70 +- .../20221114042939_MasteryRewards.cs | 46 +- .../20221114194917_RenameStatOption.cs | 50 +- .../Migrations/20221120234504_FishAlbum.cs | 46 +- .../20221123050131_LapenshardAndQuest.cs | 70 +- .../Migrations/20221128025545_Guild.cs | 290 +- .../Migrations/20230131235322_PremiumClub.cs | 46 +- .../Migrations/20230202170503_GameEvent.cs | 66 +- .../20230222192335_GameEventUserValue.cs | 58 +- .../Migrations/20230224050109_Shop.cs | 214 +- ...0228063504_CharacterAndAccountAddFields.cs | 94 +- .../20230604024657_GachaDismantle.cs | 50 +- .../Migrations/20230702192843_BeautyShop.cs | 160 +- .../Migrations/20230822011738_Achievement.cs | 142 +- .../Migrations/20230826013538_WebStorage.cs | 84 +- .../Migrations/20230827232716_MeretMarket.cs | 242 +- .../Migrations/20230905222135_Quest.cs | 94 +- .../20230911045230_SkillCooldown.cs | 52 +- .../Migrations/20230911161332_Gathering.cs | 52 +- .../Migrations/20230921034534_ShopsPart2.cs | 464 +- .../Migrations/20231002044140_UgcMarket.cs | 244 +- .../Migrations/20240501000613_DeathPenalty.cs | 52 +- .../20240516033423_SurvivalStats.cs | 138 +- .../Migrations/20240517172222_Prestige.cs | 120 +- .../Migrations/20240518200914_ServerInfo.cs | 60 +- .../Migrations/20240519031103_BlackMarket.cs | 82 +- .../Migrations/20240522171131_GuideRecord.cs | 118 +- .../Migrations/20240528031038_SkillPoints.cs | 52 +- .../Migrations/20240530004923_StatPoints.cs | 74 +- .../Migrations/20240609073700_Club.cs | 96 +- .../Migrations/20240610175759_Medal.cs | 62 +- .../20240709075218_RemoveGameEvent.cs | 72 +- .../20240821010240_ugc-banner-slots.cs | 98 +- .../Migrations/20240914074826_RemoveShops.cs | 314 +- .../20240915233502_AddHomeLayouts.cs | 52 +- .../20240916014720_RemoveBeautyShops.cs | 138 +- .../20240918062829_MeretMarketRework.cs | 158 +- ...40919045503_AddHomeLayoutsAndCubesTable.cs | 170 +- ...240921185307_AddHomePropertiesToLayouts.cs | 94 +- ...2093312_AddItemBlueprintToUgcMarketItem.cs | 52 +- .../20240927044107_AddCubeSettings.cs | 116 +- .../Migrations/20240928075403_Marriage.cs | 130 +- .../20241002170426_AddAccountPassword.cs | 54 +- .../Migrations/20241012062127_WeddingHall.cs | 126 +- .../Migrations/20241013181858_Nurturing.cs | 70 +- .../20241121023155_RemoveMailReceiverIdFK.cs | 52 +- .../20250207213214_HomeDecoration.cs | 118 +- .../Migrations/20250214234115_CubeInteract.cs | 112 +- .../20250219083923_ReworkInteractCube.cs | 72 +- .../20250304061437_InteractCubeFix.cs | 42 +- .../20250304120212_CharacterReturnChannel.cs | 50 +- .../Migrations/20250306005429_DungeonInfo.cs | 112 +- .../Migrations/20250306081311_CubeCleanUp.cs | 92 +- .../Migrations/20250331233640_DungeonInfo2.cs | 194 +- .../Migrations/20250401090105_PlayerReport.cs | 84 +- .../20250404235104_RemoveSkillCooldown.cs | 50 +- .../Migrations/20250406064924_DeathCount.cs | 70 +- .../20250410190731_AdminPermissions.cs | 52 +- .../Migrations/20250506175402_Mentor.cs | 50 +- Maple2.Server.World/Ms2ContextFactory.cs | 54 +- Maple2.Server.World/Program.cs | 238 +- Maple2.Server.World/Service/GlobalService.cs | 184 +- .../Service/WorldService.Admin.cs | 248 +- .../Service/WorldService.BlackMarket.cs | 190 +- .../Service/WorldService.Buddy.cs | 52 +- .../Service/WorldService.Chat.cs | 308 +- .../Service/WorldService.Club.cs | 484 +- .../Service/WorldService.GamePorts.cs | 32 +- .../Service/WorldService.GameReset.cs | 46 +- .../Service/WorldService.GroupChat.cs | 268 +- .../Service/WorldService.Guild.cs | 572 +- .../Service/WorldService.Locks.cs | 78 +- .../Service/WorldService.Marriage.cs | 52 +- .../Service/WorldService.Migrate.cs | 262 +- .../Service/WorldService.Party.cs | 514 +- .../Service/WorldService.PartySearch.cs | 158 +- .../Service/WorldService.PlayerConfig.cs | 98 +- .../Service/WorldService.PlayerWarp.cs | 60 +- .../Service/WorldService.Sync.cs | 294 +- .../Service/WorldService.TimeEvent.cs | 108 +- Maple2.Server.World/Service/WorldService.cs | 94 +- Maple2.Server.World/WorldServer.cs | 560 +- Maple2.Tools/Collision/BoundingBox.cs | 64 +- Maple2.Tools/Collision/Circle.cs | 120 +- Maple2.Tools/Collision/HoleCircle.cs | 98 +- Maple2.Tools/Collision/IPolygon.cs | 46 +- Maple2.Tools/Collision/IPrism.cs | 24 +- Maple2.Tools/Collision/PointPrism.cs | 108 +- Maple2.Tools/Collision/Polygon.cs | 224 +- Maple2.Tools/Collision/Prism.cs | 58 +- Maple2.Tools/Collision/Range.cs | 18 +- Maple2.Tools/Collision/Rectangle.cs | 50 +- Maple2.Tools/Collision/Trapezoid.cs | 48 +- Maple2.Tools/ConcurrentMultiDictionary.cs | 176 +- Maple2.Tools/DotRecast/DotRecastHelper.cs | 98 +- Maple2.Tools/Dotenv.cs | 62 +- .../ClassSerializationExtensions.cs | 110 +- Maple2.Tools/Extensions/DateTimeExtension.cs | 26 +- .../Extensions/EnumerableExtensions.cs | 210 +- Maple2.Tools/Extensions/FloatExtensions.cs | 26 +- Maple2.Tools/Extensions/ListExtension.cs | 132 +- Maple2.Tools/Extensions/MatrixExtensions.cs | 262 +- Maple2.Tools/Extensions/PacketExtensions.cs | 348 +- .../Extensions/QuaternionExtensions.cs | 38 +- Maple2.Tools/Extensions/RandomExtensions.cs | 26 +- Maple2.Tools/Extensions/StringExtension.cs | 50 +- .../StructSerializationExtensions.cs | 58 +- Maple2.Tools/Extensions/VectorExtensions.cs | 302 +- Maple2.Tools/IByteSerializable.cs | 22 +- Maple2.Tools/LimitedStack.cs | 100 +- Maple2.Tools/Paths.cs | 34 +- Maple2.Tools/Scheduler/EventQueue.cs | 228 +- Maple2.Tools/Scheduler/ScheduledEvent.cs | 82 +- Maple2.Tools/VectorMath/BoundingBox3.cs | 358 +- Maple2.Tools/VectorMath/Ray.cs | 8 +- Maple2.Tools/VectorMath/Transform.cs | 372 +- Maple2.Tools/WeightedSet.cs | 90 +- 1237 files changed, 131695 insertions(+), 131655 deletions(-) diff --git a/Maple2.Database/Context/MetadataContext.cs b/Maple2.Database/Context/MetadataContext.cs index 528561097..89ef77a54 100644 --- a/Maple2.Database/Context/MetadataContext.cs +++ b/Maple2.Database/Context/MetadataContext.cs @@ -1,263 +1,263 @@ -using Maple2.Database.Extensions; -using Maple2.Database.Model.Metadata; -using Maple2.Model.Metadata; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Context; - -public sealed class MetadataContext(DbContextOptions options) : DbContext(options) { - public DbSet SchemaVersion { get; set; } = null!; - public DbSet TableChecksum { get; set; } = null!; - public DbSet AdditionalEffectMetadata { get; set; } = null!; - public DbSet AnimationMetadata { get; set; } = null!; - public DbSet AiMetadata { get; set; } = null!; - public DbSet ItemMetadata { get; set; } = null!; - public DbSet NpcMetadata { get; set; } = null!; - public DbSet MapMetadata { get; set; } = null!; - public DbSet MapEntity { get; set; } = null!; - public DbSet PetMetadata { get; set; } = null!; - public DbSet QuestMetadata { get; set; } = null!; - public DbSet RideMetadata { get; set; } = null!; - public DbSet ScriptMetadata { get; set; } = null!; - public DbSet SkillMetadata { get; set; } = null!; - public DbSet TableMetadata { get; set; } = null!; - public DbSet AchievementMetadata { get; set; } = null!; - public DbSet UgcMapMetadata { get; set; } = null!; - public DbSet ExportedUgcMapMetadata { get; set; } = null!; - public DbSet ServerTableMetadata { get; set; } = null!; - public DbSet NifMetadata { get; set; } = null!; - public DbSet NXSMeshMetadata { get; set; } = null!; - public DbSet FunctionCubeMetadata { get; set; } = null!; - public DbSet MapDataMetadata { get; set; } = null!; - public DbSet TriggerMetadata { get; set; } = null!; - - protected override void OnModelCreating(ModelBuilder modelBuilder) { - base.OnModelCreating(modelBuilder); - modelBuilder.Entity(Maple2.Database.Model.Metadata.SchemaVersion.Configure); - modelBuilder.Entity(Maple2.Database.Model.Metadata.TableChecksum.Configure); - modelBuilder.Entity(ConfigureAdditionalEffectMetadata); - modelBuilder.Entity(ConfigureAnimationMetadata); - modelBuilder.Entity(ConfigureAiMetadata); - modelBuilder.Entity(ConfigureItemMetadata); - modelBuilder.Entity(ConfigureNpcMetadata); - modelBuilder.Entity(ConfigureMapMetadata); - modelBuilder.Entity(ConfigureMapEntity); - modelBuilder.Entity(ConfigureMapData); - modelBuilder.Entity(ConfigurePetMetadata); - modelBuilder.Entity(ConfigureQuestMetadata); - modelBuilder.Entity(ConfigureRideMetadata); - modelBuilder.Entity(ConfigureScriptMetadata); - modelBuilder.Entity(ConfigureSkillMetadata); - modelBuilder.Entity(ConfigureTableMetadata); - modelBuilder.Entity(ConfigureAchievementMetadata); - modelBuilder.Entity(ConfigureUgcMapMetadata); - modelBuilder.Entity(ConfigureExportedUgcMapMetadata); - modelBuilder.Entity(ConfigureServerTableMetadata); - modelBuilder.Entity(ConfigureNifMetadata); - modelBuilder.Entity(ConfigureNXSMeshMetadata); - modelBuilder.Entity(ConfigureFunctionCubeMetadata); - modelBuilder.Entity(ConfigureTriggerMetadata); - } - - private static void ConfigureAdditionalEffectMetadata(EntityTypeBuilder builder) { - builder.ToTable("additional-effect"); - builder.HasKey(effect => new { effect.Id, effect.Level }); - builder.Property(effect => effect.Condition).HasJsonConversion(); - builder.Property(effect => effect.Property).HasJsonConversion(); - builder.Property(effect => effect.Consume).HasJsonConversion(); - builder.Property(effect => effect.Update).HasJsonConversion(); - builder.Property(effect => effect.Status).HasJsonConversion(); - builder.Property(effect => effect.Recovery).HasJsonConversion(); - builder.Property(effect => effect.Dot).HasJsonConversion(); - builder.Property(effect => effect.Reflect).HasJsonConversion(); - builder.Property(effect => effect.Shield).HasJsonConversion(); - builder.Property(effect => effect.InvokeEffect).HasJsonConversion(); - builder.Property(effect => effect.Skills).HasJsonConversion(); - builder.Property(effect => effect.TickSkills).HasJsonConversion(); - builder.Property(effect => effect.ModifyOverlapCount).HasJsonConversion(); - } - - private static void ConfigureAnimationMetadata(EntityTypeBuilder builder) { - builder.ToTable("animation"); - builder.HasKey(ani => ani.Model); - builder.Property(ani => ani.Sequences).HasJsonConversion(); - } - - private static void ConfigureAiMetadata(EntityTypeBuilder builder) { - builder.ToTable("ai"); - builder.HasKey(npcAi => npcAi.Name); - builder.Property(npcAi => npcAi.Reserved).HasJsonConversion(); - builder.Property(npcAi => npcAi.Battle).HasJsonConversion(); - builder.Property(npcAi => npcAi.BattleEnd).HasJsonConversion(); - builder.Property(npcAi => npcAi.AiPresets).HasJsonConversion(); - } - - private static void ConfigureItemMetadata(EntityTypeBuilder builder) { - builder.ToTable("item"); - builder.HasKey(item => item.Id); - builder.Property(item => item.SlotNames).HasJsonConversion(); - builder.Property(item => item.DefaultHairs).HasJsonConversion(); - builder.Property(item => item.Life).HasJsonConversion(); - builder.Property(item => item.Property).HasJsonConversion(); - builder.Property(item => item.Customize).HasJsonConversion(); - builder.Property(item => item.Limit).HasJsonConversion(); - builder.Property(item => item.Skill).HasJsonConversion(); - builder.Property(item => item.Function).HasJsonConversion(); - builder.Property(item => item.AdditionalEffects).HasJsonConversion(); - builder.Property(item => item.Option).HasJsonConversion(); - builder.Property(item => item.Music).HasJsonConversion(); - builder.Property(item => item.Housing).HasJsonConversion(); - builder.Property(item => item.Install).HasJsonConversion(); - } - - private static void ConfigureNpcMetadata(EntityTypeBuilder builder) { - builder.ToTable("npc"); - builder.HasKey(npc => npc.Id); - builder.Property(npc => npc.Model).HasJsonConversion(); - builder.Property(npc => npc.Stat).HasJsonConversion(); - builder.Property(npc => npc.Basic).HasJsonConversion(); - builder.Property(npc => npc.Distance).HasJsonConversion(); - builder.Property(npc => npc.Skill).HasJsonConversion(); - builder.Property(npc => npc.Property).HasJsonConversion(); - builder.Property(npc => npc.DropInfo).HasJsonConversion(); - builder.Property(npc => npc.Action).HasJsonConversion(); - builder.Property(npc => npc.Dead).HasJsonConversion(); - builder.Property(npc => npc.LookAtTarget).HasJsonConversion(); - } - - private static void ConfigureMapMetadata(EntityTypeBuilder builder) { - builder.ToTable("map"); - builder.HasKey(map => map.Id); - builder.Property(map => map.Property).HasJsonConversion(); - builder.Property(map => map.Limit).HasJsonConversion(); - builder.Property(map => map.Drop).HasJsonConversion(); - builder.Property(map => map.Spawns).HasJsonConversion(); - builder.Property(map => map.CashCall).HasJsonConversion(); - builder.Property(map => map.EntranceBuffs).HasJsonConversion(); - } - - private static void ConfigureMapEntity(EntityTypeBuilder builder) { - builder.ToTable("map-entity"); - builder.HasKey(entity => new { entity.XBlock, Id = entity.Guid }); - builder.Property(entity => entity.Block).HasJsonConversion().IsRequired(); - } - - private static void ConfigureMapData(EntityTypeBuilder builder) { - builder.ToTable("map-data"); - builder.HasKey(entity => entity.XBlock); - } - - private static void ConfigurePetMetadata(EntityTypeBuilder builder) { - builder.ToTable("pet"); - builder.HasKey(pet => pet.Id); - builder.HasIndex(pet => pet.NpcId); - builder.Property(pet => pet.AiPresets).HasJsonConversion(); - builder.Property(pet => pet.Skill).HasJsonConversion(); - builder.Property(pet => pet.Effect).HasJsonConversion(); - builder.Property(pet => pet.Distance).HasJsonConversion(); - builder.Property(pet => pet.Time).HasJsonConversion(); - } - - private static void ConfigureQuestMetadata(EntityTypeBuilder builder) { - builder.ToTable("quest"); - builder.HasKey(quest => quest.Id); - builder.Property(quest => quest.Basic).HasJsonConversion(); - builder.Property(quest => quest.Require).HasJsonConversion(); - builder.Property(quest => quest.AcceptReward).HasJsonConversion(); - builder.Property(quest => quest.CompleteReward).HasJsonConversion(); - builder.Property(quest => quest.Conditions).HasJsonConversion(); - builder.Property(quest => quest.RemoteAccept).HasJsonConversion(); - builder.Property(quest => quest.RemoteComplete).HasJsonConversion(); - builder.Property(quest => quest.GoToNpc).HasJsonConversion(); - builder.Property(quest => quest.GoToDungeon).HasJsonConversion(); - builder.Property(quest => quest.Dispatch).HasJsonConversion(); - builder.Property(quest => quest.Mentoring).HasJsonConversion(); - builder.Property(quest => quest.SummonPortal).HasJsonConversion(); - } - - private static void ConfigureRideMetadata(EntityTypeBuilder builder) { - builder.ToTable("ride"); - builder.HasKey(ride => ride.Id); - builder.Property(ride => ride.Basic).HasJsonConversion(); - builder.Property(ride => ride.Speed).HasJsonConversion(); - builder.Property(ride => ride.Stats).HasJsonConversion(); - } - - private static void ConfigureScriptMetadata(EntityTypeBuilder builder) { - builder.ToTable("script"); - builder.HasKey(script => script.Id); - builder.HasIndex(script => script.Type); - builder.Property(script => script.States).HasJsonConversion(); - } - - private static void ConfigureSkillMetadata(EntityTypeBuilder builder) { - builder.ToTable("skill"); - builder.HasKey(skill => skill.Id); - builder.Property(skill => skill.Property).HasJsonConversion(); - builder.Property(skill => skill.State).HasJsonConversion(); - builder.Property(skill => skill.Levels).HasJsonConversion(); - } - - private static void ConfigureTableMetadata(EntityTypeBuilder builder) { - builder.ToTable("table"); - builder.HasKey(table => table.Name); - builder.Property(table => table.Table).HasJsonConversion().IsRequired(); - } - - private static void ConfigureAchievementMetadata(EntityTypeBuilder builder) { - builder.ToTable("achievement"); - builder.HasKey(achievement => achievement.Id); - builder.Property(achievement => achievement.CategoryTags).HasJsonConversion(); - builder.Property(achievement => achievement.Grades).HasJsonConversion(); - } - - private static void ConfigureUgcMapMetadata(EntityTypeBuilder builder) { - builder.ToTable("ugcmap"); - builder.HasKey(map => map.Id); - builder.Property(map => map.Plots).HasJsonConversion().IsRequired(); - } - - private static void ConfigureExportedUgcMapMetadata(EntityTypeBuilder builder) { - builder.ToTable("exportedugcmap"); - builder.HasKey(map => map.Id); - builder.Property(map => map.BaseCubePosition).HasJsonConversion().IsRequired(); - builder.Property(map => map.IndoorSize).HasJsonConversion().IsRequired(); - builder.Property(map => map.Cubes).HasJsonConversion().IsRequired(); - } - - private static void ConfigureServerTableMetadata(EntityTypeBuilder builder) { - builder.ToTable("server-table"); - builder.HasKey(table => table.Name); - builder.Property(table => table.Table).HasJsonConversion().IsRequired(); - } - - private static void ConfigureNifMetadata(EntityTypeBuilder builder) { - builder.ToTable("nif"); - builder.HasKey(nif => nif.Llid); - builder.Property(nif => nif.Blocks).HasJsonConversion(); - builder.Property(nif => nif.PhysXBounds).HasJsonConversion(); - } - - private static void ConfigureNXSMeshMetadata(EntityTypeBuilder builder) { - builder.ToTable("nxs-mesh"); - builder.Property(mesh => mesh.Index).ValueGeneratedNever(); - builder.HasKey(mesh => mesh.Index); - builder.Property(nif => nif.Bounds).HasJsonConversion(); - } - - private static void ConfigureFunctionCubeMetadata(EntityTypeBuilder builder) { - builder.ToTable("function-cube"); - builder.HasKey(cube => cube.Id); - builder.Property(cube => cube.AutoStateChange).HasJsonConversion(); - builder.Property(cube => cube.Nurturing).HasJsonConversion(); - } - - private static void ConfigureTriggerMetadata(EntityTypeBuilder builder) { - builder.ToTable("trigger"); - builder.HasKey(trigger => new { - trigger.MapXBlock, - trigger.Name, - }); - } -} +using Maple2.Database.Extensions; +using Maple2.Database.Model.Metadata; +using Maple2.Model.Metadata; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Context; + +public sealed class MetadataContext(DbContextOptions options) : DbContext(options) { + public DbSet SchemaVersion { get; set; } = null!; + public DbSet TableChecksum { get; set; } = null!; + public DbSet AdditionalEffectMetadata { get; set; } = null!; + public DbSet AnimationMetadata { get; set; } = null!; + public DbSet AiMetadata { get; set; } = null!; + public DbSet ItemMetadata { get; set; } = null!; + public DbSet NpcMetadata { get; set; } = null!; + public DbSet MapMetadata { get; set; } = null!; + public DbSet MapEntity { get; set; } = null!; + public DbSet PetMetadata { get; set; } = null!; + public DbSet QuestMetadata { get; set; } = null!; + public DbSet RideMetadata { get; set; } = null!; + public DbSet ScriptMetadata { get; set; } = null!; + public DbSet SkillMetadata { get; set; } = null!; + public DbSet TableMetadata { get; set; } = null!; + public DbSet AchievementMetadata { get; set; } = null!; + public DbSet UgcMapMetadata { get; set; } = null!; + public DbSet ExportedUgcMapMetadata { get; set; } = null!; + public DbSet ServerTableMetadata { get; set; } = null!; + public DbSet NifMetadata { get; set; } = null!; + public DbSet NXSMeshMetadata { get; set; } = null!; + public DbSet FunctionCubeMetadata { get; set; } = null!; + public DbSet MapDataMetadata { get; set; } = null!; + public DbSet TriggerMetadata { get; set; } = null!; + + protected override void OnModelCreating(ModelBuilder modelBuilder) { + base.OnModelCreating(modelBuilder); + modelBuilder.Entity(Maple2.Database.Model.Metadata.SchemaVersion.Configure); + modelBuilder.Entity(Maple2.Database.Model.Metadata.TableChecksum.Configure); + modelBuilder.Entity(ConfigureAdditionalEffectMetadata); + modelBuilder.Entity(ConfigureAnimationMetadata); + modelBuilder.Entity(ConfigureAiMetadata); + modelBuilder.Entity(ConfigureItemMetadata); + modelBuilder.Entity(ConfigureNpcMetadata); + modelBuilder.Entity(ConfigureMapMetadata); + modelBuilder.Entity(ConfigureMapEntity); + modelBuilder.Entity(ConfigureMapData); + modelBuilder.Entity(ConfigurePetMetadata); + modelBuilder.Entity(ConfigureQuestMetadata); + modelBuilder.Entity(ConfigureRideMetadata); + modelBuilder.Entity(ConfigureScriptMetadata); + modelBuilder.Entity(ConfigureSkillMetadata); + modelBuilder.Entity(ConfigureTableMetadata); + modelBuilder.Entity(ConfigureAchievementMetadata); + modelBuilder.Entity(ConfigureUgcMapMetadata); + modelBuilder.Entity(ConfigureExportedUgcMapMetadata); + modelBuilder.Entity(ConfigureServerTableMetadata); + modelBuilder.Entity(ConfigureNifMetadata); + modelBuilder.Entity(ConfigureNXSMeshMetadata); + modelBuilder.Entity(ConfigureFunctionCubeMetadata); + modelBuilder.Entity(ConfigureTriggerMetadata); + } + + private static void ConfigureAdditionalEffectMetadata(EntityTypeBuilder builder) { + builder.ToTable("additional-effect"); + builder.HasKey(effect => new { effect.Id, effect.Level }); + builder.Property(effect => effect.Condition).HasJsonConversion(); + builder.Property(effect => effect.Property).HasJsonConversion(); + builder.Property(effect => effect.Consume).HasJsonConversion(); + builder.Property(effect => effect.Update).HasJsonConversion(); + builder.Property(effect => effect.Status).HasJsonConversion(); + builder.Property(effect => effect.Recovery).HasJsonConversion(); + builder.Property(effect => effect.Dot).HasJsonConversion(); + builder.Property(effect => effect.Reflect).HasJsonConversion(); + builder.Property(effect => effect.Shield).HasJsonConversion(); + builder.Property(effect => effect.InvokeEffect).HasJsonConversion(); + builder.Property(effect => effect.Skills).HasJsonConversion(); + builder.Property(effect => effect.TickSkills).HasJsonConversion(); + builder.Property(effect => effect.ModifyOverlapCount).HasJsonConversion(); + } + + private static void ConfigureAnimationMetadata(EntityTypeBuilder builder) { + builder.ToTable("animation"); + builder.HasKey(ani => ani.Model); + builder.Property(ani => ani.Sequences).HasJsonConversion(); + } + + private static void ConfigureAiMetadata(EntityTypeBuilder builder) { + builder.ToTable("ai"); + builder.HasKey(npcAi => npcAi.Name); + builder.Property(npcAi => npcAi.Reserved).HasJsonConversion(); + builder.Property(npcAi => npcAi.Battle).HasJsonConversion(); + builder.Property(npcAi => npcAi.BattleEnd).HasJsonConversion(); + builder.Property(npcAi => npcAi.AiPresets).HasJsonConversion(); + } + + private static void ConfigureItemMetadata(EntityTypeBuilder builder) { + builder.ToTable("item"); + builder.HasKey(item => item.Id); + builder.Property(item => item.SlotNames).HasJsonConversion(); + builder.Property(item => item.DefaultHairs).HasJsonConversion(); + builder.Property(item => item.Life).HasJsonConversion(); + builder.Property(item => item.Property).HasJsonConversion(); + builder.Property(item => item.Customize).HasJsonConversion(); + builder.Property(item => item.Limit).HasJsonConversion(); + builder.Property(item => item.Skill).HasJsonConversion(); + builder.Property(item => item.Function).HasJsonConversion(); + builder.Property(item => item.AdditionalEffects).HasJsonConversion(); + builder.Property(item => item.Option).HasJsonConversion(); + builder.Property(item => item.Music).HasJsonConversion(); + builder.Property(item => item.Housing).HasJsonConversion(); + builder.Property(item => item.Install).HasJsonConversion(); + } + + private static void ConfigureNpcMetadata(EntityTypeBuilder builder) { + builder.ToTable("npc"); + builder.HasKey(npc => npc.Id); + builder.Property(npc => npc.Model).HasJsonConversion(); + builder.Property(npc => npc.Stat).HasJsonConversion(); + builder.Property(npc => npc.Basic).HasJsonConversion(); + builder.Property(npc => npc.Distance).HasJsonConversion(); + builder.Property(npc => npc.Skill).HasJsonConversion(); + builder.Property(npc => npc.Property).HasJsonConversion(); + builder.Property(npc => npc.DropInfo).HasJsonConversion(); + builder.Property(npc => npc.Action).HasJsonConversion(); + builder.Property(npc => npc.Dead).HasJsonConversion(); + builder.Property(npc => npc.LookAtTarget).HasJsonConversion(); + } + + private static void ConfigureMapMetadata(EntityTypeBuilder builder) { + builder.ToTable("map"); + builder.HasKey(map => map.Id); + builder.Property(map => map.Property).HasJsonConversion(); + builder.Property(map => map.Limit).HasJsonConversion(); + builder.Property(map => map.Drop).HasJsonConversion(); + builder.Property(map => map.Spawns).HasJsonConversion(); + builder.Property(map => map.CashCall).HasJsonConversion(); + builder.Property(map => map.EntranceBuffs).HasJsonConversion(); + } + + private static void ConfigureMapEntity(EntityTypeBuilder builder) { + builder.ToTable("map-entity"); + builder.HasKey(entity => new { entity.XBlock, Id = entity.Guid }); + builder.Property(entity => entity.Block).HasJsonConversion().IsRequired(); + } + + private static void ConfigureMapData(EntityTypeBuilder builder) { + builder.ToTable("map-data"); + builder.HasKey(entity => entity.XBlock); + } + + private static void ConfigurePetMetadata(EntityTypeBuilder builder) { + builder.ToTable("pet"); + builder.HasKey(pet => pet.Id); + builder.HasIndex(pet => pet.NpcId); + builder.Property(pet => pet.AiPresets).HasJsonConversion(); + builder.Property(pet => pet.Skill).HasJsonConversion(); + builder.Property(pet => pet.Effect).HasJsonConversion(); + builder.Property(pet => pet.Distance).HasJsonConversion(); + builder.Property(pet => pet.Time).HasJsonConversion(); + } + + private static void ConfigureQuestMetadata(EntityTypeBuilder builder) { + builder.ToTable("quest"); + builder.HasKey(quest => quest.Id); + builder.Property(quest => quest.Basic).HasJsonConversion(); + builder.Property(quest => quest.Require).HasJsonConversion(); + builder.Property(quest => quest.AcceptReward).HasJsonConversion(); + builder.Property(quest => quest.CompleteReward).HasJsonConversion(); + builder.Property(quest => quest.Conditions).HasJsonConversion(); + builder.Property(quest => quest.RemoteAccept).HasJsonConversion(); + builder.Property(quest => quest.RemoteComplete).HasJsonConversion(); + builder.Property(quest => quest.GoToNpc).HasJsonConversion(); + builder.Property(quest => quest.GoToDungeon).HasJsonConversion(); + builder.Property(quest => quest.Dispatch).HasJsonConversion(); + builder.Property(quest => quest.Mentoring).HasJsonConversion(); + builder.Property(quest => quest.SummonPortal).HasJsonConversion(); + } + + private static void ConfigureRideMetadata(EntityTypeBuilder builder) { + builder.ToTable("ride"); + builder.HasKey(ride => ride.Id); + builder.Property(ride => ride.Basic).HasJsonConversion(); + builder.Property(ride => ride.Speed).HasJsonConversion(); + builder.Property(ride => ride.Stats).HasJsonConversion(); + } + + private static void ConfigureScriptMetadata(EntityTypeBuilder builder) { + builder.ToTable("script"); + builder.HasKey(script => script.Id); + builder.HasIndex(script => script.Type); + builder.Property(script => script.States).HasJsonConversion(); + } + + private static void ConfigureSkillMetadata(EntityTypeBuilder builder) { + builder.ToTable("skill"); + builder.HasKey(skill => skill.Id); + builder.Property(skill => skill.Property).HasJsonConversion(); + builder.Property(skill => skill.State).HasJsonConversion(); + builder.Property(skill => skill.Levels).HasJsonConversion(); + } + + private static void ConfigureTableMetadata(EntityTypeBuilder builder) { + builder.ToTable("table"); + builder.HasKey(table => table.Name); + builder.Property(table => table.Table).HasJsonConversion().IsRequired(); + } + + private static void ConfigureAchievementMetadata(EntityTypeBuilder builder) { + builder.ToTable("achievement"); + builder.HasKey(achievement => achievement.Id); + builder.Property(achievement => achievement.CategoryTags).HasJsonConversion(); + builder.Property(achievement => achievement.Grades).HasJsonConversion(); + } + + private static void ConfigureUgcMapMetadata(EntityTypeBuilder builder) { + builder.ToTable("ugcmap"); + builder.HasKey(map => map.Id); + builder.Property(map => map.Plots).HasJsonConversion().IsRequired(); + } + + private static void ConfigureExportedUgcMapMetadata(EntityTypeBuilder builder) { + builder.ToTable("exportedugcmap"); + builder.HasKey(map => map.Id); + builder.Property(map => map.BaseCubePosition).HasJsonConversion().IsRequired(); + builder.Property(map => map.IndoorSize).HasJsonConversion().IsRequired(); + builder.Property(map => map.Cubes).HasJsonConversion().IsRequired(); + } + + private static void ConfigureServerTableMetadata(EntityTypeBuilder builder) { + builder.ToTable("server-table"); + builder.HasKey(table => table.Name); + builder.Property(table => table.Table).HasJsonConversion().IsRequired(); + } + + private static void ConfigureNifMetadata(EntityTypeBuilder builder) { + builder.ToTable("nif"); + builder.HasKey(nif => nif.Llid); + builder.Property(nif => nif.Blocks).HasJsonConversion(); + builder.Property(nif => nif.PhysXBounds).HasJsonConversion(); + } + + private static void ConfigureNXSMeshMetadata(EntityTypeBuilder builder) { + builder.ToTable("nxs-mesh"); + builder.Property(mesh => mesh.Index).ValueGeneratedNever(); + builder.HasKey(mesh => mesh.Index); + builder.Property(nif => nif.Bounds).HasJsonConversion(); + } + + private static void ConfigureFunctionCubeMetadata(EntityTypeBuilder builder) { + builder.ToTable("function-cube"); + builder.HasKey(cube => cube.Id); + builder.Property(cube => cube.AutoStateChange).HasJsonConversion(); + builder.Property(cube => cube.Nurturing).HasJsonConversion(); + } + + private static void ConfigureTriggerMetadata(EntityTypeBuilder builder) { + builder.ToTable("trigger"); + builder.HasKey(trigger => new { + trigger.MapXBlock, + trigger.Name, + }); + } +} diff --git a/Maple2.Database/Context/Ms2Context.cs b/Maple2.Database/Context/Ms2Context.cs index 6db8a809c..008b0c653 100644 --- a/Maple2.Database/Context/Ms2Context.cs +++ b/Maple2.Database/Context/Ms2Context.cs @@ -1,98 +1,98 @@ -using Maple2.Database.Model; -using Maple2.Database.Model.Shop; -using Microsoft.EntityFrameworkCore; - -namespace Maple2.Database.Context; - -public sealed class Ms2Context(DbContextOptions options) : DbContext(options) { - internal DbSet Account { get; set; } = null!; - internal DbSet Character { get; set; } = null!; - internal DbSet CharacterConfig { get; set; } = null!; - internal DbSet CharacterUnlock { get; set; } = null!; - internal DbSet Guild { get; set; } = null!; - internal DbSet GuildMember { get; set; } = null!; - internal DbSet GuildApplication { get; set; } = null!; - internal DbSet Home { get; set; } = null!; - internal DbSet Item { get; set; } = null!; - internal DbSet PetConfig { get; set; } = null!; - internal DbSet ItemStorage { get; set; } = null!; - internal DbSet Club { get; set; } = null!; - internal DbSet ClubMember { get; set; } = null!; - internal DbSet SkillTab { get; set; } = null!; - internal DbSet Buddy { get; set; } = null!; - internal DbSet UgcMap { get; set; } = null!; - internal DbSet UgcMapCube { get; set; } = null!; - internal DbSet UgcResource { get; set; } = null!; - internal DbSet Mail { get; set; } = null!; - internal DbSet MesoMarket { get; set; } = null!; - internal DbSet MesoMarketSold { get; set; } = null!; - internal DbSet CharacterShopData { get; set; } = null!; - internal DbSet CharacterShopItemData { get; set; } = null!; - internal DbSet GameEventUserValue { get; set; } = null!; - internal DbSet SystemBanner { get; set; } = null!; - internal DbSet UgcMarketItem { get; set; } = null!; - internal DbSet SoldUgcMarketItem { get; set; } = null!; - internal DbSet SoldMeretMarketItem { get; set; } = null!; - internal DbSet BlackMarketListing { get; set; } = null!; - internal DbSet Achievement { get; set; } = null!; - internal DbSet Quest { get; set; } = null!; - internal DbSet ServerInfo { get; set; } = null!; - internal DbSet Medal { get; set; } = null!; - internal DbSet BannerSlots { get; set; } = null!; - internal DbSet HomeLayout { get; set; } = null!; - internal DbSet UgcCubeLayout { get; set; } = null!; - internal DbSet Marriage { get; set; } = null!; - internal DbSet WeddingHall { get; set; } = null!; - internal DbSet Nurturing { get; set; } = null!; - internal DbSet DungeonRecord { get; set; } = null!; - internal DbSet PlayerReports { get; set; } = null!; - - protected override void OnModelCreating(ModelBuilder modelBuilder) { - base.OnModelCreating(modelBuilder); - modelBuilder.Entity(Maple2.Database.Model.Account.Configure); - modelBuilder.Entity(Maple2.Database.Model.Character.Configure); - modelBuilder.Entity(Maple2.Database.Model.CharacterConfig.Configure); - modelBuilder.Entity(Maple2.Database.Model.CharacterUnlock.Configure); - modelBuilder.Entity(Maple2.Database.Model.Guild.Configure); - modelBuilder.Entity(Maple2.Database.Model.GuildMember.Configure); - modelBuilder.Entity(Maple2.Database.Model.GuildApplication.Configure); - modelBuilder.Entity(Maple2.Database.Model.Home.Configure); - modelBuilder.Entity(Maple2.Database.Model.Item.Configure); - modelBuilder.Entity(Maple2.Database.Model.PetConfig.Configure); - modelBuilder.Entity(Maple2.Database.Model.ItemStorage.Configure); - modelBuilder.Entity(Maple2.Database.Model.Club.Configure); - modelBuilder.Entity(Maple2.Database.Model.ClubMember.Configure); - modelBuilder.Entity(Maple2.Database.Model.SkillTab.Configure); - modelBuilder.Entity(Maple2.Database.Model.Buddy.Configure); - modelBuilder.Entity(Maple2.Database.Model.UgcMap.Configure); - modelBuilder.Entity(Maple2.Database.Model.UgcMapCube.Configure); - modelBuilder.Entity(Maple2.Database.Model.UgcResource.Configure); - modelBuilder.Entity(Maple2.Database.Model.Mail.Configure); - modelBuilder.Entity(Maple2.Database.Model.SystemBanner.Configure); - modelBuilder.Entity(Maple2.Database.Model.UgcMarketItem.Configure); - modelBuilder.Entity(Maple2.Database.Model.SoldUgcMarketItem.Configure); - modelBuilder.Entity(Maple2.Database.Model.Achievement.Configure); - modelBuilder.Entity(Maple2.Database.Model.Quest.Configure); - modelBuilder.Entity(Maple2.Database.Model.Medal.Configure); - modelBuilder.Entity(BannerSlot.Configure); - modelBuilder.Entity(Maple2.Database.Model.HomeLayout.Configure); - modelBuilder.Entity(HomeLayoutCube.Configure); - modelBuilder.Entity(Maple2.Database.Model.Marriage.Configure); - modelBuilder.Entity(Maple2.Database.Model.WeddingHall.Configure); - modelBuilder.Entity(Maple2.Database.Model.Nurturing.Configure); - - modelBuilder.Entity(MesoListing.Configure); - modelBuilder.Entity(SoldMesoListing.Configure); - modelBuilder.Entity(Maple2.Database.Model.SoldMeretMarketItem.Configure); - modelBuilder.Entity(Maple2.Database.Model.Shop.CharacterShopData.Configure); - modelBuilder.Entity(Maple2.Database.Model.Shop.CharacterShopItemData.Configure); - modelBuilder.Entity(Maple2.Database.Model.BlackMarketListing.Configure); - - modelBuilder.Entity(Maple2.Database.Model.GameEventUserValue.Configure); - - modelBuilder.Entity(Maple2.Database.Model.ServerInfo.Configure); - modelBuilder.Entity(PlayerReport.Configure); - - modelBuilder.Entity(Maple2.Database.Model.DungeonRecord.Configure); - } -} +using Maple2.Database.Model; +using Maple2.Database.Model.Shop; +using Microsoft.EntityFrameworkCore; + +namespace Maple2.Database.Context; + +public sealed class Ms2Context(DbContextOptions options) : DbContext(options) { + internal DbSet Account { get; set; } = null!; + internal DbSet Character { get; set; } = null!; + internal DbSet CharacterConfig { get; set; } = null!; + internal DbSet CharacterUnlock { get; set; } = null!; + internal DbSet Guild { get; set; } = null!; + internal DbSet GuildMember { get; set; } = null!; + internal DbSet GuildApplication { get; set; } = null!; + internal DbSet Home { get; set; } = null!; + internal DbSet Item { get; set; } = null!; + internal DbSet PetConfig { get; set; } = null!; + internal DbSet ItemStorage { get; set; } = null!; + internal DbSet Club { get; set; } = null!; + internal DbSet ClubMember { get; set; } = null!; + internal DbSet SkillTab { get; set; } = null!; + internal DbSet Buddy { get; set; } = null!; + internal DbSet UgcMap { get; set; } = null!; + internal DbSet UgcMapCube { get; set; } = null!; + internal DbSet UgcResource { get; set; } = null!; + internal DbSet Mail { get; set; } = null!; + internal DbSet MesoMarket { get; set; } = null!; + internal DbSet MesoMarketSold { get; set; } = null!; + internal DbSet CharacterShopData { get; set; } = null!; + internal DbSet CharacterShopItemData { get; set; } = null!; + internal DbSet GameEventUserValue { get; set; } = null!; + internal DbSet SystemBanner { get; set; } = null!; + internal DbSet UgcMarketItem { get; set; } = null!; + internal DbSet SoldUgcMarketItem { get; set; } = null!; + internal DbSet SoldMeretMarketItem { get; set; } = null!; + internal DbSet BlackMarketListing { get; set; } = null!; + internal DbSet Achievement { get; set; } = null!; + internal DbSet Quest { get; set; } = null!; + internal DbSet ServerInfo { get; set; } = null!; + internal DbSet Medal { get; set; } = null!; + internal DbSet BannerSlots { get; set; } = null!; + internal DbSet HomeLayout { get; set; } = null!; + internal DbSet UgcCubeLayout { get; set; } = null!; + internal DbSet Marriage { get; set; } = null!; + internal DbSet WeddingHall { get; set; } = null!; + internal DbSet Nurturing { get; set; } = null!; + internal DbSet DungeonRecord { get; set; } = null!; + internal DbSet PlayerReports { get; set; } = null!; + + protected override void OnModelCreating(ModelBuilder modelBuilder) { + base.OnModelCreating(modelBuilder); + modelBuilder.Entity(Maple2.Database.Model.Account.Configure); + modelBuilder.Entity(Maple2.Database.Model.Character.Configure); + modelBuilder.Entity(Maple2.Database.Model.CharacterConfig.Configure); + modelBuilder.Entity(Maple2.Database.Model.CharacterUnlock.Configure); + modelBuilder.Entity(Maple2.Database.Model.Guild.Configure); + modelBuilder.Entity(Maple2.Database.Model.GuildMember.Configure); + modelBuilder.Entity(Maple2.Database.Model.GuildApplication.Configure); + modelBuilder.Entity(Maple2.Database.Model.Home.Configure); + modelBuilder.Entity(Maple2.Database.Model.Item.Configure); + modelBuilder.Entity(Maple2.Database.Model.PetConfig.Configure); + modelBuilder.Entity(Maple2.Database.Model.ItemStorage.Configure); + modelBuilder.Entity(Maple2.Database.Model.Club.Configure); + modelBuilder.Entity(Maple2.Database.Model.ClubMember.Configure); + modelBuilder.Entity(Maple2.Database.Model.SkillTab.Configure); + modelBuilder.Entity(Maple2.Database.Model.Buddy.Configure); + modelBuilder.Entity(Maple2.Database.Model.UgcMap.Configure); + modelBuilder.Entity(Maple2.Database.Model.UgcMapCube.Configure); + modelBuilder.Entity(Maple2.Database.Model.UgcResource.Configure); + modelBuilder.Entity(Maple2.Database.Model.Mail.Configure); + modelBuilder.Entity(Maple2.Database.Model.SystemBanner.Configure); + modelBuilder.Entity(Maple2.Database.Model.UgcMarketItem.Configure); + modelBuilder.Entity(Maple2.Database.Model.SoldUgcMarketItem.Configure); + modelBuilder.Entity(Maple2.Database.Model.Achievement.Configure); + modelBuilder.Entity(Maple2.Database.Model.Quest.Configure); + modelBuilder.Entity(Maple2.Database.Model.Medal.Configure); + modelBuilder.Entity(BannerSlot.Configure); + modelBuilder.Entity(Maple2.Database.Model.HomeLayout.Configure); + modelBuilder.Entity(HomeLayoutCube.Configure); + modelBuilder.Entity(Maple2.Database.Model.Marriage.Configure); + modelBuilder.Entity(Maple2.Database.Model.WeddingHall.Configure); + modelBuilder.Entity(Maple2.Database.Model.Nurturing.Configure); + + modelBuilder.Entity(MesoListing.Configure); + modelBuilder.Entity(SoldMesoListing.Configure); + modelBuilder.Entity(Maple2.Database.Model.SoldMeretMarketItem.Configure); + modelBuilder.Entity(Maple2.Database.Model.Shop.CharacterShopData.Configure); + modelBuilder.Entity(Maple2.Database.Model.Shop.CharacterShopItemData.Configure); + modelBuilder.Entity(Maple2.Database.Model.BlackMarketListing.Configure); + + modelBuilder.Entity(Maple2.Database.Model.GameEventUserValue.Configure); + + modelBuilder.Entity(Maple2.Database.Model.ServerInfo.Configure); + modelBuilder.Entity(PlayerReport.Configure); + + modelBuilder.Entity(Maple2.Database.Model.DungeonRecord.Configure); + } +} diff --git a/Maple2.Database/Context/WebContext.cs b/Maple2.Database/Context/WebContext.cs index 085ee98a8..e1a3a800f 100644 --- a/Maple2.Database/Context/WebContext.cs +++ b/Maple2.Database/Context/WebContext.cs @@ -1,13 +1,13 @@ -using Maple2.Database.Model; -using Microsoft.EntityFrameworkCore; - -namespace Maple2.Database.Context; - -public sealed class WebContext(DbContextOptions options) : DbContext(options) { - internal DbSet UgcResource { get; set; } = null!; - - protected override void OnModelCreating(ModelBuilder modelBuilder) { - base.OnModelCreating(modelBuilder); - modelBuilder.Entity(Maple2.Database.Model.UgcResource.Configure); - } -} +using Maple2.Database.Model; +using Microsoft.EntityFrameworkCore; + +namespace Maple2.Database.Context; + +public sealed class WebContext(DbContextOptions options) : DbContext(options) { + internal DbSet UgcResource { get; set; } = null!; + + protected override void OnModelCreating(ModelBuilder modelBuilder) { + base.OnModelCreating(modelBuilder); + modelBuilder.Entity(Maple2.Database.Model.UgcResource.Configure); + } +} diff --git a/Maple2.Database/Extensions/DateTimeExtensions.cs b/Maple2.Database/Extensions/DateTimeExtensions.cs index 987f97eed..b61b783c9 100644 --- a/Maple2.Database/Extensions/DateTimeExtensions.cs +++ b/Maple2.Database/Extensions/DateTimeExtensions.cs @@ -1,15 +1,15 @@ -namespace Maple2.Database.Extensions; - -public static class DateTimeExtensions { - public static long ToEpochSeconds(this DateTime dateTime) { - if (dateTime <= DateTime.UnixEpoch) { - return DateTime.UnixEpoch.Second; - } - - return (long) (dateTime.ToUniversalTime() - DateTime.UnixEpoch).TotalSeconds; - } - - public static DateTime FromEpochSeconds(this long epochSeconds) { - return DateTimeOffset.FromUnixTimeSeconds(epochSeconds).LocalDateTime; - } -} +namespace Maple2.Database.Extensions; + +public static class DateTimeExtensions { + public static long ToEpochSeconds(this DateTime dateTime) { + if (dateTime <= DateTime.UnixEpoch) { + return DateTime.UnixEpoch.Second; + } + + return (long) (dateTime.ToUniversalTime() - DateTime.UnixEpoch).TotalSeconds; + } + + public static DateTime FromEpochSeconds(this long epochSeconds) { + return DateTimeOffset.FromUnixTimeSeconds(epochSeconds).LocalDateTime; + } +} diff --git a/Maple2.Database/Extensions/DbContextExtensions.cs b/Maple2.Database/Extensions/DbContextExtensions.cs index 43e3a1fd6..6be8d32be 100644 --- a/Maple2.Database/Extensions/DbContextExtensions.cs +++ b/Maple2.Database/Extensions/DbContextExtensions.cs @@ -1,36 +1,36 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; - -namespace Maple2.Database.Extensions; - -public static class DbContextExtensions { - public static string? GetTableName(this DbContext context) where T : class { - IEntityType entityType = context.Model.GetEntityTypes().First(type => type.ClrType == typeof(T)); - - IAnnotation tableNameAnnotation = entityType.GetAnnotation("Relational:TableName"); - return tableNameAnnotation.Value?.ToString(); - } - - public static bool TrySaveChanges(this DbContext context, bool autoAccept = true) { - try { - context.SaveChanges(autoAccept); - return true; - } catch (Exception ex) { - Console.WriteLine($"> Failed {context.ContextId}"); - Console.WriteLine(ex); - return false; - } - } - - internal static void DisplayStates(this IEnumerable entries) { - foreach (EntityEntry entry in entries) { - Console.WriteLine($"Entity: {entry.Entity.GetType().Name}, State: {entry.State.ToString()} "); - } - } - - public static void Overwrite(this DbContext context, T entity) where T : class { - context.Entry(entity).State = EntityState.Modified; - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; + +namespace Maple2.Database.Extensions; + +public static class DbContextExtensions { + public static string? GetTableName(this DbContext context) where T : class { + IEntityType entityType = context.Model.GetEntityTypes().First(type => type.ClrType == typeof(T)); + + IAnnotation tableNameAnnotation = entityType.GetAnnotation("Relational:TableName"); + return tableNameAnnotation.Value?.ToString(); + } + + public static bool TrySaveChanges(this DbContext context, bool autoAccept = true) { + try { + context.SaveChanges(autoAccept); + return true; + } catch (Exception ex) { + Console.WriteLine($"> Failed {context.ContextId}"); + Console.WriteLine(ex); + return false; + } + } + + internal static void DisplayStates(this IEnumerable entries) { + foreach (EntityEntry entry in entries) { + Console.WriteLine($"Entity: {entry.Entity.GetType().Name}, State: {entry.State.ToString()} "); + } + } + + public static void Overwrite(this DbContext context, T entity) where T : class { + context.Entry(entity).State = EntityState.Modified; + } +} diff --git a/Maple2.Database/Extensions/PropertyBuilderExtensions.cs b/Maple2.Database/Extensions/PropertyBuilderExtensions.cs index ccda3e753..a814fb890 100644 --- a/Maple2.Database/Extensions/PropertyBuilderExtensions.cs +++ b/Maple2.Database/Extensions/PropertyBuilderExtensions.cs @@ -1,35 +1,35 @@ -using System.Linq.Expressions; -using System.Text.Json; -using System.Text.Json.Serialization; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Extensions; - -internal static class PropertyBuilderExtensions { - private static readonly JsonSerializerOptions Options = new() { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, - }; - - static PropertyBuilderExtensions() { - Options.Converters.Add(new Vector3Converter()); - } - - public static PropertyBuilder HasJsonConversion(this PropertyBuilder builder) { - return builder.HasConversion( - property => JsonSerializer.Serialize(property, Options), - value => JsonSerializer.Deserialize(value, Options)! - ).HasColumnType("json"); - } - - public static ReferenceReferenceBuilder OneToOne( - this EntityTypeBuilder builder) where TEntity : class where TRelatedEntity : class { - return builder.HasOne().WithOne(); - } - - public static ReferenceReferenceBuilder OneToOne( - this EntityTypeBuilder builder, Expression>? navigationExpression) - where TEntity : class where TRelatedEntity : class { - return builder.HasOne(navigationExpression).WithOne(); - } -} +using System.Linq.Expressions; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Extensions; + +internal static class PropertyBuilderExtensions { + private static readonly JsonSerializerOptions Options = new() { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, + }; + + static PropertyBuilderExtensions() { + Options.Converters.Add(new Vector3Converter()); + } + + public static PropertyBuilder HasJsonConversion(this PropertyBuilder builder) { + return builder.HasConversion( + property => JsonSerializer.Serialize(property, Options), + value => JsonSerializer.Deserialize(value, Options)! + ).HasColumnType("json"); + } + + public static ReferenceReferenceBuilder OneToOne( + this EntityTypeBuilder builder) where TEntity : class where TRelatedEntity : class { + return builder.HasOne().WithOne(); + } + + public static ReferenceReferenceBuilder OneToOne( + this EntityTypeBuilder builder, Expression>? navigationExpression) + where TEntity : class where TRelatedEntity : class { + return builder.HasOne(navigationExpression).WithOne(); + } +} diff --git a/Maple2.Database/Extensions/Vector3Converter.cs b/Maple2.Database/Extensions/Vector3Converter.cs index a4065337f..2f206ef55 100644 --- a/Maple2.Database/Extensions/Vector3Converter.cs +++ b/Maple2.Database/Extensions/Vector3Converter.cs @@ -1,23 +1,23 @@ -using System.Numerics; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Maple2.Database.Extensions; - -public class Vector3Converter : JsonConverter { - private struct Vector3Surrogate { - public float X { get; set; } - public float Y { get; set; } - public float Z { get; set; } - } - - public override Vector3 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - var surrogate = JsonSerializer.Deserialize(ref reader, options); - return new Vector3(surrogate.X, surrogate.Y, surrogate.Z); - } - - public override void Write(Utf8JsonWriter writer, Vector3 value, JsonSerializerOptions options) { - var surrogate = new Vector3Surrogate { X = value.X, Y = value.Y, Z = value.Z }; - writer.WriteRawValue(JsonSerializer.SerializeToUtf8Bytes(surrogate, options)); - } -} +using System.Numerics; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Maple2.Database.Extensions; + +public class Vector3Converter : JsonConverter { + private struct Vector3Surrogate { + public float X { get; set; } + public float Y { get; set; } + public float Z { get; set; } + } + + public override Vector3 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + var surrogate = JsonSerializer.Deserialize(ref reader, options); + return new Vector3(surrogate.X, surrogate.Y, surrogate.Z); + } + + public override void Write(Utf8JsonWriter writer, Vector3 value, JsonSerializerOptions options) { + var surrogate = new Vector3Surrogate { X = value.X, Y = value.Y, Z = value.Z }; + writer.WriteRawValue(JsonSerializer.SerializeToUtf8Bytes(surrogate, options)); + } +} diff --git a/Maple2.Database/Model/Account.cs b/Maple2.Database/Model/Account.cs index 8b44f2af3..0aeb614c8 100644 --- a/Maple2.Database/Model/Account.cs +++ b/Maple2.Database/Model/Account.cs @@ -1,152 +1,152 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class Account { - public long Id { get; set; } - public required string Username { get; set; } - public string Password { get; set; } - public Guid MachineId { get; set; } - public int MaxCharacters { get; set; } - public int PrestigeLevel { get; set; } - public int PrestigeLevelsGained { get; set; } - public long PrestigeExp { get; set; } - public long PrestigeCurrentExp { get; set; } - public IList PrestigeMissions { get; set; } - public IList PrestigeRewardsClaimed { get; set; } - public long PremiumTime { get; set; } - public IList PremiumRewardsClaimed { get; set; } // TODO: clear list on daily reset - public required AccountCurrency Currency { get; set; } - public required MarketLimits MarketLimits { get; set; } - - public int SurvivalLevel { get; set; } - public long SurvivalExp { get; set; } - public int SurvivalSilverLevelRewardClaimed { get; set; } - public int SurvivalGoldLevelRewardClaimed { get; set; } - public bool ActiveGoldPass { get; set; } - - public DateTime CreationTime { get; set; } - public DateTime LastModified { get; set; } - - public bool Online { get; set; } - public string Permissions { get; set; } - - public ICollection? Characters { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Account?(Maple2.Model.Game.Account? other) { - if (other == null) { - return null; - } - - return new Account { - LastModified = other.LastModified, - Id = other.Id, - Username = other.Username, - MachineId = other.MachineId, - MaxCharacters = other.MaxCharacters, - PrestigeLevel = other.PrestigeLevel, - PrestigeLevelsGained = other.PrestigeLevelsGained, - PrestigeExp = other.PrestigeExp, - PrestigeCurrentExp = other.PrestigeCurrentExp, - PrestigeRewardsClaimed = other.PrestigeRewardsClaimed, - PrestigeMissions = other.PrestigeMissions.Select(mission => new PrestigeMission { - Id = mission.Id, - GainedLevels = mission.GainedLevels, - Awarded = mission.Awarded, - }).ToList(), - PremiumTime = other.PremiumTime, - PremiumRewardsClaimed = other.PremiumRewardsClaimed, - Currency = new AccountCurrency(), - MarketLimits = new MarketLimits { - MesoListed = other.MesoMarketListed, - MesoPurchased = other.MesoMarketPurchased, - }, - SurvivalLevel = other.SurvivalLevel, - SurvivalExp = other.SurvivalExp, - SurvivalSilverLevelRewardClaimed = other.SurvivalSilverLevelRewardClaimed, - SurvivalGoldLevelRewardClaimed = other.SurvivalGoldLevelRewardClaimed, - ActiveGoldPass = other.ActiveGoldPass, - Online = other.Online, - Permissions = other.AdminPermissions.ToString(), - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.Account?(Account? other) { - if (other == null) { - return null; - } - - return new Maple2.Model.Game.Account { - LastModified = other.LastModified, - Id = other.Id, - Username = other.Username, - MachineId = other.MachineId, - MaxCharacters = other.MaxCharacters, - PrestigeLevel = other.PrestigeLevel, - PrestigeLevelsGained = other.PrestigeLevelsGained, - PrestigeExp = other.PrestigeExp, - PrestigeCurrentExp = other.PrestigeCurrentExp, - PrestigeRewardsClaimed = other.PrestigeRewardsClaimed, - PrestigeMissions = other.PrestigeMissions.Select(mission => new Maple2.Model.Game.PrestigeMission(mission.Id) { - GainedLevels = mission.GainedLevels, - Awarded = mission.Awarded, - }).ToList(), - PremiumTime = other.PremiumTime, - PremiumRewardsClaimed = other.PremiumRewardsClaimed, - MesoMarketListed = other.MarketLimits.MesoListed, - MesoMarketPurchased = other.MarketLimits.MesoPurchased, - SurvivalLevel = other.SurvivalLevel, - SurvivalExp = other.SurvivalExp, - SurvivalSilverLevelRewardClaimed = other.SurvivalSilverLevelRewardClaimed, - SurvivalGoldLevelRewardClaimed = other.SurvivalGoldLevelRewardClaimed, - ActiveGoldPass = other.ActiveGoldPass, - Online = other.Online, - AdminPermissions = Enum.TryParse(other.Permissions, true, out AdminPermissions permissions) ? permissions : AdminPermissions.None, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("account"); - builder.HasKey(account => account.Id); - builder.Property(account => account.Username).IsRequired(); - builder.HasIndex(account => account.Username).IsUnique(); - builder.Property(account => account.Password).IsRequired().HasMaxLength(255).HasColumnType("varchar(255)"); - builder.Property(account => account.MaxCharacters).HasDefaultValue(Constant.DefaultMaxCharacters); - builder.HasMany(account => account.Characters); - builder.Property(account => account.Currency).HasJsonConversion().IsRequired(); - builder.Property(account => account.MarketLimits).HasJsonConversion().IsRequired(); - builder.Property(account => account.PremiumRewardsClaimed).HasJsonConversion(); - builder.Property(account => account.PrestigeMissions).HasJsonConversion(); - builder.Property(account => account.PrestigeRewardsClaimed).HasJsonConversion(); - - builder.Property(account => account.LastModified).IsRowVersion(); - IMutableProperty creationTime = builder.Property(account => account.CreationTime) - .ValueGeneratedOnAdd().Metadata; - creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); - } -} - -internal class AccountCurrency { - public long Meret { get; set; } - public long GameMeret { get; set; } - public long MesoToken { get; set; } -} - -internal class MarketLimits { - public int MesoListed { get; set; } - public int MesoPurchased { get; set; } -} - -internal class PrestigeMission { - public long Id { get; set; } - public long GainedLevels { get; set; } - public bool Awarded { get; set; } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class Account { + public long Id { get; set; } + public required string Username { get; set; } + public string Password { get; set; } + public Guid MachineId { get; set; } + public int MaxCharacters { get; set; } + public int PrestigeLevel { get; set; } + public int PrestigeLevelsGained { get; set; } + public long PrestigeExp { get; set; } + public long PrestigeCurrentExp { get; set; } + public IList PrestigeMissions { get; set; } + public IList PrestigeRewardsClaimed { get; set; } + public long PremiumTime { get; set; } + public IList PremiumRewardsClaimed { get; set; } // TODO: clear list on daily reset + public required AccountCurrency Currency { get; set; } + public required MarketLimits MarketLimits { get; set; } + + public int SurvivalLevel { get; set; } + public long SurvivalExp { get; set; } + public int SurvivalSilverLevelRewardClaimed { get; set; } + public int SurvivalGoldLevelRewardClaimed { get; set; } + public bool ActiveGoldPass { get; set; } + + public DateTime CreationTime { get; set; } + public DateTime LastModified { get; set; } + + public bool Online { get; set; } + public string Permissions { get; set; } + + public ICollection? Characters { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Account?(Maple2.Model.Game.Account? other) { + if (other == null) { + return null; + } + + return new Account { + LastModified = other.LastModified, + Id = other.Id, + Username = other.Username, + MachineId = other.MachineId, + MaxCharacters = other.MaxCharacters, + PrestigeLevel = other.PrestigeLevel, + PrestigeLevelsGained = other.PrestigeLevelsGained, + PrestigeExp = other.PrestigeExp, + PrestigeCurrentExp = other.PrestigeCurrentExp, + PrestigeRewardsClaimed = other.PrestigeRewardsClaimed, + PrestigeMissions = other.PrestigeMissions.Select(mission => new PrestigeMission { + Id = mission.Id, + GainedLevels = mission.GainedLevels, + Awarded = mission.Awarded, + }).ToList(), + PremiumTime = other.PremiumTime, + PremiumRewardsClaimed = other.PremiumRewardsClaimed, + Currency = new AccountCurrency(), + MarketLimits = new MarketLimits { + MesoListed = other.MesoMarketListed, + MesoPurchased = other.MesoMarketPurchased, + }, + SurvivalLevel = other.SurvivalLevel, + SurvivalExp = other.SurvivalExp, + SurvivalSilverLevelRewardClaimed = other.SurvivalSilverLevelRewardClaimed, + SurvivalGoldLevelRewardClaimed = other.SurvivalGoldLevelRewardClaimed, + ActiveGoldPass = other.ActiveGoldPass, + Online = other.Online, + Permissions = other.AdminPermissions.ToString(), + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.Account?(Account? other) { + if (other == null) { + return null; + } + + return new Maple2.Model.Game.Account { + LastModified = other.LastModified, + Id = other.Id, + Username = other.Username, + MachineId = other.MachineId, + MaxCharacters = other.MaxCharacters, + PrestigeLevel = other.PrestigeLevel, + PrestigeLevelsGained = other.PrestigeLevelsGained, + PrestigeExp = other.PrestigeExp, + PrestigeCurrentExp = other.PrestigeCurrentExp, + PrestigeRewardsClaimed = other.PrestigeRewardsClaimed, + PrestigeMissions = other.PrestigeMissions.Select(mission => new Maple2.Model.Game.PrestigeMission(mission.Id) { + GainedLevels = mission.GainedLevels, + Awarded = mission.Awarded, + }).ToList(), + PremiumTime = other.PremiumTime, + PremiumRewardsClaimed = other.PremiumRewardsClaimed, + MesoMarketListed = other.MarketLimits.MesoListed, + MesoMarketPurchased = other.MarketLimits.MesoPurchased, + SurvivalLevel = other.SurvivalLevel, + SurvivalExp = other.SurvivalExp, + SurvivalSilverLevelRewardClaimed = other.SurvivalSilverLevelRewardClaimed, + SurvivalGoldLevelRewardClaimed = other.SurvivalGoldLevelRewardClaimed, + ActiveGoldPass = other.ActiveGoldPass, + Online = other.Online, + AdminPermissions = Enum.TryParse(other.Permissions, true, out AdminPermissions permissions) ? permissions : AdminPermissions.None, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("account"); + builder.HasKey(account => account.Id); + builder.Property(account => account.Username).IsRequired(); + builder.HasIndex(account => account.Username).IsUnique(); + builder.Property(account => account.Password).IsRequired().HasMaxLength(255).HasColumnType("varchar(255)"); + builder.Property(account => account.MaxCharacters).HasDefaultValue(Constant.DefaultMaxCharacters); + builder.HasMany(account => account.Characters); + builder.Property(account => account.Currency).HasJsonConversion().IsRequired(); + builder.Property(account => account.MarketLimits).HasJsonConversion().IsRequired(); + builder.Property(account => account.PremiumRewardsClaimed).HasJsonConversion(); + builder.Property(account => account.PrestigeMissions).HasJsonConversion(); + builder.Property(account => account.PrestigeRewardsClaimed).HasJsonConversion(); + + builder.Property(account => account.LastModified).IsRowVersion(); + IMutableProperty creationTime = builder.Property(account => account.CreationTime) + .ValueGeneratedOnAdd().Metadata; + creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + } +} + +internal class AccountCurrency { + public long Meret { get; set; } + public long GameMeret { get; set; } + public long MesoToken { get; set; } +} + +internal class MarketLimits { + public int MesoListed { get; set; } + public int MesoPurchased { get; set; } +} + +internal class PrestigeMission { + public long Id { get; set; } + public long GainedLevels { get; set; } + public bool Awarded { get; set; } +} diff --git a/Maple2.Database/Model/Achievement.cs b/Maple2.Database/Model/Achievement.cs index 289da4c60..e96b88fba 100644 --- a/Maple2.Database/Model/Achievement.cs +++ b/Maple2.Database/Model/Achievement.cs @@ -1,56 +1,56 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class Achievement { - public long OwnerId { get; set; } - public int Id { get; set; } - - public int CompletedCount { get; set; } - public int CurrentGrade { get; set; } - public int RewardGrade { get; set; } - public bool Favorite { get; set; } - public long Counter { get; set; } - public AchievementCategory Category { get; set; } - public required IDictionary Grades { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Achievement?(Maple2.Model.Game.Achievement? other) { - if (other == null) { - return null; - } - return new Achievement { - Id = other.Id, - CompletedCount = other.Grades.Count, - CurrentGrade = other.CurrentGrade, - RewardGrade = other.RewardGrade, - Favorite = other.Favorite, - Counter = other.Counter, - Category = other.Category, - Grades = other.Grades, - }; - } - - // Use explicit Convert() here because we need metadata to construct Achievement. - public Maple2.Model.Game.Achievement Convert(AchievementMetadata metadata) { - return new Maple2.Model.Game.Achievement(metadata) { - CurrentGrade = CurrentGrade, - RewardGrade = RewardGrade, - Favorite = Favorite, - Counter = Counter, - Category = Category, - Grades = Grades, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("achievement"); - builder.HasKey(achieve => new { achieve.OwnerId, achieve.Id }); - builder.Property(achieve => achieve.Grades).HasJsonConversion().IsRequired(); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class Achievement { + public long OwnerId { get; set; } + public int Id { get; set; } + + public int CompletedCount { get; set; } + public int CurrentGrade { get; set; } + public int RewardGrade { get; set; } + public bool Favorite { get; set; } + public long Counter { get; set; } + public AchievementCategory Category { get; set; } + public required IDictionary Grades { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Achievement?(Maple2.Model.Game.Achievement? other) { + if (other == null) { + return null; + } + return new Achievement { + Id = other.Id, + CompletedCount = other.Grades.Count, + CurrentGrade = other.CurrentGrade, + RewardGrade = other.RewardGrade, + Favorite = other.Favorite, + Counter = other.Counter, + Category = other.Category, + Grades = other.Grades, + }; + } + + // Use explicit Convert() here because we need metadata to construct Achievement. + public Maple2.Model.Game.Achievement Convert(AchievementMetadata metadata) { + return new Maple2.Model.Game.Achievement(metadata) { + CurrentGrade = CurrentGrade, + RewardGrade = RewardGrade, + Favorite = Favorite, + Counter = Counter, + Category = Category, + Grades = Grades, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("achievement"); + builder.HasKey(achieve => new { achieve.OwnerId, achieve.Id }); + builder.Property(achieve => achieve.Grades).HasJsonConversion().IsRequired(); + } +} diff --git a/Maple2.Database/Model/Buddy.cs b/Maple2.Database/Model/Buddy.cs index 461b79f9a..75ce406bd 100644 --- a/Maple2.Database/Model/Buddy.cs +++ b/Maple2.Database/Model/Buddy.cs @@ -1,58 +1,58 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class Buddy { - public long Id { get; set; } - public long OwnerId { get; set; } - public long BuddyId { get; set; } - public Character? BuddyCharacter { get; set; } - public BuddyType Type { get; set; } - public required string Message { get; set; } - - public DateTime LastModified { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Buddy?(Maple2.Model.Game.BuddyEntry? other) { - return other == null ? null : new Buddy { - Id = other.Id, - OwnerId = other.OwnerId, - BuddyId = other.BuddyId, - Type = other.Type, - Message = other.Message, - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.BuddyEntry?(Buddy? other) { - return other == null ? null : new Maple2.Model.Game.BuddyEntry { - Id = other.Id, - OwnerId = other.OwnerId, - BuddyId = other.BuddyId, - LastModified = other.LastModified.ToEpochSeconds(), - Type = other.Type, - Message = other.Message, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("buddy"); - builder.HasKey(buddy => buddy.Id); - - builder.HasOne() - .WithMany() - .HasForeignKey(buddy => buddy.OwnerId) - .IsRequired(); - builder.HasOne(buddy => buddy.BuddyCharacter) - .WithMany() - .HasForeignKey(buddy => buddy.BuddyId) - .IsRequired(); - - builder.Property(buddy => buddy.LastModified) - .ValueGeneratedOnAddOrUpdate(); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class Buddy { + public long Id { get; set; } + public long OwnerId { get; set; } + public long BuddyId { get; set; } + public Character? BuddyCharacter { get; set; } + public BuddyType Type { get; set; } + public required string Message { get; set; } + + public DateTime LastModified { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Buddy?(Maple2.Model.Game.BuddyEntry? other) { + return other == null ? null : new Buddy { + Id = other.Id, + OwnerId = other.OwnerId, + BuddyId = other.BuddyId, + Type = other.Type, + Message = other.Message, + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.BuddyEntry?(Buddy? other) { + return other == null ? null : new Maple2.Model.Game.BuddyEntry { + Id = other.Id, + OwnerId = other.OwnerId, + BuddyId = other.BuddyId, + LastModified = other.LastModified.ToEpochSeconds(), + Type = other.Type, + Message = other.Message, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("buddy"); + builder.HasKey(buddy => buddy.Id); + + builder.HasOne() + .WithMany() + .HasForeignKey(buddy => buddy.OwnerId) + .IsRequired(); + builder.HasOne(buddy => buddy.BuddyCharacter) + .WithMany() + .HasForeignKey(buddy => buddy.BuddyId) + .IsRequired(); + + builder.Property(buddy => buddy.LastModified) + .ValueGeneratedOnAddOrUpdate(); + } +} diff --git a/Maple2.Database/Model/Character.cs b/Maple2.Database/Model/Character.cs index 880bbe399..369705f60 100644 --- a/Maple2.Database/Model/Character.cs +++ b/Maple2.Database/Model/Character.cs @@ -1,173 +1,173 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Common; -using Maple2.Model.Enum; -using Maple2.Model.Game; -using Maple2.Tools; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class Character { - public long AccountId { get; set; } - public long Id { get; set; } - public required string Name { get; set; } - public Gender Gender { get; set; } - public Job Job { get; set; } - public short Level { get; set; } - public SkinColor SkinColor { get; set; } - public int MapId { get; set; } - public int ReturnMapId { get; set; } - public short Channel { get; set; } - public short ReturnChannel { get; set; } - public MentorRole MentorRole { get; set; } - public required Experience Experience { get; set; } - public required Profile Profile { get; set; } - public required Cooldown Cooldown { get; set; } - public required CharacterCurrency Currency { get; set; } - public required Mastery Mastery { get; set; } - public DateTime DeleteTime { get; set; } - public DateTime CreationTime { get; set; } - public DateTime LastModified { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Character?(Maple2.Model.Game.Character? other) { - return other == null ? null : new Character { - LastModified = other.LastModified, - AccountId = other.AccountId, - Id = other.Id, - Name = other.Name, - Gender = other.Gender, - Job = other.Job, - Level = other.Level, - SkinColor = other.SkinColor, - MapId = other.MapId, - ReturnMapId = other.ReturnMaps.Peek(), - Experience = new Experience { - Exp = other.Exp, - RestExp = other.RestExp, - }, - Profile = new Profile { - Motto = other.Motto, - Picture = other.Picture, - Title = other.Title, - Insignia = other.Insignia, - }, - Cooldown = new Cooldown { - Doctor = other.DoctorCooldown, - Storage = other.StorageCooldown, - }, - Currency = new CharacterCurrency(), - Mastery = new Mastery() { - Alchemy = other.Mastery.Alchemy, - Cooking = other.Mastery.Cooking, - Farming = other.Mastery.Farming, - Fishing = other.Mastery.Fishing, - Foraging = other.Mastery.Foraging, - Handicrafts = other.Mastery.Handicrafts, - Smithing = other.Mastery.Smithing, - Instrument = other.Mastery.Instrument, - Mining = other.Mastery.Mining, - PetTaming = other.Mastery.PetTaming, - Ranching = other.Mastery.Ranching, - }, - DeleteTime = other.DeleteTime.FromEpochSeconds(), - Channel = other.Channel, - ReturnChannel = other.ReturnChannel, - MentorRole = other.MentorRole, - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.Character?(Character? other) { - return other == null ? null : new Maple2.Model.Game.Character { - LastModified = other.LastModified, - LastOnlineTime = other.LastModified.ToEpochSeconds(), - AccountId = other.AccountId, - Id = other.Id, - Name = other.Name, - CreationTime = other.CreationTime.ToEpochSeconds(), - Gender = other.Gender, - Job = other.Job, - Level = other.Level, - SkinColor = other.SkinColor, - Exp = other.Experience.Exp, - RestExp = other.Experience.RestExp, - MapId = other.MapId, - ReturnMaps = new LimitedStack(3, other.ReturnMapId), - Mastery = other.Mastery, - Motto = other.Profile.Motto, - Picture = other.Profile.Picture, - Title = other.Profile.Title, - Insignia = other.Profile.Insignia, - DoctorCooldown = other.Cooldown.Doctor, - StorageCooldown = other.Cooldown.Storage, - DeleteTime = other.DeleteTime.ToEpochSeconds(), - Channel = other.Channel, - ReturnChannel = other.ReturnChannel, - MentorRole = other.MentorRole, - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator CharacterInfo?(Character? other) { - return other == null ? null : new CharacterInfo(other.AccountId, other.Id, other.Name, other.Profile.Motto, other.Profile.Picture, other.Gender, other.Job, other.Level) { - MapId = other.MapId, - Channel = other.Channel, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("character"); - builder.HasKey(character => character.Id); - builder.HasOne() - .WithMany(account => account.Characters) - .HasForeignKey(character => character.AccountId); - builder.HasIndex(character => character.Name).IsUnique(); - builder.Property(character => character.Level) - .HasDefaultValue(1); - builder.Property(character => character.SkinColor).HasJsonConversion().IsRequired(); - builder.Property(character => character.Experience).HasJsonConversion().IsRequired(); - builder.Property(character => character.Profile).HasJsonConversion().IsRequired(); - builder.Property(character => character.Cooldown).HasJsonConversion().IsRequired(); - builder.Property(character => character.Currency).HasJsonConversion().IsRequired(); - builder.Property(character => character.Mastery).HasJsonConversion().IsRequired(); - - builder.Property(character => character.LastModified).IsRowVersion(); - IMutableProperty creationTime = builder.Property(character => character.CreationTime) - .ValueGeneratedOnAdd().Metadata; - creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); - } -} - -internal class Experience { - public long Exp { get; set; } - public long RestExp { get; set; } -} - -internal class Profile { - public required string Motto { get; set; } - public required string Picture { get; set; } - public int Title { get; set; } - public short Insignia { get; set; } -} - -internal class Cooldown { - public long Storage { get; set; } - public long Doctor { get; set; } -} - -internal class CharacterCurrency { - public long Meso { get; set; } - public long EventMeret { get; set; } - public long ValorToken { get; set; } - public long Treva { get; set; } - public long Rue { get; set; } - public long HaviFruit { get; set; } - public long ReverseCoin { get; set; } - public long MentorToken { get; set; } - public long MenteeToken { get; set; } - public long StarPoint { get; set; } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Common; +using Maple2.Model.Enum; +using Maple2.Model.Game; +using Maple2.Tools; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class Character { + public long AccountId { get; set; } + public long Id { get; set; } + public required string Name { get; set; } + public Gender Gender { get; set; } + public Job Job { get; set; } + public short Level { get; set; } + public SkinColor SkinColor { get; set; } + public int MapId { get; set; } + public int ReturnMapId { get; set; } + public short Channel { get; set; } + public short ReturnChannel { get; set; } + public MentorRole MentorRole { get; set; } + public required Experience Experience { get; set; } + public required Profile Profile { get; set; } + public required Cooldown Cooldown { get; set; } + public required CharacterCurrency Currency { get; set; } + public required Mastery Mastery { get; set; } + public DateTime DeleteTime { get; set; } + public DateTime CreationTime { get; set; } + public DateTime LastModified { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Character?(Maple2.Model.Game.Character? other) { + return other == null ? null : new Character { + LastModified = other.LastModified, + AccountId = other.AccountId, + Id = other.Id, + Name = other.Name, + Gender = other.Gender, + Job = other.Job, + Level = other.Level, + SkinColor = other.SkinColor, + MapId = other.MapId, + ReturnMapId = other.ReturnMaps.Peek(), + Experience = new Experience { + Exp = other.Exp, + RestExp = other.RestExp, + }, + Profile = new Profile { + Motto = other.Motto, + Picture = other.Picture, + Title = other.Title, + Insignia = other.Insignia, + }, + Cooldown = new Cooldown { + Doctor = other.DoctorCooldown, + Storage = other.StorageCooldown, + }, + Currency = new CharacterCurrency(), + Mastery = new Mastery() { + Alchemy = other.Mastery.Alchemy, + Cooking = other.Mastery.Cooking, + Farming = other.Mastery.Farming, + Fishing = other.Mastery.Fishing, + Foraging = other.Mastery.Foraging, + Handicrafts = other.Mastery.Handicrafts, + Smithing = other.Mastery.Smithing, + Instrument = other.Mastery.Instrument, + Mining = other.Mastery.Mining, + PetTaming = other.Mastery.PetTaming, + Ranching = other.Mastery.Ranching, + }, + DeleteTime = other.DeleteTime.FromEpochSeconds(), + Channel = other.Channel, + ReturnChannel = other.ReturnChannel, + MentorRole = other.MentorRole, + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.Character?(Character? other) { + return other == null ? null : new Maple2.Model.Game.Character { + LastModified = other.LastModified, + LastOnlineTime = other.LastModified.ToEpochSeconds(), + AccountId = other.AccountId, + Id = other.Id, + Name = other.Name, + CreationTime = other.CreationTime.ToEpochSeconds(), + Gender = other.Gender, + Job = other.Job, + Level = other.Level, + SkinColor = other.SkinColor, + Exp = other.Experience.Exp, + RestExp = other.Experience.RestExp, + MapId = other.MapId, + ReturnMaps = new LimitedStack(3, other.ReturnMapId), + Mastery = other.Mastery, + Motto = other.Profile.Motto, + Picture = other.Profile.Picture, + Title = other.Profile.Title, + Insignia = other.Profile.Insignia, + DoctorCooldown = other.Cooldown.Doctor, + StorageCooldown = other.Cooldown.Storage, + DeleteTime = other.DeleteTime.ToEpochSeconds(), + Channel = other.Channel, + ReturnChannel = other.ReturnChannel, + MentorRole = other.MentorRole, + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator CharacterInfo?(Character? other) { + return other == null ? null : new CharacterInfo(other.AccountId, other.Id, other.Name, other.Profile.Motto, other.Profile.Picture, other.Gender, other.Job, other.Level) { + MapId = other.MapId, + Channel = other.Channel, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("character"); + builder.HasKey(character => character.Id); + builder.HasOne() + .WithMany(account => account.Characters) + .HasForeignKey(character => character.AccountId); + builder.HasIndex(character => character.Name).IsUnique(); + builder.Property(character => character.Level) + .HasDefaultValue(1); + builder.Property(character => character.SkinColor).HasJsonConversion().IsRequired(); + builder.Property(character => character.Experience).HasJsonConversion().IsRequired(); + builder.Property(character => character.Profile).HasJsonConversion().IsRequired(); + builder.Property(character => character.Cooldown).HasJsonConversion().IsRequired(); + builder.Property(character => character.Currency).HasJsonConversion().IsRequired(); + builder.Property(character => character.Mastery).HasJsonConversion().IsRequired(); + + builder.Property(character => character.LastModified).IsRowVersion(); + IMutableProperty creationTime = builder.Property(character => character.CreationTime) + .ValueGeneratedOnAdd().Metadata; + creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + } +} + +internal class Experience { + public long Exp { get; set; } + public long RestExp { get; set; } +} + +internal class Profile { + public required string Motto { get; set; } + public required string Picture { get; set; } + public int Title { get; set; } + public short Insignia { get; set; } +} + +internal class Cooldown { + public long Storage { get; set; } + public long Doctor { get; set; } +} + +internal class CharacterCurrency { + public long Meso { get; set; } + public long EventMeret { get; set; } + public long ValorToken { get; set; } + public long Treva { get; set; } + public long Rue { get; set; } + public long HaviFruit { get; set; } + public long ReverseCoin { get; set; } + public long MentorToken { get; set; } + public long MenteeToken { get; set; } + public long StarPoint { get; set; } +} diff --git a/Maple2.Database/Model/CharacterConfig.cs b/Maple2.Database/Model/CharacterConfig.cs index 9efdfa907..8fd1a0be2 100644 --- a/Maple2.Database/Model/CharacterConfig.cs +++ b/Maple2.Database/Model/CharacterConfig.cs @@ -1,137 +1,137 @@ -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Maple2.Model.Game; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class CharacterConfig { - public long CharacterId { get; set; } - public IList? KeyBinds { get; set; } - public IList? HotBars { get; set; } - public IList? SkillMacros { get; set; } - public IList? Wardrobes { get; set; } - public IDictionary? StatAllocation { get; set; } - public IDictionary? StatPoints { get; set; } - public IList? SkillPoint { get; set; } - public SkillBook? SkillBook { get; set; } - public IList? FavoriteStickers { get; set; } - public IList? FavoriteDesigners { get; set; } - public IDictionary? Lapenshards { get; set; } - public int InstantRevivalCount { get; set; } - public IDictionary? GatheringCounts { get; set; } - public IDictionary? GuideRecords { get; set; } - public int ExplorationProgress { get; set; } - - public DateTime LastModified { get; set; } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("character-config"); - builder.HasKey(config => config.CharacterId); - builder.OneToOne() - .HasForeignKey(config => config.CharacterId); - builder.Property(config => config.KeyBinds).HasJsonConversion(); - builder.Property(config => config.HotBars).HasJsonConversion(); - builder.Property(config => config.SkillMacros).HasJsonConversion(); - builder.Property(config => config.SkillPoint).HasJsonConversion(); - builder.Property(config => config.Wardrobes).HasJsonConversion(); - builder.Property(config => config.StatAllocation).HasJsonConversion(); - builder.Property(config => config.StatPoints).HasJsonConversion(); - - builder.OwnsOne(config => config.SkillBook) - .Property(skillBook => skillBook.MaxSkillTabs) - .HasDefaultValue(1); - builder.OwnsOne(config => config.SkillBook) - .HasOne() - .WithOne() - .HasPrincipalKey(skillTab => skillTab.Id) - .HasForeignKey(skillBook => skillBook.ActiveSkillTabId); - builder.Property(config => config.FavoriteStickers).HasJsonConversion(); - builder.Property(config => config.FavoriteDesigners).HasJsonConversion(); - builder.Property(config => config.Lapenshards).HasJsonConversion(); - builder.Property(config => config.GatheringCounts).HasJsonConversion(); - builder.Property(config => config.GuideRecords).HasJsonConversion(); - - builder.Property(unlock => unlock.LastModified).IsRowVersion(); - } -} - -internal class SkillMacro { - public required string Name { get; set; } - public long KeyId { get; set; } - public required IList Skills { get; set; } - - public static implicit operator SkillMacro(Maple2.Model.Game.SkillMacro? other) { - return other == null ? new SkillMacro { - Name = string.Empty, - Skills = [], - } : new SkillMacro { - Name = other.Name, - KeyId = other.KeyId, - Skills = other.Skills.ToList(), - }; - } - - public static implicit operator Maple2.Model.Game.SkillMacro(SkillMacro? other) { - return other == null ? new Maple2.Model.Game.SkillMacro(string.Empty, 0) : - new Maple2.Model.Game.SkillMacro(other.Name, other.KeyId, other.Skills.ToHashSet()); - } -} - -internal class Wardrobe { - public int Type { get; set; } - public int KeyId { get; set; } - public required string Name { get; set; } - public required Dictionary Equips { get; set; } - - public static implicit operator Wardrobe(Maple2.Model.Game.Wardrobe? other) { - return other == null ? new Wardrobe { - Name = string.Empty, - Equips = new Dictionary(), - } : new Wardrobe { - Type = other.Type, - Name = other.Name, - KeyId = other.KeyId, - Equips = other.Equips.ToDictionary( - entry => entry.Key, - entry => new Equip { - ItemId = entry.Value.ItemId, - ItemUid = entry.Value.ItemUid, - Rarity = entry.Value.Rarity, - } - ), - }; - } - - public static implicit operator Maple2.Model.Game.Wardrobe(Wardrobe? other) { - if (other == null) { - return new Maple2.Model.Game.Wardrobe(0, string.Empty); - } - - var wardrobe = new Maple2.Model.Game.Wardrobe(other.Type, other.Name) { - KeyId = other.KeyId, - }; - foreach ((EquipSlot slot, Equip equip) in other.Equips) { - wardrobe.Equips[slot] = new Maple2.Model.Game.Wardrobe.Equip(equip.ItemUid, equip.ItemId, slot, equip.Rarity); - } - return wardrobe; - } - - internal class Equip { - public long ItemUid { get; set; } - public int ItemId { get; set; } - public int Rarity { get; set; } - } -} - -internal class SkillBook { - public int MaxSkillTabs { get; set; } - public long ActiveSkillTabId { get; set; } -} - -internal class SkillPoint { - public SkillPointSource Source { get; set; } - public short Rank { get; set; } - public int Points { get; set; } -} +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Maple2.Model.Game; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class CharacterConfig { + public long CharacterId { get; set; } + public IList? KeyBinds { get; set; } + public IList? HotBars { get; set; } + public IList? SkillMacros { get; set; } + public IList? Wardrobes { get; set; } + public IDictionary? StatAllocation { get; set; } + public IDictionary? StatPoints { get; set; } + public IList? SkillPoint { get; set; } + public SkillBook? SkillBook { get; set; } + public IList? FavoriteStickers { get; set; } + public IList? FavoriteDesigners { get; set; } + public IDictionary? Lapenshards { get; set; } + public int InstantRevivalCount { get; set; } + public IDictionary? GatheringCounts { get; set; } + public IDictionary? GuideRecords { get; set; } + public int ExplorationProgress { get; set; } + + public DateTime LastModified { get; set; } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("character-config"); + builder.HasKey(config => config.CharacterId); + builder.OneToOne() + .HasForeignKey(config => config.CharacterId); + builder.Property(config => config.KeyBinds).HasJsonConversion(); + builder.Property(config => config.HotBars).HasJsonConversion(); + builder.Property(config => config.SkillMacros).HasJsonConversion(); + builder.Property(config => config.SkillPoint).HasJsonConversion(); + builder.Property(config => config.Wardrobes).HasJsonConversion(); + builder.Property(config => config.StatAllocation).HasJsonConversion(); + builder.Property(config => config.StatPoints).HasJsonConversion(); + + builder.OwnsOne(config => config.SkillBook) + .Property(skillBook => skillBook.MaxSkillTabs) + .HasDefaultValue(1); + builder.OwnsOne(config => config.SkillBook) + .HasOne() + .WithOne() + .HasPrincipalKey(skillTab => skillTab.Id) + .HasForeignKey(skillBook => skillBook.ActiveSkillTabId); + builder.Property(config => config.FavoriteStickers).HasJsonConversion(); + builder.Property(config => config.FavoriteDesigners).HasJsonConversion(); + builder.Property(config => config.Lapenshards).HasJsonConversion(); + builder.Property(config => config.GatheringCounts).HasJsonConversion(); + builder.Property(config => config.GuideRecords).HasJsonConversion(); + + builder.Property(unlock => unlock.LastModified).IsRowVersion(); + } +} + +internal class SkillMacro { + public required string Name { get; set; } + public long KeyId { get; set; } + public required IList Skills { get; set; } + + public static implicit operator SkillMacro(Maple2.Model.Game.SkillMacro? other) { + return other == null ? new SkillMacro { + Name = string.Empty, + Skills = [], + } : new SkillMacro { + Name = other.Name, + KeyId = other.KeyId, + Skills = other.Skills.ToList(), + }; + } + + public static implicit operator Maple2.Model.Game.SkillMacro(SkillMacro? other) { + return other == null ? new Maple2.Model.Game.SkillMacro(string.Empty, 0) : + new Maple2.Model.Game.SkillMacro(other.Name, other.KeyId, other.Skills.ToHashSet()); + } +} + +internal class Wardrobe { + public int Type { get; set; } + public int KeyId { get; set; } + public required string Name { get; set; } + public required Dictionary Equips { get; set; } + + public static implicit operator Wardrobe(Maple2.Model.Game.Wardrobe? other) { + return other == null ? new Wardrobe { + Name = string.Empty, + Equips = new Dictionary(), + } : new Wardrobe { + Type = other.Type, + Name = other.Name, + KeyId = other.KeyId, + Equips = other.Equips.ToDictionary( + entry => entry.Key, + entry => new Equip { + ItemId = entry.Value.ItemId, + ItemUid = entry.Value.ItemUid, + Rarity = entry.Value.Rarity, + } + ), + }; + } + + public static implicit operator Maple2.Model.Game.Wardrobe(Wardrobe? other) { + if (other == null) { + return new Maple2.Model.Game.Wardrobe(0, string.Empty); + } + + var wardrobe = new Maple2.Model.Game.Wardrobe(other.Type, other.Name) { + KeyId = other.KeyId, + }; + foreach ((EquipSlot slot, Equip equip) in other.Equips) { + wardrobe.Equips[slot] = new Maple2.Model.Game.Wardrobe.Equip(equip.ItemUid, equip.ItemId, slot, equip.Rarity); + } + return wardrobe; + } + + internal class Equip { + public long ItemUid { get; set; } + public int ItemId { get; set; } + public int Rarity { get; set; } + } +} + +internal class SkillBook { + public int MaxSkillTabs { get; set; } + public long ActiveSkillTabId { get; set; } +} + +internal class SkillPoint { + public SkillPointSource Source { get; set; } + public short Rank { get; set; } + public int Points { get; set; } +} diff --git a/Maple2.Database/Model/CharacterUnlock.cs b/Maple2.Database/Model/CharacterUnlock.cs index 42f3cd13a..e2792412c 100644 --- a/Maple2.Database/Model/CharacterUnlock.cs +++ b/Maple2.Database/Model/CharacterUnlock.cs @@ -1,166 +1,166 @@ -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Maple2.Tools.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class CharacterUnlock { - public long CharacterId { get; set; } - public required ISet Maps { get; set; } - public required ISet Taxis { get; set; } - public required ISet Titles { get; set; } - public required IList Emotes { get; set; } - public required IDictionary StickerSets { get; set; } - public required IDictionary MasteryRewardsClaimed { get; set; } - public required IDictionary DungeonRankRewards { get; set; } - public required IDictionary Pets { get; set; } - public required IList FishAlbum { get; set; } - public required ISet InteractedObjects { get; set; } - public required IDictionary CollectedItems { get; set; } - public required InventoryExpand Expand { get; set; } - public short HairSlotExpand { get; set; } - public DateTime LastModified { get; init; } - - public static implicit operator CharacterUnlock(Maple2.Model.Game.Unlock? other) { - return other == null ? new CharacterUnlock { - Maps = new SortedSet(), - Taxis = new SortedSet(), - Titles = new SortedSet(), - Emotes = new List(), - StickerSets = new Dictionary(), - MasteryRewardsClaimed = new Dictionary(), - Pets = new SortedDictionary(), - FishAlbum = new List(), - Expand = new InventoryExpand(), - InteractedObjects = new SortedSet(), - CollectedItems = new Dictionary(), - DungeonRankRewards = new Dictionary(), - } : new CharacterUnlock { - LastModified = other.LastModified, - Expand = new InventoryExpand { - Gear = other.Expand.GetValueOrDefault(InventoryType.Gear), - Outfit = other.Expand.GetValueOrDefault(InventoryType.Outfit), - Mount = other.Expand.GetValueOrDefault(InventoryType.Mount), - Catalyst = other.Expand.GetValueOrDefault(InventoryType.Catalyst), - FishingMusic = other.Expand.GetValueOrDefault(InventoryType.FishingMusic), - Quest = other.Expand.GetValueOrDefault(InventoryType.Quest), - Gemstone = other.Expand.GetValueOrDefault(InventoryType.Gemstone), - Misc = other.Expand.GetValueOrDefault(InventoryType.Misc), - LifeSkill = other.Expand.GetValueOrDefault(InventoryType.LifeSkill), - Pets = other.Expand.GetValueOrDefault(InventoryType.Pets), - Consumable = other.Expand.GetValueOrDefault(InventoryType.Consumable), - Currency = other.Expand.GetValueOrDefault(InventoryType.Currency), - Badge = other.Expand.GetValueOrDefault(InventoryType.Badge), - Lapenshard = other.Expand.GetValueOrDefault(InventoryType.Lapenshard), - Fragment = other.Expand.GetValueOrDefault(InventoryType.Fragment), - }, - HairSlotExpand = other.HairSlotExpand, - Maps = other.Maps, - Taxis = other.Taxis, - Titles = other.Titles, - Emotes = other.Emotes, - StickerSets = other.StickerSets, - MasteryRewardsClaimed = other.MasteryRewardsClaimed, - DungeonRankRewards = other.DungeonRankRewards.Values.Select(reward => reward).ToDictionary(reward => reward.Id), - Pets = other.Pets, - FishAlbum = other.FishAlbum.Values.Select(fish => fish).ToArray(), - InteractedObjects = other.InteractedObjects, - CollectedItems = other.CollectedItems, - }; - } - - public static implicit operator Maple2.Model.Game.Unlock(CharacterUnlock? other) { - if (other == null) { - return new Maple2.Model.Game.Unlock(); - } - - var unlock = new Maple2.Model.Game.Unlock { - LastModified = other.LastModified, - Expand = new Dictionary { - {InventoryType.Gear, other.Expand.Gear}, - {InventoryType.Outfit, other.Expand.Outfit}, - {InventoryType.Mount, other.Expand.Mount}, - {InventoryType.Catalyst, other.Expand.Catalyst}, - {InventoryType.FishingMusic, other.Expand.FishingMusic}, - {InventoryType.Quest, other.Expand.Quest}, - {InventoryType.Gemstone, other.Expand.Gemstone}, - {InventoryType.Misc, other.Expand.Misc}, - {InventoryType.LifeSkill, other.Expand.LifeSkill}, - {InventoryType.Pets, other.Expand.Pets}, - {InventoryType.Consumable, other.Expand.Consumable}, - {InventoryType.Currency, other.Expand.Currency}, - {InventoryType.Badge, other.Expand.Badge}, - {InventoryType.Lapenshard, other.Expand.Lapenshard}, - {InventoryType.Fragment, other.Expand.Fragment}, - }, - HairSlotExpand = other.HairSlotExpand, - }; - - unlock.Maps.UnionWith(other.Maps); - unlock.Taxis.UnionWith(other.Taxis); - unlock.Titles.UnionWith(other.Titles); - unlock.InteractedObjects.UnionWith(other.InteractedObjects); - - foreach (int emoteId in other.Emotes) { - unlock.Emotes.Add(emoteId); - } - foreach ((int groupId, long expiration) in other.StickerSets) { - unlock.StickerSets[groupId] = expiration; - } - foreach ((int rewardId, bool isClaimed) in other.MasteryRewardsClaimed) { - unlock.MasteryRewardsClaimed[rewardId] = isClaimed; - } - foreach ((int petId, short rarity) in other.Pets) { - unlock.Pets[petId] = rarity; - } - foreach (FishEntry entry in other.FishAlbum) { - unlock.FishAlbum[entry.Id] = entry; - } - foreach ((int itemId, byte quantity) in other.CollectedItems) { - unlock.CollectedItems[itemId] = quantity; - } - - return unlock; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("character-unlock"); - builder.HasKey(unlock => unlock.CharacterId); - builder.OneToOne() - .HasForeignKey(unlock => unlock.CharacterId); - builder.Property(unlock => unlock.Expand).HasJsonConversion().IsRequired(); - builder.Property(unlock => unlock.Maps).HasJsonConversion().IsRequired(); - builder.Property(unlock => unlock.Taxis).HasJsonConversion().IsRequired(); - builder.Property(unlock => unlock.Titles).HasJsonConversion().IsRequired(); - builder.Property(unlock => unlock.Emotes).HasJsonConversion().IsRequired(); - builder.Property(unlock => unlock.StickerSets).HasJsonConversion(); - builder.Property(unlock => unlock.MasteryRewardsClaimed).HasJsonConversion(); - builder.Property(unlock => unlock.Pets).HasJsonConversion().IsRequired(); - builder.Property(unlock => unlock.FishAlbum).HasJsonConversion().IsRequired(); - builder.Property(unlock => unlock.InteractedObjects).HasJsonConversion().IsRequired(); - builder.Property(unlock => unlock.CollectedItems).HasJsonConversion().IsRequired(); - builder.Property(unlock => unlock.DungeonRankRewards).HasJsonConversion().IsRequired(); - - builder.Property(unlock => unlock.LastModified).IsRowVersion(); - } -} - -internal class InventoryExpand { - public short Gear { get; set; } - public short Outfit { get; set; } - public short Mount { get; set; } - public short Catalyst { get; set; } - public short FishingMusic { get; set; } - public short Quest { get; set; } - public short Gemstone { get; set; } - public short Misc { get; set; } - public short LifeSkill { get; set; } - public short Pets { get; set; } - public short Consumable { get; set; } - public short Currency { get; set; } - public short Badge { get; set; } - public short Lapenshard { get; set; } - public short Fragment { get; set; } -} +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Maple2.Tools.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class CharacterUnlock { + public long CharacterId { get; set; } + public required ISet Maps { get; set; } + public required ISet Taxis { get; set; } + public required ISet Titles { get; set; } + public required IList Emotes { get; set; } + public required IDictionary StickerSets { get; set; } + public required IDictionary MasteryRewardsClaimed { get; set; } + public required IDictionary DungeonRankRewards { get; set; } + public required IDictionary Pets { get; set; } + public required IList FishAlbum { get; set; } + public required ISet InteractedObjects { get; set; } + public required IDictionary CollectedItems { get; set; } + public required InventoryExpand Expand { get; set; } + public short HairSlotExpand { get; set; } + public DateTime LastModified { get; init; } + + public static implicit operator CharacterUnlock(Maple2.Model.Game.Unlock? other) { + return other == null ? new CharacterUnlock { + Maps = new SortedSet(), + Taxis = new SortedSet(), + Titles = new SortedSet(), + Emotes = new List(), + StickerSets = new Dictionary(), + MasteryRewardsClaimed = new Dictionary(), + Pets = new SortedDictionary(), + FishAlbum = new List(), + Expand = new InventoryExpand(), + InteractedObjects = new SortedSet(), + CollectedItems = new Dictionary(), + DungeonRankRewards = new Dictionary(), + } : new CharacterUnlock { + LastModified = other.LastModified, + Expand = new InventoryExpand { + Gear = other.Expand.GetValueOrDefault(InventoryType.Gear), + Outfit = other.Expand.GetValueOrDefault(InventoryType.Outfit), + Mount = other.Expand.GetValueOrDefault(InventoryType.Mount), + Catalyst = other.Expand.GetValueOrDefault(InventoryType.Catalyst), + FishingMusic = other.Expand.GetValueOrDefault(InventoryType.FishingMusic), + Quest = other.Expand.GetValueOrDefault(InventoryType.Quest), + Gemstone = other.Expand.GetValueOrDefault(InventoryType.Gemstone), + Misc = other.Expand.GetValueOrDefault(InventoryType.Misc), + LifeSkill = other.Expand.GetValueOrDefault(InventoryType.LifeSkill), + Pets = other.Expand.GetValueOrDefault(InventoryType.Pets), + Consumable = other.Expand.GetValueOrDefault(InventoryType.Consumable), + Currency = other.Expand.GetValueOrDefault(InventoryType.Currency), + Badge = other.Expand.GetValueOrDefault(InventoryType.Badge), + Lapenshard = other.Expand.GetValueOrDefault(InventoryType.Lapenshard), + Fragment = other.Expand.GetValueOrDefault(InventoryType.Fragment), + }, + HairSlotExpand = other.HairSlotExpand, + Maps = other.Maps, + Taxis = other.Taxis, + Titles = other.Titles, + Emotes = other.Emotes, + StickerSets = other.StickerSets, + MasteryRewardsClaimed = other.MasteryRewardsClaimed, + DungeonRankRewards = other.DungeonRankRewards.Values.Select(reward => reward).ToDictionary(reward => reward.Id), + Pets = other.Pets, + FishAlbum = other.FishAlbum.Values.Select(fish => fish).ToArray(), + InteractedObjects = other.InteractedObjects, + CollectedItems = other.CollectedItems, + }; + } + + public static implicit operator Maple2.Model.Game.Unlock(CharacterUnlock? other) { + if (other == null) { + return new Maple2.Model.Game.Unlock(); + } + + var unlock = new Maple2.Model.Game.Unlock { + LastModified = other.LastModified, + Expand = new Dictionary { + {InventoryType.Gear, other.Expand.Gear}, + {InventoryType.Outfit, other.Expand.Outfit}, + {InventoryType.Mount, other.Expand.Mount}, + {InventoryType.Catalyst, other.Expand.Catalyst}, + {InventoryType.FishingMusic, other.Expand.FishingMusic}, + {InventoryType.Quest, other.Expand.Quest}, + {InventoryType.Gemstone, other.Expand.Gemstone}, + {InventoryType.Misc, other.Expand.Misc}, + {InventoryType.LifeSkill, other.Expand.LifeSkill}, + {InventoryType.Pets, other.Expand.Pets}, + {InventoryType.Consumable, other.Expand.Consumable}, + {InventoryType.Currency, other.Expand.Currency}, + {InventoryType.Badge, other.Expand.Badge}, + {InventoryType.Lapenshard, other.Expand.Lapenshard}, + {InventoryType.Fragment, other.Expand.Fragment}, + }, + HairSlotExpand = other.HairSlotExpand, + }; + + unlock.Maps.UnionWith(other.Maps); + unlock.Taxis.UnionWith(other.Taxis); + unlock.Titles.UnionWith(other.Titles); + unlock.InteractedObjects.UnionWith(other.InteractedObjects); + + foreach (int emoteId in other.Emotes) { + unlock.Emotes.Add(emoteId); + } + foreach ((int groupId, long expiration) in other.StickerSets) { + unlock.StickerSets[groupId] = expiration; + } + foreach ((int rewardId, bool isClaimed) in other.MasteryRewardsClaimed) { + unlock.MasteryRewardsClaimed[rewardId] = isClaimed; + } + foreach ((int petId, short rarity) in other.Pets) { + unlock.Pets[petId] = rarity; + } + foreach (FishEntry entry in other.FishAlbum) { + unlock.FishAlbum[entry.Id] = entry; + } + foreach ((int itemId, byte quantity) in other.CollectedItems) { + unlock.CollectedItems[itemId] = quantity; + } + + return unlock; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("character-unlock"); + builder.HasKey(unlock => unlock.CharacterId); + builder.OneToOne() + .HasForeignKey(unlock => unlock.CharacterId); + builder.Property(unlock => unlock.Expand).HasJsonConversion().IsRequired(); + builder.Property(unlock => unlock.Maps).HasJsonConversion().IsRequired(); + builder.Property(unlock => unlock.Taxis).HasJsonConversion().IsRequired(); + builder.Property(unlock => unlock.Titles).HasJsonConversion().IsRequired(); + builder.Property(unlock => unlock.Emotes).HasJsonConversion().IsRequired(); + builder.Property(unlock => unlock.StickerSets).HasJsonConversion(); + builder.Property(unlock => unlock.MasteryRewardsClaimed).HasJsonConversion(); + builder.Property(unlock => unlock.Pets).HasJsonConversion().IsRequired(); + builder.Property(unlock => unlock.FishAlbum).HasJsonConversion().IsRequired(); + builder.Property(unlock => unlock.InteractedObjects).HasJsonConversion().IsRequired(); + builder.Property(unlock => unlock.CollectedItems).HasJsonConversion().IsRequired(); + builder.Property(unlock => unlock.DungeonRankRewards).HasJsonConversion().IsRequired(); + + builder.Property(unlock => unlock.LastModified).IsRowVersion(); + } +} + +internal class InventoryExpand { + public short Gear { get; set; } + public short Outfit { get; set; } + public short Mount { get; set; } + public short Catalyst { get; set; } + public short FishingMusic { get; set; } + public short Quest { get; set; } + public short Gemstone { get; set; } + public short Misc { get; set; } + public short LifeSkill { get; set; } + public short Pets { get; set; } + public short Consumable { get; set; } + public short Currency { get; set; } + public short Badge { get; set; } + public short Lapenshard { get; set; } + public short Fragment { get; set; } +} diff --git a/Maple2.Database/Model/Club.cs b/Maple2.Database/Model/Club.cs index 3c43e1b38..df016b7b3 100644 --- a/Maple2.Database/Model/Club.cs +++ b/Maple2.Database/Model/Club.cs @@ -1,92 +1,92 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class Club { - public long Id { get; set; } - public required string Name { get; set; } - public ClubState State { get; set; } - public int BuffId { get; set; } - public DateTime CreationTime { get; set; } - public DateTime NameChangeCooldown { get; set; } - - public long LeaderId { get; set; } - public List? Members { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Club?(Maple2.Model.Game.Club.Club? other) { - return other == null ? null : new Club { - // CreationTime set by DB - Id = other.Id, - Name = other.Name, - LeaderId = other.Leader.Info.CharacterId, - BuffId = other.BuffId, - State = other.State, - NameChangeCooldown = other.NameChangeCooldown.FromEpochSeconds(), - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.Club.Club?(Club? other) { - return other == null ? null : new Maple2.Model.Game.Club.Club(other.Id, other.Name, other.LeaderId) { - NameChangeCooldown = other.NameChangeCooldown.ToEpochSeconds(), - CreationTime = other.CreationTime.ToEpochSeconds(), - BuffId = other.BuffId, - State = other.State, - // Leader and Members set separately - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("club"); - builder.HasKey(club => club.Id); - builder.HasIndex(club => club.Name).IsUnique(); - - builder.HasOne() - .WithMany() - .HasForeignKey(club => club.LeaderId) - .IsRequired(); - builder.HasMany(club => club.Members); - - IMutableProperty creationTime = builder.Property(club => club.CreationTime) - .ValueGeneratedOnAdd().Metadata; - creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); - } -} - -internal class ClubMember { - public long ClubId { get; set; } - public long CharacterId { get; set; } - public Character? Character { get; set; } - public DateTime CreationTime { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator ClubMember?(Maple2.Model.Game.Club.ClubMember? other) { - return other == null ? null : new ClubMember { - // CreationTime set by DB - CharacterId = other.Info.CharacterId, - ClubId = other.ClubId, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("club-member"); - builder.HasKey(member => new { member.ClubId, member.CharacterId }); - - builder.HasOne(member => member.Character) - .WithMany() - .HasForeignKey(member => member.CharacterId); - builder.HasOne() - .WithMany(club => club.Members) - .HasForeignKey(member => member.ClubId); - - IMutableProperty creationTime = builder.Property(member => member.CreationTime) - .ValueGeneratedOnAdd().Metadata; - creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class Club { + public long Id { get; set; } + public required string Name { get; set; } + public ClubState State { get; set; } + public int BuffId { get; set; } + public DateTime CreationTime { get; set; } + public DateTime NameChangeCooldown { get; set; } + + public long LeaderId { get; set; } + public List? Members { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Club?(Maple2.Model.Game.Club.Club? other) { + return other == null ? null : new Club { + // CreationTime set by DB + Id = other.Id, + Name = other.Name, + LeaderId = other.Leader.Info.CharacterId, + BuffId = other.BuffId, + State = other.State, + NameChangeCooldown = other.NameChangeCooldown.FromEpochSeconds(), + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.Club.Club?(Club? other) { + return other == null ? null : new Maple2.Model.Game.Club.Club(other.Id, other.Name, other.LeaderId) { + NameChangeCooldown = other.NameChangeCooldown.ToEpochSeconds(), + CreationTime = other.CreationTime.ToEpochSeconds(), + BuffId = other.BuffId, + State = other.State, + // Leader and Members set separately + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("club"); + builder.HasKey(club => club.Id); + builder.HasIndex(club => club.Name).IsUnique(); + + builder.HasOne() + .WithMany() + .HasForeignKey(club => club.LeaderId) + .IsRequired(); + builder.HasMany(club => club.Members); + + IMutableProperty creationTime = builder.Property(club => club.CreationTime) + .ValueGeneratedOnAdd().Metadata; + creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + } +} + +internal class ClubMember { + public long ClubId { get; set; } + public long CharacterId { get; set; } + public Character? Character { get; set; } + public DateTime CreationTime { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator ClubMember?(Maple2.Model.Game.Club.ClubMember? other) { + return other == null ? null : new ClubMember { + // CreationTime set by DB + CharacterId = other.Info.CharacterId, + ClubId = other.ClubId, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("club-member"); + builder.HasKey(member => new { member.ClubId, member.CharacterId }); + + builder.HasOne(member => member.Character) + .WithMany() + .HasForeignKey(member => member.CharacterId); + builder.HasOne() + .WithMany(club => club.Members) + .HasForeignKey(member => member.ClubId); + + IMutableProperty creationTime = builder.Property(member => member.CreationTime) + .ValueGeneratedOnAdd().Metadata; + creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + } +} diff --git a/Maple2.Database/Model/DungeonRankReward.cs b/Maple2.Database/Model/DungeonRankReward.cs index 1bb0b7b80..f797213fd 100644 --- a/Maple2.Database/Model/DungeonRankReward.cs +++ b/Maple2.Database/Model/DungeonRankReward.cs @@ -1,27 +1,27 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; - -namespace Maple2.Database.Model; - -internal class DungeonRankReward { - public int Id { get; set; } - public int RankClaimed { get; set; } - public DateTime ClaimTime { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator DungeonRankReward?(Maple2.Model.Game.Dungeon.DungeonRankReward? other) { - return other == null ? null : new DungeonRankReward { - Id = other.Id, - RankClaimed = other.RankClaimed, - ClaimTime = other.UpdatedTimestamp.FromEpochSeconds(), - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.Dungeon.DungeonRankReward?(DungeonRankReward? other) { - return other == null ? null : new Maple2.Model.Game.Dungeon.DungeonRankReward(other.Id) { - RankClaimed = other.RankClaimed, - UpdatedTimestamp = other.ClaimTime.ToEpochSeconds(), - }; - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; + +namespace Maple2.Database.Model; + +internal class DungeonRankReward { + public int Id { get; set; } + public int RankClaimed { get; set; } + public DateTime ClaimTime { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator DungeonRankReward?(Maple2.Model.Game.Dungeon.DungeonRankReward? other) { + return other == null ? null : new DungeonRankReward { + Id = other.Id, + RankClaimed = other.RankClaimed, + ClaimTime = other.UpdatedTimestamp.FromEpochSeconds(), + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.Dungeon.DungeonRankReward?(DungeonRankReward? other) { + return other == null ? null : new Maple2.Model.Game.Dungeon.DungeonRankReward(other.Id) { + RankClaimed = other.RankClaimed, + UpdatedTimestamp = other.ClaimTime.ToEpochSeconds(), + }; + } +} diff --git a/Maple2.Database/Model/DungeonRecord.cs b/Maple2.Database/Model/DungeonRecord.cs index a297ee5de..92e67bc8e 100644 --- a/Maple2.Database/Model/DungeonRecord.cs +++ b/Maple2.Database/Model/DungeonRecord.cs @@ -1,70 +1,70 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class DungeonRecord { - public int DungeonId { get; set; } - public long OwnerId { get; set; } - public DateTime ClearTime { get; init; } - public int TotalClears { get; init; } - public byte CurrentSubClears { get; set; } - public byte CurrentClears { get; set; } - public short LifetimeRecord { get; set; } - public short CurrentRecord { get; set; } - public byte ExtraCurrentSubClears { get; set; } - public byte ExtraCurrentClears { get; set; } - public DateTime DailyResetTime { get; set; } - public DateTime UnionCooldownTime { get; set; } - public DateTime CooldownTime { get; set; } - public DungeonRecordFlag Flag { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator DungeonRecord?(Maple2.Model.Game.Dungeon.DungeonRecord? other) { - return other == null ? null : new DungeonRecord { - DungeonId = other.DungeonId, - CurrentSubClears = other.UnionSubClears, - CurrentClears = other.UnionClears, - DailyResetTime = other.UnionSubCooldownTimestamp.FromEpochSeconds(), - UnionCooldownTime = other.UnionCooldownTimestamp.FromEpochSeconds(), - ClearTime = other.ClearTimestamp.FromEpochSeconds(), - CooldownTime = other.CooldownTimestamp.FromEpochSeconds(), - TotalClears = other.TotalClears, - LifetimeRecord = other.LifetimeRecord, - CurrentRecord = other.CurrentRecord, - ExtraCurrentSubClears = other.ExtraSubClears, - ExtraCurrentClears = other.ExtraClears, - Flag = other.Flag, - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.Dungeon.DungeonRecord?(DungeonRecord? other) { - return other == null ? null : new Maple2.Model.Game.Dungeon.DungeonRecord(other.DungeonId) { - UnionSubClears = other.CurrentSubClears, - UnionClears = other.CurrentClears, - UnionSubCooldownTimestamp = other.DailyResetTime.ToEpochSeconds(), - UnionCooldownTimestamp = other.UnionCooldownTime.ToEpochSeconds(), - ClearTimestamp = other.ClearTime.ToEpochSeconds(), - CooldownTimestamp = other.CooldownTime.ToEpochSeconds(), - TotalClears = other.TotalClears, - LifetimeRecord = other.LifetimeRecord, - CurrentRecord = other.CurrentRecord, - ExtraSubClears = other.ExtraCurrentSubClears, - ExtraClears = other.ExtraCurrentClears, - Flag = other.Flag, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("dungeon-record"); - builder.HasKey(record => new { record.OwnerId, record.DungeonId }); - builder.HasOne() - .WithMany() - .HasForeignKey(record => record.OwnerId); - - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class DungeonRecord { + public int DungeonId { get; set; } + public long OwnerId { get; set; } + public DateTime ClearTime { get; init; } + public int TotalClears { get; init; } + public byte CurrentSubClears { get; set; } + public byte CurrentClears { get; set; } + public short LifetimeRecord { get; set; } + public short CurrentRecord { get; set; } + public byte ExtraCurrentSubClears { get; set; } + public byte ExtraCurrentClears { get; set; } + public DateTime DailyResetTime { get; set; } + public DateTime UnionCooldownTime { get; set; } + public DateTime CooldownTime { get; set; } + public DungeonRecordFlag Flag { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator DungeonRecord?(Maple2.Model.Game.Dungeon.DungeonRecord? other) { + return other == null ? null : new DungeonRecord { + DungeonId = other.DungeonId, + CurrentSubClears = other.UnionSubClears, + CurrentClears = other.UnionClears, + DailyResetTime = other.UnionSubCooldownTimestamp.FromEpochSeconds(), + UnionCooldownTime = other.UnionCooldownTimestamp.FromEpochSeconds(), + ClearTime = other.ClearTimestamp.FromEpochSeconds(), + CooldownTime = other.CooldownTimestamp.FromEpochSeconds(), + TotalClears = other.TotalClears, + LifetimeRecord = other.LifetimeRecord, + CurrentRecord = other.CurrentRecord, + ExtraCurrentSubClears = other.ExtraSubClears, + ExtraCurrentClears = other.ExtraClears, + Flag = other.Flag, + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.Dungeon.DungeonRecord?(DungeonRecord? other) { + return other == null ? null : new Maple2.Model.Game.Dungeon.DungeonRecord(other.DungeonId) { + UnionSubClears = other.CurrentSubClears, + UnionClears = other.CurrentClears, + UnionSubCooldownTimestamp = other.DailyResetTime.ToEpochSeconds(), + UnionCooldownTimestamp = other.UnionCooldownTime.ToEpochSeconds(), + ClearTimestamp = other.ClearTime.ToEpochSeconds(), + CooldownTimestamp = other.CooldownTime.ToEpochSeconds(), + TotalClears = other.TotalClears, + LifetimeRecord = other.LifetimeRecord, + CurrentRecord = other.CurrentRecord, + ExtraSubClears = other.ExtraCurrentSubClears, + ExtraClears = other.ExtraCurrentClears, + Flag = other.Flag, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("dungeon-record"); + builder.HasKey(record => new { record.OwnerId, record.DungeonId }); + builder.HasOne() + .WithMany() + .HasForeignKey(record => record.OwnerId); + + } +} diff --git a/Maple2.Database/Model/FishEntry.cs b/Maple2.Database/Model/FishEntry.cs index d5a3dd99e..586d6b70b 100644 --- a/Maple2.Database/Model/FishEntry.cs +++ b/Maple2.Database/Model/FishEntry.cs @@ -1,29 +1,29 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Maple2.Database.Model; - -internal class FishEntry { - public int Id { get; set; } - public int TotalCaught { get; set; } - public int TotalPrizeFish { get; set; } - public int LargestSize { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator FishEntry?(Maple2.Model.Game.FishEntry? other) { - return other == null ? null : new FishEntry { - Id = other.Id, - TotalCaught = other.TotalCaught, - TotalPrizeFish = other.TotalPrizeFish, - LargestSize = other.LargestSize, - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.FishEntry?(FishEntry? other) { - return other == null ? null : new Maple2.Model.Game.FishEntry(other.Id) { - TotalCaught = other.TotalCaught, - TotalPrizeFish = other.TotalPrizeFish, - LargestSize = other.LargestSize, - }; - } -} +using System.Diagnostics.CodeAnalysis; + +namespace Maple2.Database.Model; + +internal class FishEntry { + public int Id { get; set; } + public int TotalCaught { get; set; } + public int TotalPrizeFish { get; set; } + public int LargestSize { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator FishEntry?(Maple2.Model.Game.FishEntry? other) { + return other == null ? null : new FishEntry { + Id = other.Id, + TotalCaught = other.TotalCaught, + TotalPrizeFish = other.TotalPrizeFish, + LargestSize = other.LargestSize, + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.FishEntry?(FishEntry? other) { + return other == null ? null : new Maple2.Model.Game.FishEntry(other.Id) { + TotalCaught = other.TotalCaught, + TotalPrizeFish = other.TotalPrizeFish, + LargestSize = other.LargestSize, + }; + } +} diff --git a/Maple2.Database/Model/GameEventUserValue.cs b/Maple2.Database/Model/GameEventUserValue.cs index b93f7aa5c..93f6ca290 100644 --- a/Maple2.Database/Model/GameEventUserValue.cs +++ b/Maple2.Database/Model/GameEventUserValue.cs @@ -1,41 +1,41 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Model.Enum; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class GameEventUserValue { - public long CharacterId { get; set; } - public GameEventUserValueType Type { get; set; } - public string Value { get; set; } - public int EventId { get; set; } - public long ExpirationTime { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator GameEventUserValue?(Maple2.Model.Game.GameEventUserValue? other) { - return other == null ? null : new GameEventUserValue { - Type = other.Type, - Value = other.Value, - EventId = other.EventId, - ExpirationTime = other.ExpirationTime, - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.GameEventUserValue?(GameEventUserValue? other) { - return other == null ? null : new Maple2.Model.Game.GameEventUserValue(other.Value) { - Type = other.Type, - EventId = other.EventId, - ExpirationTime = other.ExpirationTime, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("game-event-user-value"); - builder.HasKey(value => new { value.CharacterId, value.EventId, value.Type }); - builder.HasOne() - .WithMany() - .HasForeignKey(value => value.CharacterId); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Model.Enum; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class GameEventUserValue { + public long CharacterId { get; set; } + public GameEventUserValueType Type { get; set; } + public string Value { get; set; } + public int EventId { get; set; } + public long ExpirationTime { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator GameEventUserValue?(Maple2.Model.Game.GameEventUserValue? other) { + return other == null ? null : new GameEventUserValue { + Type = other.Type, + Value = other.Value, + EventId = other.EventId, + ExpirationTime = other.ExpirationTime, + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.GameEventUserValue?(GameEventUserValue? other) { + return other == null ? null : new Maple2.Model.Game.GameEventUserValue(other.Value) { + Type = other.Type, + EventId = other.EventId, + ExpirationTime = other.ExpirationTime, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("game-event-user-value"); + builder.HasKey(value => new { value.CharacterId, value.EventId, value.Type }); + builder.HasOne() + .WithMany() + .HasForeignKey(value => value.CharacterId); + } +} diff --git a/Maple2.Database/Model/Guild/Guild.cs b/Maple2.Database/Model/Guild/Guild.cs index 521f0c377..8dbcf693d 100644 --- a/Maple2.Database/Model/Guild/Guild.cs +++ b/Maple2.Database/Model/Guild/Guild.cs @@ -1,104 +1,104 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class Guild { - public long Id { get; set; } - public required string Name { get; set; } - public string Emblem { get; set; } = string.Empty; - public string Notice { get; set; } = string.Empty; - public GuildFocus Focus { get; set; } - public int Experience { get; set; } - public int Funds { get; set; } - public int HouseRank { get; set; } - public int HouseTheme { get; set; } - public IList Ranks { get; set; } - public IList Buffs { get; set; } - public IList Posters { get; set; } - public IList Npcs { get; set; } - - public long LeaderId { get; set; } - public IList? Members { get; set; } - - public DateTime CreationTime { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Guild?(Maple2.Model.Game.Guild? other) { - return other == null ? null : new Guild { - Id = other.Id, - Name = other.Name, - Emblem = other.Emblem, - Notice = other.Notice, - Focus = other.Focus, - Experience = other.Experience, - Funds = other.Funds, - HouseRank = other.HouseRank, - HouseTheme = other.HouseTheme, - Ranks = other.Ranks.Select(rank => new GuildRank { - Name = rank.Name, - Permission = rank.Permission, - }).ToArray(), - Buffs = other.Buffs.Select(buff => new GuildBuff { - Id = buff.Id, - Level = buff.Level, - ExpiryTime = buff.ExpiryTime, - }).ToArray(), - Posters = other.Posters.Select(poster => new GuildPoster { - Id = poster.Id, - Picture = poster.Picture, - OwnerId = poster.OwnerId, - OwnerName = poster.OwnerName, - }).ToArray(), - Npcs = other.Npcs.Select(npc => new GuildNpc { - Type = npc.Type, - Level = npc.Level, - }).ToArray(), - LeaderId = other.LeaderCharacterId, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("guild"); - builder.HasKey(guild => guild.Id); - builder.Property(guild => guild.Ranks).HasJsonConversion(); - builder.Property(guild => guild.Buffs).HasJsonConversion(); - builder.Property(guild => guild.Posters).HasJsonConversion(); - builder.Property(guild => guild.Npcs).HasJsonConversion(); - - builder.OneToOne() - .HasForeignKey(guild => guild.LeaderId); - builder.HasMany(guild => guild.Members); - - IMutableProperty creationTime = builder.Property(guild => guild.CreationTime) - .ValueGeneratedOnAdd().Metadata; - creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); - } -} - -internal class GuildRank { - public required string Name { get; set; } - public GuildPermission Permission { get; set; } -} - -internal class GuildBuff { - public int Id { get; set; } - public int Level { get; set; } - public long ExpiryTime { get; set; } -} - -internal class GuildPoster { - public int Id { get; set; } - public required string Picture { get; set; } - public long OwnerId { get; set; } - public required string OwnerName { get; set; } -} - -internal class GuildNpc { - public GuildNpcType Type { get; set; } - public int Level { get; set; } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class Guild { + public long Id { get; set; } + public required string Name { get; set; } + public string Emblem { get; set; } = string.Empty; + public string Notice { get; set; } = string.Empty; + public GuildFocus Focus { get; set; } + public int Experience { get; set; } + public int Funds { get; set; } + public int HouseRank { get; set; } + public int HouseTheme { get; set; } + public IList Ranks { get; set; } + public IList Buffs { get; set; } + public IList Posters { get; set; } + public IList Npcs { get; set; } + + public long LeaderId { get; set; } + public IList? Members { get; set; } + + public DateTime CreationTime { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Guild?(Maple2.Model.Game.Guild? other) { + return other == null ? null : new Guild { + Id = other.Id, + Name = other.Name, + Emblem = other.Emblem, + Notice = other.Notice, + Focus = other.Focus, + Experience = other.Experience, + Funds = other.Funds, + HouseRank = other.HouseRank, + HouseTheme = other.HouseTheme, + Ranks = other.Ranks.Select(rank => new GuildRank { + Name = rank.Name, + Permission = rank.Permission, + }).ToArray(), + Buffs = other.Buffs.Select(buff => new GuildBuff { + Id = buff.Id, + Level = buff.Level, + ExpiryTime = buff.ExpiryTime, + }).ToArray(), + Posters = other.Posters.Select(poster => new GuildPoster { + Id = poster.Id, + Picture = poster.Picture, + OwnerId = poster.OwnerId, + OwnerName = poster.OwnerName, + }).ToArray(), + Npcs = other.Npcs.Select(npc => new GuildNpc { + Type = npc.Type, + Level = npc.Level, + }).ToArray(), + LeaderId = other.LeaderCharacterId, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("guild"); + builder.HasKey(guild => guild.Id); + builder.Property(guild => guild.Ranks).HasJsonConversion(); + builder.Property(guild => guild.Buffs).HasJsonConversion(); + builder.Property(guild => guild.Posters).HasJsonConversion(); + builder.Property(guild => guild.Npcs).HasJsonConversion(); + + builder.OneToOne() + .HasForeignKey(guild => guild.LeaderId); + builder.HasMany(guild => guild.Members); + + IMutableProperty creationTime = builder.Property(guild => guild.CreationTime) + .ValueGeneratedOnAdd().Metadata; + creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + } +} + +internal class GuildRank { + public required string Name { get; set; } + public GuildPermission Permission { get; set; } +} + +internal class GuildBuff { + public int Id { get; set; } + public int Level { get; set; } + public long ExpiryTime { get; set; } +} + +internal class GuildPoster { + public int Id { get; set; } + public required string Picture { get; set; } + public long OwnerId { get; set; } + public required string OwnerName { get; set; } +} + +internal class GuildNpc { + public GuildNpcType Type { get; set; } + public int Level { get; set; } +} diff --git a/Maple2.Database/Model/Guild/GuildApplication.cs b/Maple2.Database/Model/Guild/GuildApplication.cs index b789536db..bf511607e 100644 --- a/Maple2.Database/Model/Guild/GuildApplication.cs +++ b/Maple2.Database/Model/Guild/GuildApplication.cs @@ -1,28 +1,28 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class GuildApplication { - public long Id { get; set; } - public long GuildId { get; set; } - public long ApplicantId { get; set; } - - public DateTime CreationTime { get; set; } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("guild-application"); - builder.HasKey(app => app.Id); - builder.HasOne() - .WithMany() - .HasForeignKey(app => app.GuildId); - builder.HasOne() - .WithMany() - .HasForeignKey(app => app.ApplicantId); - - IMutableProperty creationTime = builder.Property(app => app.CreationTime) - .ValueGeneratedOnAdd().Metadata; - creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class GuildApplication { + public long Id { get; set; } + public long GuildId { get; set; } + public long ApplicantId { get; set; } + + public DateTime CreationTime { get; set; } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("guild-application"); + builder.HasKey(app => app.Id); + builder.HasOne() + .WithMany() + .HasForeignKey(app => app.GuildId); + builder.HasOne() + .WithMany() + .HasForeignKey(app => app.ApplicantId); + + IMutableProperty creationTime = builder.Property(app => app.CreationTime) + .ValueGeneratedOnAdd().Metadata; + creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + } +} diff --git a/Maple2.Database/Model/Guild/GuildMember.cs b/Maple2.Database/Model/Guild/GuildMember.cs index e1e13fa5a..e3cc73c2a 100644 --- a/Maple2.Database/Model/Guild/GuildMember.cs +++ b/Maple2.Database/Model/Guild/GuildMember.cs @@ -1,54 +1,54 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class GuildMember { - public long GuildId { get; set; } - public long CharacterId { get; set; } - - public string Message { get; set; } = string.Empty; - public byte Rank { get; set; } - - public int WeeklyContribution { get; set; } - public int TotalContribution { get; set; } - public int DailyDonationCount { get; set; } - - public Character Character { get; set; } - - public DateTime CheckinTime { get; set; } - public DateTime DonationTime { get; set; } - public DateTime CreationTime { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator GuildMember?(Maple2.Model.Game.GuildMember? other) { - return other == null ? null : new GuildMember { - GuildId = other.GuildId, - CharacterId = other.CharacterId, - Message = other.Message, - Rank = other.Rank, - WeeklyContribution = other.WeeklyContribution, - TotalContribution = other.TotalContribution, - DailyDonationCount = other.DailyDonationCount, - CheckinTime = other.CheckinTime.FromEpochSeconds(), - DonationTime = other.DonationTime.FromEpochSeconds(), - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("guild-member"); - builder.HasKey(member => new { member.GuildId, member.CharacterId }); - builder.OneToOne(member => member.Character) - .HasForeignKey(member => member.CharacterId); - builder.HasOne() - .WithMany(guild => guild.Members) - .HasForeignKey(member => member.GuildId); - - IMutableProperty creationTime = builder.Property(member => member.CreationTime) - .ValueGeneratedOnAdd().Metadata; - creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class GuildMember { + public long GuildId { get; set; } + public long CharacterId { get; set; } + + public string Message { get; set; } = string.Empty; + public byte Rank { get; set; } + + public int WeeklyContribution { get; set; } + public int TotalContribution { get; set; } + public int DailyDonationCount { get; set; } + + public Character Character { get; set; } + + public DateTime CheckinTime { get; set; } + public DateTime DonationTime { get; set; } + public DateTime CreationTime { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator GuildMember?(Maple2.Model.Game.GuildMember? other) { + return other == null ? null : new GuildMember { + GuildId = other.GuildId, + CharacterId = other.CharacterId, + Message = other.Message, + Rank = other.Rank, + WeeklyContribution = other.WeeklyContribution, + TotalContribution = other.TotalContribution, + DailyDonationCount = other.DailyDonationCount, + CheckinTime = other.CheckinTime.FromEpochSeconds(), + DonationTime = other.DonationTime.FromEpochSeconds(), + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("guild-member"); + builder.HasKey(member => new { member.GuildId, member.CharacterId }); + builder.OneToOne(member => member.Character) + .HasForeignKey(member => member.CharacterId); + builder.HasOne() + .WithMany(guild => guild.Members) + .HasForeignKey(member => member.GuildId); + + IMutableProperty creationTime = builder.Property(member => member.CreationTime) + .ValueGeneratedOnAdd().Metadata; + creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + } +} diff --git a/Maple2.Database/Model/Item/Item.cs b/Maple2.Database/Model/Item/Item.cs index 5db426e15..b022c02a3 100644 --- a/Maple2.Database/Model/Item/Item.cs +++ b/Maple2.Database/Model/Item/Item.cs @@ -1,162 +1,162 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class Item { - public long Id { get; set; } - public long OwnerId { get; set; } - public int ItemId { get; set; } - public int Rarity { get; set; } - public short Slot { get; set; } = -1; - public ItemGroup Group { get; set; } = ItemGroup.Default; - public int Amount { get; set; } = 1; - public DateTime ExpiryTime { get; set; } - public int TimeChangedOption { get; set; } - public int RemainUses { get; set; } - public bool IsLocked { get; set; } - public long UnlockTime { get; set; } - public short GlamorForges { get; set; } - public int GachaDismantleId { get; set; } - - public ItemAppearance? Appearance { get; set; } - public ItemStats? Stats { get; set; } - public ItemEnchant? Enchant { get; set; } - public ItemLimitBreak? LimitBreak { get; set; } - - public ItemTransfer? Transfer { get; set; } - public ItemSocket? Socket { get; set; } - public ItemCoupleInfo? CoupleInfo { get; set; } - public ItemBinding? Binding { get; set; } - - public ItemSubType? SubType { get; set; } - - public DateTime CreationTime { get; set; } - public DateTime LastModified { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Item?(Maple2.Model.Game.Item? other) { - if (other == null) { - return null; - } - - var item = new Item { - Id = other.Uid, - ItemId = other.Id, - Rarity = other.Rarity, - Slot = other.Slot, - Group = other.Group, - Amount = other.Amount, - TimeChangedOption = other.TimeChangedOption, - RemainUses = other.RemainUses, - IsLocked = other.IsLocked, - UnlockTime = other.UnlockTime, - GlamorForges = other.GlamorForges, - GachaDismantleId = other.GachaDismantleId, - Appearance = other.Appearance == null ? null : other.Appearance switch { - Maple2.Model.Game.HairAppearance hair => (HairAppearance) hair, - Maple2.Model.Game.DecalAppearance decal => (DecalAppearance) decal, - Maple2.Model.Game.CapAppearance cap => (CapAppearance) cap, - _ => (ColorAppearance) other.Appearance, - }, - Stats = other.Stats, - Enchant = other.Enchant, - LimitBreak = other.LimitBreak, - Transfer = other.Transfer, - Socket = other.Socket, - CoupleInfo = other.CoupleInfo, - Binding = other.Binding, - }; - - if (other.CreationTime != 0) { - item.CreationTime = other.CreationTime.FromEpochSeconds(); - } - - if (other.ExpiryTime != 0) { - item.ExpiryTime = other.ExpiryTime.FromEpochSeconds(); - } - - if (other.Template != null && other.Blueprint != null) { - item.SubType = new ItemUgc(other.Template, other.Blueprint); - } else if (other.Pet != null) { - item.SubType = (ItemPet) other.Pet; - } else if (other.Music != null) { - item.SubType = (ItemCustomMusicScore) other.Music; - } else if (other.Badge != null) { - item.SubType = (ItemBadge) other.Badge; - } - - return item; - } - - // Use explicit Convert() here because we need metadata to construct Item. - public Maple2.Model.Game.Item Convert(ItemMetadata metadata) { - var item = new Maple2.Model.Game.Item(metadata, Rarity, Amount, false) { - Uid = Id, - Slot = Slot, - Group = Group, - CreationTime = CreationTime.ToEpochSeconds(), - ExpiryTime = ExpiryTime.ToEpochSeconds(), - TimeChangedOption = TimeChangedOption, - RemainUses = RemainUses, - IsLocked = IsLocked, - UnlockTime = UnlockTime, - GlamorForges = GlamorForges, - GachaDismantleId = GachaDismantleId, - Appearance = Appearance switch { - HairAppearance hair => hair, - DecalAppearance decal => decal, - CapAppearance cap => cap, - ColorAppearance color => color, - _ => new Maple2.Model.Game.ItemAppearance(default), - }, - Stats = Stats, - Enchant = Enchant, - LimitBreak = LimitBreak, - Transfer = Transfer, - Socket = Socket, - CoupleInfo = CoupleInfo, - Binding = Binding, - }; - - switch (SubType) { - case ItemUgc(var ugcItemLook, var itemBlueprint): - item.Template = ugcItemLook; - item.Blueprint = itemBlueprint; - break; - case ItemPet pet: - item.Pet = pet; - break; - case ItemCustomMusicScore music: - item.Music = music; - break; - case ItemBadge badge: - item.Badge = badge; - break; - } - - return item; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("item"); - builder.HasKey(item => item.Id); - - builder.Property(item => item.Appearance).HasJsonConversion().IsRequired(); - builder.Property(item => item.Stats).HasJsonConversion(); - builder.Property(item => item.Enchant).HasJsonConversion(); - builder.Property(item => item.LimitBreak).HasJsonConversion(); - builder.Property(item => item.Transfer).HasJsonConversion(); - builder.Property(item => item.Socket).HasJsonConversion(); - builder.Property(item => item.CoupleInfo).HasJsonConversion(); - builder.Property(item => item.Binding).HasJsonConversion(); - builder.Property(item => item.SubType).HasJsonConversion(); - - builder.Property(item => item.CreationTime).ValueGeneratedOnAdd(); - builder.Property(item => item.LastModified).ValueGeneratedOnAdd(); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class Item { + public long Id { get; set; } + public long OwnerId { get; set; } + public int ItemId { get; set; } + public int Rarity { get; set; } + public short Slot { get; set; } = -1; + public ItemGroup Group { get; set; } = ItemGroup.Default; + public int Amount { get; set; } = 1; + public DateTime ExpiryTime { get; set; } + public int TimeChangedOption { get; set; } + public int RemainUses { get; set; } + public bool IsLocked { get; set; } + public long UnlockTime { get; set; } + public short GlamorForges { get; set; } + public int GachaDismantleId { get; set; } + + public ItemAppearance? Appearance { get; set; } + public ItemStats? Stats { get; set; } + public ItemEnchant? Enchant { get; set; } + public ItemLimitBreak? LimitBreak { get; set; } + + public ItemTransfer? Transfer { get; set; } + public ItemSocket? Socket { get; set; } + public ItemCoupleInfo? CoupleInfo { get; set; } + public ItemBinding? Binding { get; set; } + + public ItemSubType? SubType { get; set; } + + public DateTime CreationTime { get; set; } + public DateTime LastModified { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Item?(Maple2.Model.Game.Item? other) { + if (other == null) { + return null; + } + + var item = new Item { + Id = other.Uid, + ItemId = other.Id, + Rarity = other.Rarity, + Slot = other.Slot, + Group = other.Group, + Amount = other.Amount, + TimeChangedOption = other.TimeChangedOption, + RemainUses = other.RemainUses, + IsLocked = other.IsLocked, + UnlockTime = other.UnlockTime, + GlamorForges = other.GlamorForges, + GachaDismantleId = other.GachaDismantleId, + Appearance = other.Appearance == null ? null : other.Appearance switch { + Maple2.Model.Game.HairAppearance hair => (HairAppearance) hair, + Maple2.Model.Game.DecalAppearance decal => (DecalAppearance) decal, + Maple2.Model.Game.CapAppearance cap => (CapAppearance) cap, + _ => (ColorAppearance) other.Appearance, + }, + Stats = other.Stats, + Enchant = other.Enchant, + LimitBreak = other.LimitBreak, + Transfer = other.Transfer, + Socket = other.Socket, + CoupleInfo = other.CoupleInfo, + Binding = other.Binding, + }; + + if (other.CreationTime != 0) { + item.CreationTime = other.CreationTime.FromEpochSeconds(); + } + + if (other.ExpiryTime != 0) { + item.ExpiryTime = other.ExpiryTime.FromEpochSeconds(); + } + + if (other.Template != null && other.Blueprint != null) { + item.SubType = new ItemUgc(other.Template, other.Blueprint); + } else if (other.Pet != null) { + item.SubType = (ItemPet) other.Pet; + } else if (other.Music != null) { + item.SubType = (ItemCustomMusicScore) other.Music; + } else if (other.Badge != null) { + item.SubType = (ItemBadge) other.Badge; + } + + return item; + } + + // Use explicit Convert() here because we need metadata to construct Item. + public Maple2.Model.Game.Item Convert(ItemMetadata metadata) { + var item = new Maple2.Model.Game.Item(metadata, Rarity, Amount, false) { + Uid = Id, + Slot = Slot, + Group = Group, + CreationTime = CreationTime.ToEpochSeconds(), + ExpiryTime = ExpiryTime.ToEpochSeconds(), + TimeChangedOption = TimeChangedOption, + RemainUses = RemainUses, + IsLocked = IsLocked, + UnlockTime = UnlockTime, + GlamorForges = GlamorForges, + GachaDismantleId = GachaDismantleId, + Appearance = Appearance switch { + HairAppearance hair => hair, + DecalAppearance decal => decal, + CapAppearance cap => cap, + ColorAppearance color => color, + _ => new Maple2.Model.Game.ItemAppearance(default), + }, + Stats = Stats, + Enchant = Enchant, + LimitBreak = LimitBreak, + Transfer = Transfer, + Socket = Socket, + CoupleInfo = CoupleInfo, + Binding = Binding, + }; + + switch (SubType) { + case ItemUgc(var ugcItemLook, var itemBlueprint): + item.Template = ugcItemLook; + item.Blueprint = itemBlueprint; + break; + case ItemPet pet: + item.Pet = pet; + break; + case ItemCustomMusicScore music: + item.Music = music; + break; + case ItemBadge badge: + item.Badge = badge; + break; + } + + return item; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("item"); + builder.HasKey(item => item.Id); + + builder.Property(item => item.Appearance).HasJsonConversion().IsRequired(); + builder.Property(item => item.Stats).HasJsonConversion(); + builder.Property(item => item.Enchant).HasJsonConversion(); + builder.Property(item => item.LimitBreak).HasJsonConversion(); + builder.Property(item => item.Transfer).HasJsonConversion(); + builder.Property(item => item.Socket).HasJsonConversion(); + builder.Property(item => item.CoupleInfo).HasJsonConversion(); + builder.Property(item => item.Binding).HasJsonConversion(); + builder.Property(item => item.SubType).HasJsonConversion(); + + builder.Property(item => item.CreationTime).ValueGeneratedOnAdd(); + builder.Property(item => item.LastModified).ValueGeneratedOnAdd(); + } +} diff --git a/Maple2.Database/Model/Item/ItemAppearance.cs b/Maple2.Database/Model/Item/ItemAppearance.cs index 64504a711..734416635 100644 --- a/Maple2.Database/Model/Item/ItemAppearance.cs +++ b/Maple2.Database/Model/Item/ItemAppearance.cs @@ -1,69 +1,69 @@ -using System.Diagnostics.CodeAnalysis; -using System.Numerics; -using System.Text.Json.Serialization; -using Maple2.Model.Common; - -namespace Maple2.Database.Model; - -[JsonPolymorphic(TypeDiscriminatorPropertyName = "!")] -[JsonDerivedType(typeof(ColorAppearance), typeDiscriminator: "default")] -[JsonDerivedType(typeof(HairAppearance), typeDiscriminator: "hair")] -[JsonDerivedType(typeof(DecalAppearance), typeDiscriminator: "decal")] -[JsonDerivedType(typeof(CapAppearance), typeDiscriminator: "cap")] -internal abstract record ItemAppearance(EquipColor Color); - -internal record ColorAppearance(EquipColor Color) : ItemAppearance(Color) { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator ColorAppearance?(Maple2.Model.Game.ItemAppearance? other) { - return other == null ? null : new ColorAppearance(other.Color); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.ItemAppearance?(ColorAppearance? other) { - return other == null ? null : new Maple2.Model.Game.ItemAppearance(other.Color); - } -} - -internal record HairAppearance(EquipColor Color, float BackLength, Vector3 BackPosition1, Vector3 BackPosition2, - float FrontLength, Vector3 FrontPosition1, Vector3 FrontPosition2) : ItemAppearance(Color) { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator HairAppearance?(Maple2.Model.Game.HairAppearance? other) { - return other == null ? null : new HairAppearance(other.Color, other.BackLength, other.BackPosition1, - other.BackPosition2, other.FrontLength, other.FrontPosition1, other.FrontPosition2); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.HairAppearance?(HairAppearance? other) { - return other == null ? null : new Maple2.Model.Game.HairAppearance(other.Color, other.BackLength, - other.BackPosition1, other.BackPosition2, other.FrontLength, other.FrontPosition1, other.FrontPosition2); - } -} - -internal record DecalAppearance(EquipColor Color, float Position1, float Position2, float Position3, float Position4) : ItemAppearance(Color) { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator DecalAppearance?(Maple2.Model.Game.DecalAppearance? other) { - return other == null ? null : new DecalAppearance(other.Color, other.Position1, other.Position2, - other.Position3, other.Position4); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.DecalAppearance?(DecalAppearance? other) { - return other == null ? null : new Maple2.Model.Game.DecalAppearance(other.Color, other.Position1, - other.Position2, other.Position3, other.Position4); - } -} - -internal record CapAppearance(EquipColor Color, Vector3 Position1, Vector3 Position2, Vector3 Position3, - Vector3 Position4, float Unknown) : ItemAppearance(Color) { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator CapAppearance?(Maple2.Model.Game.CapAppearance? other) { - return other == null ? null : new CapAppearance(other.Color, other.Position1, other.Position2, - other.Position3, other.Position4, other.Unknown); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.CapAppearance?(CapAppearance? other) { - return other == null ? null : new Maple2.Model.Game.CapAppearance(other.Color, other.Position1, - other.Position2, other.Position3, other.Position4, other.Unknown); - } -} +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Text.Json.Serialization; +using Maple2.Model.Common; + +namespace Maple2.Database.Model; + +[JsonPolymorphic(TypeDiscriminatorPropertyName = "!")] +[JsonDerivedType(typeof(ColorAppearance), typeDiscriminator: "default")] +[JsonDerivedType(typeof(HairAppearance), typeDiscriminator: "hair")] +[JsonDerivedType(typeof(DecalAppearance), typeDiscriminator: "decal")] +[JsonDerivedType(typeof(CapAppearance), typeDiscriminator: "cap")] +internal abstract record ItemAppearance(EquipColor Color); + +internal record ColorAppearance(EquipColor Color) : ItemAppearance(Color) { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator ColorAppearance?(Maple2.Model.Game.ItemAppearance? other) { + return other == null ? null : new ColorAppearance(other.Color); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.ItemAppearance?(ColorAppearance? other) { + return other == null ? null : new Maple2.Model.Game.ItemAppearance(other.Color); + } +} + +internal record HairAppearance(EquipColor Color, float BackLength, Vector3 BackPosition1, Vector3 BackPosition2, + float FrontLength, Vector3 FrontPosition1, Vector3 FrontPosition2) : ItemAppearance(Color) { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator HairAppearance?(Maple2.Model.Game.HairAppearance? other) { + return other == null ? null : new HairAppearance(other.Color, other.BackLength, other.BackPosition1, + other.BackPosition2, other.FrontLength, other.FrontPosition1, other.FrontPosition2); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.HairAppearance?(HairAppearance? other) { + return other == null ? null : new Maple2.Model.Game.HairAppearance(other.Color, other.BackLength, + other.BackPosition1, other.BackPosition2, other.FrontLength, other.FrontPosition1, other.FrontPosition2); + } +} + +internal record DecalAppearance(EquipColor Color, float Position1, float Position2, float Position3, float Position4) : ItemAppearance(Color) { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator DecalAppearance?(Maple2.Model.Game.DecalAppearance? other) { + return other == null ? null : new DecalAppearance(other.Color, other.Position1, other.Position2, + other.Position3, other.Position4); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.DecalAppearance?(DecalAppearance? other) { + return other == null ? null : new Maple2.Model.Game.DecalAppearance(other.Color, other.Position1, + other.Position2, other.Position3, other.Position4); + } +} + +internal record CapAppearance(EquipColor Color, Vector3 Position1, Vector3 Position2, Vector3 Position3, + Vector3 Position4, float Unknown) : ItemAppearance(Color) { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator CapAppearance?(Maple2.Model.Game.CapAppearance? other) { + return other == null ? null : new CapAppearance(other.Color, other.Position1, other.Position2, + other.Position3, other.Position4, other.Unknown); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.CapAppearance?(CapAppearance? other) { + return other == null ? null : new Maple2.Model.Game.CapAppearance(other.Color, other.Position1, + other.Position2, other.Position3, other.Position4, other.Unknown); + } +} diff --git a/Maple2.Database/Model/Item/ItemInfo.cs b/Maple2.Database/Model/Item/ItemInfo.cs index 8329ddbb0..127daee63 100644 --- a/Maple2.Database/Model/Item/ItemInfo.cs +++ b/Maple2.Database/Model/Item/ItemInfo.cs @@ -1,42 +1,42 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Model.Enum; - -namespace Maple2.Database.Model; - -internal record ItemTransfer(TransferFlag Flag, int RemainTrades, int RepackageCount, ItemBinding? Binding) { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator ItemTransfer?(Maple2.Model.Game.ItemTransfer? other) { - return other == null ? null : - new ItemTransfer(other.Flag, other.RemainTrades, other.RepackageCount, other.Binding); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.ItemTransfer?(ItemTransfer? other) { - return other == null ? null : - new Maple2.Model.Game.ItemTransfer(other.Flag, other.RemainTrades, other.RepackageCount, other.Binding); - } -} - -internal record ItemCoupleInfo(long CharacterId, string Name, bool IsCreator) { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator ItemCoupleInfo?(Maple2.Model.Game.ItemCoupleInfo? other) { - return other == null ? null : new ItemCoupleInfo(other.CharacterId, other.Name, other.IsCreator); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.ItemCoupleInfo?(ItemCoupleInfo? other) { - return other == null ? null : new Maple2.Model.Game.ItemCoupleInfo(other.CharacterId, other.Name, other.IsCreator); - } -} - -internal record ItemBinding(long CharacterId, string Name) { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator ItemBinding?(Maple2.Model.Game.ItemBinding? other) { - return other == null ? null : new ItemBinding(other.CharacterId, other.Name); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.ItemBinding?(ItemBinding? other) { - return other == null ? null : new Maple2.Model.Game.ItemBinding(other.CharacterId, other.Name); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Model.Enum; + +namespace Maple2.Database.Model; + +internal record ItemTransfer(TransferFlag Flag, int RemainTrades, int RepackageCount, ItemBinding? Binding) { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator ItemTransfer?(Maple2.Model.Game.ItemTransfer? other) { + return other == null ? null : + new ItemTransfer(other.Flag, other.RemainTrades, other.RepackageCount, other.Binding); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.ItemTransfer?(ItemTransfer? other) { + return other == null ? null : + new Maple2.Model.Game.ItemTransfer(other.Flag, other.RemainTrades, other.RepackageCount, other.Binding); + } +} + +internal record ItemCoupleInfo(long CharacterId, string Name, bool IsCreator) { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator ItemCoupleInfo?(Maple2.Model.Game.ItemCoupleInfo? other) { + return other == null ? null : new ItemCoupleInfo(other.CharacterId, other.Name, other.IsCreator); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.ItemCoupleInfo?(ItemCoupleInfo? other) { + return other == null ? null : new Maple2.Model.Game.ItemCoupleInfo(other.CharacterId, other.Name, other.IsCreator); + } +} + +internal record ItemBinding(long CharacterId, string Name) { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator ItemBinding?(Maple2.Model.Game.ItemBinding? other) { + return other == null ? null : new ItemBinding(other.CharacterId, other.Name); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.ItemBinding?(ItemBinding? other) { + return other == null ? null : new Maple2.Model.Game.ItemBinding(other.CharacterId, other.Name); + } +} diff --git a/Maple2.Database/Model/Item/ItemSocket.cs b/Maple2.Database/Model/Item/ItemSocket.cs index 4dce9ab0e..84abe6036 100644 --- a/Maple2.Database/Model/Item/ItemSocket.cs +++ b/Maple2.Database/Model/Item/ItemSocket.cs @@ -1,40 +1,40 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Maple2.Database.Model; - -internal record ItemSocket(byte MaxSlots, ItemGemstone[] Sockets) { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator ItemSocket?(Maple2.Model.Game.ItemSocket? other) { - if (other == null) { - return null; - } - - ItemGemstone[] sockets = Array.ConvertAll(other.Sockets, gemstone => (ItemGemstone) gemstone!); - return new ItemSocket(other.MaxSlots, sockets); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.ItemSocket?(ItemSocket? other) { - if (other == null) { - return null; - } - - Maple2.Model.Game.ItemGemstone[] sockets = - Array.ConvertAll(other.Sockets, gemstone => (Maple2.Model.Game.ItemGemstone) gemstone); - return new Maple2.Model.Game.ItemSocket(other.MaxSlots, sockets); - } -} - -internal record ItemGemstone(int ItemId, ItemBinding Binding, ItemStats Stats, bool IsLocked, long UnlockTime) { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator ItemGemstone?(Maple2.Model.Game.ItemGemstone? other) { - return other == null ? null : - new ItemGemstone(other.ItemId, other.Binding!, other.Stats!, other.IsLocked, other.UnlockTime); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.ItemGemstone?(ItemGemstone? other) { - return other == null ? null : - new Maple2.Model.Game.ItemGemstone(other.ItemId, other.Binding, other.Stats, other.IsLocked, other.UnlockTime); - } -} +using System.Diagnostics.CodeAnalysis; + +namespace Maple2.Database.Model; + +internal record ItemSocket(byte MaxSlots, ItemGemstone[] Sockets) { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator ItemSocket?(Maple2.Model.Game.ItemSocket? other) { + if (other == null) { + return null; + } + + ItemGemstone[] sockets = Array.ConvertAll(other.Sockets, gemstone => (ItemGemstone) gemstone!); + return new ItemSocket(other.MaxSlots, sockets); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.ItemSocket?(ItemSocket? other) { + if (other == null) { + return null; + } + + Maple2.Model.Game.ItemGemstone[] sockets = + Array.ConvertAll(other.Sockets, gemstone => (Maple2.Model.Game.ItemGemstone) gemstone); + return new Maple2.Model.Game.ItemSocket(other.MaxSlots, sockets); + } +} + +internal record ItemGemstone(int ItemId, ItemBinding Binding, ItemStats Stats, bool IsLocked, long UnlockTime) { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator ItemGemstone?(Maple2.Model.Game.ItemGemstone? other) { + return other == null ? null : + new ItemGemstone(other.ItemId, other.Binding!, other.Stats!, other.IsLocked, other.UnlockTime); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.ItemGemstone?(ItemGemstone? other) { + return other == null ? null : + new Maple2.Model.Game.ItemGemstone(other.ItemId, other.Binding, other.Stats, other.IsLocked, other.UnlockTime); + } +} diff --git a/Maple2.Database/Model/Item/ItemStats.cs b/Maple2.Database/Model/Item/ItemStats.cs index 7d8bfba67..c9b5bbae7 100644 --- a/Maple2.Database/Model/Item/ItemStats.cs +++ b/Maple2.Database/Model/Item/ItemStats.cs @@ -1,59 +1,59 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Model.Enum; -using Maple2.Model.Game; - -namespace Maple2.Database.Model; - -internal record ItemStats(Dictionary[] BasicOption, - Dictionary[] SpecialOption) { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator ItemStats?(Maple2.Model.Game.ItemStats? other) { - if (other == null) { - return null; - } - - Maple2.Model.Game.ItemStats.Type[] values = Enum.GetValues(); - var basicOption = new Dictionary[values.Length]; - var specialOption = new Dictionary[values.Length]; - foreach (Maple2.Model.Game.ItemStats.Type type in values) { - basicOption[(int) type] = other[type].Basic; - specialOption[(int) type] = other[type].Special; - } - - return new ItemStats(basicOption, specialOption); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.ItemStats?(ItemStats? other) { - return other == null ? null : new Maple2.Model.Game.ItemStats(other.BasicOption, other.SpecialOption); - } -} - -internal record ItemEnchant(int Enchants, int EnchantExp, byte EnchantCharges, bool Tradeable, int Charges, - Dictionary BasicOptions) { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator ItemEnchant?(Maple2.Model.Game.ItemEnchant? other) { - return other == null ? null : new ItemEnchant(other.Enchants, other.EnchantExp, other.EnchantCharges, - other.Tradeable, other.Charges, other.BasicOptions); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.ItemEnchant?(ItemEnchant? other) { - return other == null ? null : new Maple2.Model.Game.ItemEnchant(other.Enchants, other.EnchantExp, - other.EnchantCharges, other.Tradeable, other.Charges, other.BasicOptions); - } -} - -internal record ItemLimitBreak(int Level, IDictionary BasicOptions, - IDictionary SpecialOptions) { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator ItemLimitBreak?(Maple2.Model.Game.ItemLimitBreak? other) { - return other == null ? null : new ItemLimitBreak(other.Level, other.BasicOptions, other.SpecialOptions); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.ItemLimitBreak?(ItemLimitBreak? other) { - return other == null ? null : - new Maple2.Model.Game.ItemLimitBreak(other.Level, other.BasicOptions, other.SpecialOptions); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Model.Enum; +using Maple2.Model.Game; + +namespace Maple2.Database.Model; + +internal record ItemStats(Dictionary[] BasicOption, + Dictionary[] SpecialOption) { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator ItemStats?(Maple2.Model.Game.ItemStats? other) { + if (other == null) { + return null; + } + + Maple2.Model.Game.ItemStats.Type[] values = Enum.GetValues(); + var basicOption = new Dictionary[values.Length]; + var specialOption = new Dictionary[values.Length]; + foreach (Maple2.Model.Game.ItemStats.Type type in values) { + basicOption[(int) type] = other[type].Basic; + specialOption[(int) type] = other[type].Special; + } + + return new ItemStats(basicOption, specialOption); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.ItemStats?(ItemStats? other) { + return other == null ? null : new Maple2.Model.Game.ItemStats(other.BasicOption, other.SpecialOption); + } +} + +internal record ItemEnchant(int Enchants, int EnchantExp, byte EnchantCharges, bool Tradeable, int Charges, + Dictionary BasicOptions) { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator ItemEnchant?(Maple2.Model.Game.ItemEnchant? other) { + return other == null ? null : new ItemEnchant(other.Enchants, other.EnchantExp, other.EnchantCharges, + other.Tradeable, other.Charges, other.BasicOptions); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.ItemEnchant?(ItemEnchant? other) { + return other == null ? null : new Maple2.Model.Game.ItemEnchant(other.Enchants, other.EnchantExp, + other.EnchantCharges, other.Tradeable, other.Charges, other.BasicOptions); + } +} + +internal record ItemLimitBreak(int Level, IDictionary BasicOptions, + IDictionary SpecialOptions) { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator ItemLimitBreak?(Maple2.Model.Game.ItemLimitBreak? other) { + return other == null ? null : new ItemLimitBreak(other.Level, other.BasicOptions, other.SpecialOptions); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.ItemLimitBreak?(ItemLimitBreak? other) { + return other == null ? null : + new Maple2.Model.Game.ItemLimitBreak(other.Level, other.BasicOptions, other.SpecialOptions); + } +} diff --git a/Maple2.Database/Model/Item/ItemSubType.cs b/Maple2.Database/Model/Item/ItemSubType.cs index eece4516a..8c7653726 100644 --- a/Maple2.Database/Model/Item/ItemSubType.cs +++ b/Maple2.Database/Model/Item/ItemSubType.cs @@ -1,135 +1,135 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; -using Maple2.Model.Enum; - -namespace Maple2.Database.Model; - -[JsonPolymorphic(TypeDiscriminatorPropertyName = "!")] -[JsonDerivedType(typeof(ItemUgc), typeDiscriminator: "ugc")] -[JsonDerivedType(typeof(ItemPet), typeDiscriminator: "pet")] -[JsonDerivedType(typeof(ItemCustomMusicScore), typeDiscriminator: "music")] -[JsonDerivedType(typeof(ItemBadge), typeDiscriminator: "badge")] -internal abstract record ItemSubType; - -internal record ItemUgc(UgcItemLook Template, ItemBlueprint Blueprint) : ItemSubType; - -internal record UgcItemLook( - long Id, - string FileName, - string Name, - long AccountId, - long CharacterId, - string Author, - long CreationTime, - string Url) { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator UgcItemLook?(Maple2.Model.Game.UgcItemLook? other) { - return other == null ? null : new UgcItemLook(other.Id, other.FileName, other.Name, other.AccountId, other.CharacterId, - other.Author, other.CreationTime, other.Url); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.UgcItemLook?(UgcItemLook? other) { - return other == null ? null : new Maple2.Model.Game.UgcItemLook { - Id = other.Id, - FileName = other.FileName, - Name = other.Name, - AccountId = other.AccountId, - CharacterId = other.CharacterId, - Author = other.Author, - CreationTime = other.CreationTime, - Url = other.Url, - }; - } -} - -internal record ItemBlueprint( - long BlueprintUid, - int Length, - int Width, - int Height, - DateTimeOffset CreationTime, - int Type, - long AccountId, - long CharacterId, - string CharacterName) { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator ItemBlueprint?(Maple2.Model.Game.ItemBlueprint? other) { - return other == null ? null : new ItemBlueprint(other.BlueprintUid, other.Length, other.Width, other.Height, other.CreationTime, (int) other.Type, other.AccountId, other.CharacterId, other.CharacterName); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.ItemBlueprint?(ItemBlueprint? other) { - return other == null ? null : new Maple2.Model.Game.ItemBlueprint { - BlueprintUid = other.BlueprintUid, - Length = other.Length, - Width = other.Width, - Height = other.Height, - CreationTime = other.CreationTime, - Type = (BlueprintType) other.Type, - AccountId = other.AccountId, - CharacterId = other.CharacterId, - CharacterName = other.CharacterName, - }; - } -} - -internal record ItemPet(string Name, long Exp, int EvolvePoints, short Level, short RenameRemaining) : ItemSubType { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator ItemPet?(Maple2.Model.Game.ItemPet? other) { - return other == null ? null : new ItemPet(other.Name, other.Exp, other.EvolvePoints, other.Level, other.RenameRemaining); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.ItemPet?(ItemPet? other) { - return other == null ? null : new Maple2.Model.Game.ItemPet { - Name = other.Name, - Exp = other.Exp, - EvolvePoints = other.EvolvePoints, - Level = other.Level, - RenameRemaining = other.RenameRemaining, - }; - } -} - -internal record ItemCustomMusicScore( - int Length, - Instrument Instrument, - string Title, - string Author, - long AuthorId, - bool IsLocked, - string Mml) : ItemSubType { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator ItemCustomMusicScore?(Maple2.Model.Game.ItemCustomMusicScore? other) { - return other == null ? null : new ItemCustomMusicScore(other.Length, other.Instrument, other.Title, - other.Author, other.AuthorId, other.IsLocked, other.Mml); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.ItemCustomMusicScore?(ItemCustomMusicScore? other) { - return other == null ? null : new Maple2.Model.Game.ItemCustomMusicScore { - Length = other.Length, - Instrument = other.Instrument, - Title = other.Title, - Author = other.Author, - AuthorId = other.AuthorId, - IsLocked = other.IsLocked, - Mml = other.Mml, - }; - } -} - -internal record ItemBadge(int Id, bool[] Transparency, int PetSkinId) : ItemSubType { - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator ItemBadge?(Maple2.Model.Game.ItemBadge? other) { - return other == null ? null : new ItemBadge(other.Id, other.Transparency, other.PetSkinId); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.ItemBadge?(ItemBadge? other) { - return other == null ? null : new Maple2.Model.Game.ItemBadge(other.Id, other.Transparency) { - PetSkinId = other.PetSkinId, - }; - } -} +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; +using Maple2.Model.Enum; + +namespace Maple2.Database.Model; + +[JsonPolymorphic(TypeDiscriminatorPropertyName = "!")] +[JsonDerivedType(typeof(ItemUgc), typeDiscriminator: "ugc")] +[JsonDerivedType(typeof(ItemPet), typeDiscriminator: "pet")] +[JsonDerivedType(typeof(ItemCustomMusicScore), typeDiscriminator: "music")] +[JsonDerivedType(typeof(ItemBadge), typeDiscriminator: "badge")] +internal abstract record ItemSubType; + +internal record ItemUgc(UgcItemLook Template, ItemBlueprint Blueprint) : ItemSubType; + +internal record UgcItemLook( + long Id, + string FileName, + string Name, + long AccountId, + long CharacterId, + string Author, + long CreationTime, + string Url) { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator UgcItemLook?(Maple2.Model.Game.UgcItemLook? other) { + return other == null ? null : new UgcItemLook(other.Id, other.FileName, other.Name, other.AccountId, other.CharacterId, + other.Author, other.CreationTime, other.Url); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.UgcItemLook?(UgcItemLook? other) { + return other == null ? null : new Maple2.Model.Game.UgcItemLook { + Id = other.Id, + FileName = other.FileName, + Name = other.Name, + AccountId = other.AccountId, + CharacterId = other.CharacterId, + Author = other.Author, + CreationTime = other.CreationTime, + Url = other.Url, + }; + } +} + +internal record ItemBlueprint( + long BlueprintUid, + int Length, + int Width, + int Height, + DateTimeOffset CreationTime, + int Type, + long AccountId, + long CharacterId, + string CharacterName) { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator ItemBlueprint?(Maple2.Model.Game.ItemBlueprint? other) { + return other == null ? null : new ItemBlueprint(other.BlueprintUid, other.Length, other.Width, other.Height, other.CreationTime, (int) other.Type, other.AccountId, other.CharacterId, other.CharacterName); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.ItemBlueprint?(ItemBlueprint? other) { + return other == null ? null : new Maple2.Model.Game.ItemBlueprint { + BlueprintUid = other.BlueprintUid, + Length = other.Length, + Width = other.Width, + Height = other.Height, + CreationTime = other.CreationTime, + Type = (BlueprintType) other.Type, + AccountId = other.AccountId, + CharacterId = other.CharacterId, + CharacterName = other.CharacterName, + }; + } +} + +internal record ItemPet(string Name, long Exp, int EvolvePoints, short Level, short RenameRemaining) : ItemSubType { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator ItemPet?(Maple2.Model.Game.ItemPet? other) { + return other == null ? null : new ItemPet(other.Name, other.Exp, other.EvolvePoints, other.Level, other.RenameRemaining); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.ItemPet?(ItemPet? other) { + return other == null ? null : new Maple2.Model.Game.ItemPet { + Name = other.Name, + Exp = other.Exp, + EvolvePoints = other.EvolvePoints, + Level = other.Level, + RenameRemaining = other.RenameRemaining, + }; + } +} + +internal record ItemCustomMusicScore( + int Length, + Instrument Instrument, + string Title, + string Author, + long AuthorId, + bool IsLocked, + string Mml) : ItemSubType { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator ItemCustomMusicScore?(Maple2.Model.Game.ItemCustomMusicScore? other) { + return other == null ? null : new ItemCustomMusicScore(other.Length, other.Instrument, other.Title, + other.Author, other.AuthorId, other.IsLocked, other.Mml); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.ItemCustomMusicScore?(ItemCustomMusicScore? other) { + return other == null ? null : new Maple2.Model.Game.ItemCustomMusicScore { + Length = other.Length, + Instrument = other.Instrument, + Title = other.Title, + Author = other.Author, + AuthorId = other.AuthorId, + IsLocked = other.IsLocked, + Mml = other.Mml, + }; + } +} + +internal record ItemBadge(int Id, bool[] Transparency, int PetSkinId) : ItemSubType { + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator ItemBadge?(Maple2.Model.Game.ItemBadge? other) { + return other == null ? null : new ItemBadge(other.Id, other.Transparency, other.PetSkinId); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.ItemBadge?(ItemBadge? other) { + return other == null ? null : new Maple2.Model.Game.ItemBadge(other.Id, other.Transparency) { + PetSkinId = other.PetSkinId, + }; + } +} diff --git a/Maple2.Database/Model/ItemStorage.cs b/Maple2.Database/Model/ItemStorage.cs index a44abe2ee..cf31cdd94 100644 --- a/Maple2.Database/Model/ItemStorage.cs +++ b/Maple2.Database/Model/ItemStorage.cs @@ -1,19 +1,19 @@ -using Maple2.Database.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class ItemStorage { - public long AccountId { get; set; } - - public long Meso { get; set; } - public short Expand { get; set; } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("item-storage"); - builder.HasKey(storage => storage.AccountId); - builder.OneToOne() - .HasForeignKey(storage => storage.AccountId); - } -} +using Maple2.Database.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class ItemStorage { + public long AccountId { get; set; } + + public long Meso { get; set; } + public short Expand { get; set; } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("item-storage"); + builder.HasKey(storage => storage.AccountId); + builder.OneToOne() + .HasForeignKey(storage => storage.AccountId); + } +} diff --git a/Maple2.Database/Model/Mail.cs b/Maple2.Database/Model/Mail.cs index cfca17f25..f513b6532 100644 --- a/Maple2.Database/Model/Mail.cs +++ b/Maple2.Database/Model/Mail.cs @@ -1,110 +1,110 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class Mail { - public long ReceiverId { get; set; } // Can be either AccountId or CharacterId - public long Id { get; set; } - public MailType Type { get; set; } - public long SenderId { get; set; } - public string SenderName { get; set; } = string.Empty; - public string Title { get; set; } = string.Empty; - public string Content { get; set; } = string.Empty; - public string WeddingInvite { get; set; } = string.Empty; - - // List is used here to preserve order - public IList TitleArgs { get; set; } = []; - public IList ContentArgs { get; set; } = []; - - public required MailCurrency Currency { get; set; } - - public DateTime ReadTime { get; set; } - public DateTime ExpiryTime { get; set; } - public DateTime SendTime { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Mail?(Maple2.Model.Game.Mail? other) { - return other == null ? null : new Mail { - ReceiverId = other.ReceiverId, - Id = other.Id, - SenderId = other.SenderId, - Type = other.Type, - SenderName = other.SenderName, - Title = other.Title, - Content = other.Content, - TitleArgs = other.TitleArgs.Select(entry => $"{entry.Key}={entry.Value}").ToArray(), - ContentArgs = other.ContentArgs.Select(entry => $"{entry.Key}={entry.Value}").ToArray(), - WeddingInvite = other.WeddingInvite, - Currency = new MailCurrency { - Meso = other.Meso, - MesoCollectTime = other.MesoCollectTime, - Meret = other.Meret, - MeretCollectTime = other.MeretCollectTime, - GameMeret = other.GameMeret, - GameMeretCollectTime = other.GameMeretCollectTime, - }, - ReadTime = other.ReadTime.FromEpochSeconds(), - ExpiryTime = other.ExpiryTime.FromEpochSeconds(), - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.Mail?(Mail? other) { - return other == null ? null : new Maple2.Model.Game.Mail { - ReceiverId = other.ReceiverId, - Id = other.Id, - SenderId = other.SenderId, - Type = other.Type, - SenderName = other.SenderName, - Title = other.Title, - Content = other.Content, - TitleArgs = other.TitleArgs.Select(arg => { - string[] split = arg.Split("=", 2); - return split.Length > 1 ? (split[0], split[1]) : ("key", split[0]); - }).ToArray(), - ContentArgs = other.ContentArgs.Select(arg => { - string[] split = arg.Split("=", 2); - return split.Length > 1 ? (split[0], split[1]) : ("key", split[0]); - }).ToArray(), - WeddingInvite = other.WeddingInvite, - Meso = other.Currency.Meso, - MesoCollectTime = other.Currency.MesoCollectTime, - Meret = other.Currency.Meret, - MeretCollectTime = other.Currency.MeretCollectTime, - GameMeret = other.Currency.GameMeret, - GameMeretCollectTime = other.Currency.GameMeretCollectTime, - ReadTime = other.ReadTime.ToEpochSeconds(), - ExpiryTime = other.ExpiryTime.ToEpochSeconds(), - SendTime = other.SendTime.ToEpochSeconds(), - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("mail"); - builder.HasKey(mail => new { mail.ReceiverId, mail.Id }); - - builder.Property(mail => mail.Id).ValueGeneratedOnAdd(); - builder.Property(mail => mail.ReceiverId).IsRequired(); - builder.Property(mail => mail.TitleArgs).HasJsonConversion().IsRequired(); - builder.Property(mail => mail.ContentArgs).HasJsonConversion().IsRequired(); - builder.Property(mail => mail.Currency).HasJsonConversion(); - - IMutableProperty sendTime = builder.Property(mail => mail.SendTime) - .ValueGeneratedOnAdd().Metadata; - sendTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); - } -} - -internal class MailCurrency { - public long Meso { get; set; } - public long MesoCollectTime { get; set; } - public long Meret { get; set; } - public long MeretCollectTime { get; set; } - public long GameMeret { get; set; } - public long GameMeretCollectTime { get; set; } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class Mail { + public long ReceiverId { get; set; } // Can be either AccountId or CharacterId + public long Id { get; set; } + public MailType Type { get; set; } + public long SenderId { get; set; } + public string SenderName { get; set; } = string.Empty; + public string Title { get; set; } = string.Empty; + public string Content { get; set; } = string.Empty; + public string WeddingInvite { get; set; } = string.Empty; + + // List is used here to preserve order + public IList TitleArgs { get; set; } = []; + public IList ContentArgs { get; set; } = []; + + public required MailCurrency Currency { get; set; } + + public DateTime ReadTime { get; set; } + public DateTime ExpiryTime { get; set; } + public DateTime SendTime { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Mail?(Maple2.Model.Game.Mail? other) { + return other == null ? null : new Mail { + ReceiverId = other.ReceiverId, + Id = other.Id, + SenderId = other.SenderId, + Type = other.Type, + SenderName = other.SenderName, + Title = other.Title, + Content = other.Content, + TitleArgs = other.TitleArgs.Select(entry => $"{entry.Key}={entry.Value}").ToArray(), + ContentArgs = other.ContentArgs.Select(entry => $"{entry.Key}={entry.Value}").ToArray(), + WeddingInvite = other.WeddingInvite, + Currency = new MailCurrency { + Meso = other.Meso, + MesoCollectTime = other.MesoCollectTime, + Meret = other.Meret, + MeretCollectTime = other.MeretCollectTime, + GameMeret = other.GameMeret, + GameMeretCollectTime = other.GameMeretCollectTime, + }, + ReadTime = other.ReadTime.FromEpochSeconds(), + ExpiryTime = other.ExpiryTime.FromEpochSeconds(), + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.Mail?(Mail? other) { + return other == null ? null : new Maple2.Model.Game.Mail { + ReceiverId = other.ReceiverId, + Id = other.Id, + SenderId = other.SenderId, + Type = other.Type, + SenderName = other.SenderName, + Title = other.Title, + Content = other.Content, + TitleArgs = other.TitleArgs.Select(arg => { + string[] split = arg.Split("=", 2); + return split.Length > 1 ? (split[0], split[1]) : ("key", split[0]); + }).ToArray(), + ContentArgs = other.ContentArgs.Select(arg => { + string[] split = arg.Split("=", 2); + return split.Length > 1 ? (split[0], split[1]) : ("key", split[0]); + }).ToArray(), + WeddingInvite = other.WeddingInvite, + Meso = other.Currency.Meso, + MesoCollectTime = other.Currency.MesoCollectTime, + Meret = other.Currency.Meret, + MeretCollectTime = other.Currency.MeretCollectTime, + GameMeret = other.Currency.GameMeret, + GameMeretCollectTime = other.Currency.GameMeretCollectTime, + ReadTime = other.ReadTime.ToEpochSeconds(), + ExpiryTime = other.ExpiryTime.ToEpochSeconds(), + SendTime = other.SendTime.ToEpochSeconds(), + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("mail"); + builder.HasKey(mail => new { mail.ReceiverId, mail.Id }); + + builder.Property(mail => mail.Id).ValueGeneratedOnAdd(); + builder.Property(mail => mail.ReceiverId).IsRequired(); + builder.Property(mail => mail.TitleArgs).HasJsonConversion().IsRequired(); + builder.Property(mail => mail.ContentArgs).HasJsonConversion().IsRequired(); + builder.Property(mail => mail.Currency).HasJsonConversion(); + + IMutableProperty sendTime = builder.Property(mail => mail.SendTime) + .ValueGeneratedOnAdd().Metadata; + sendTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + } +} + +internal class MailCurrency { + public long Meso { get; set; } + public long MesoCollectTime { get; set; } + public long Meret { get; set; } + public long MeretCollectTime { get; set; } + public long GameMeret { get; set; } + public long GameMeretCollectTime { get; set; } +} diff --git a/Maple2.Database/Model/Map/Home.cs b/Maple2.Database/Model/Map/Home.cs index a6094bb85..f7b580cf5 100644 --- a/Maple2.Database/Model/Map/Home.cs +++ b/Maple2.Database/Model/Map/Home.cs @@ -1,111 +1,111 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class Home { - public long AccountId { get; set; } - - public string Message { get; set; } = string.Empty; - public byte Area { get; set; } - public byte Height { get; set; } - - public int CurrentArchitectScore { get; set; } - public int ArchitectScore { get; set; } - - public long DecorationLevel { get; set; } - public long DecorationExp { get; set; } - public long DecorationRewardTimestamp { get; set; } - public List InteriorRewardsClaimed { get; set; } = []; - - // Interior Settings - public HomeBackground Background { get; set; } - public HomeLighting Lighting { get; set; } - public HomeCamera Camera { get; set; } - - public string? Passcode { get; set; } - public IDictionary Permissions { get; set; } = new Dictionary(); - public List Layouts { get; set; } = []; - public List Blueprints { get; set; } = []; - - public DateTime LastModified { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Home?(Maple2.Model.Game.Home? other) { - return other == null ? null : new Home { - AccountId = other.AccountId, - - Message = other.Message, - Area = other.Area, - Height = other.Height, - CurrentArchitectScore = other.CurrentArchitectScore, - ArchitectScore = other.ArchitectScore, - Background = other.Background, - Lighting = other.Lighting, - Camera = other.Camera, - Passcode = other.Passcode, - Permissions = other.Permissions, - Layouts = other.Layouts.Select(layout => layout.Uid).ToList(), - Blueprints = other.Blueprints.Select(layout => layout.Uid).ToList(), - DecorationLevel = other.DecorationLevel, - DecorationExp = other.DecorationExp, - DecorationRewardTimestamp = other.DecorationRewardTimestamp, - InteriorRewardsClaimed = other.InteriorRewardsClaimed, - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.Home?(Home? other) { - if (other == null) { - return null; - } - - var home = new Maple2.Model.Game.Home { - AccountId = other.AccountId, - Message = other.Message, - CurrentArchitectScore = other.CurrentArchitectScore, - ArchitectScore = other.ArchitectScore, - Passcode = other.Passcode, - LastModified = other.LastModified.ToEpochSeconds(), - DecorationLevel = other.DecorationLevel, - DecorationExp = other.DecorationExp, - DecorationRewardTimestamp = other.DecorationRewardTimestamp, - InteriorRewardsClaimed = other.InteriorRewardsClaimed, - }; - - home.SetArea(other.Area); - home.SetHeight(other.Height); - home.SetBackground(other.Background); - home.SetLighting(other.Lighting); - home.SetCamera(other.Camera); - foreach ((HomePermission permission, HomePermissionSetting setting) in other.Permissions) { - home.Permissions[permission] = setting; - } - - return home; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("home"); - builder.HasKey(home => home.AccountId); - builder.OneToOne() - .HasForeignKey(home => home.AccountId); - - builder.Property(home => home.Area) - .HasDefaultValue(Constant.MinHomeArea); - builder.Property(home => home.Height) - .HasDefaultValue(Constant.MinHomeHeight); - - builder.Property(home => home.Permissions).HasJsonConversion(); - builder.Property(home => home.Layouts).HasJsonConversion(); - builder.Property(home => home.Blueprints).HasJsonConversion(); - builder.Property(home => home.InteriorRewardsClaimed).HasJsonConversion(); - - builder.Property(map => map.LastModified) - .ValueGeneratedOnAddOrUpdate(); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class Home { + public long AccountId { get; set; } + + public string Message { get; set; } = string.Empty; + public byte Area { get; set; } + public byte Height { get; set; } + + public int CurrentArchitectScore { get; set; } + public int ArchitectScore { get; set; } + + public long DecorationLevel { get; set; } + public long DecorationExp { get; set; } + public long DecorationRewardTimestamp { get; set; } + public List InteriorRewardsClaimed { get; set; } = []; + + // Interior Settings + public HomeBackground Background { get; set; } + public HomeLighting Lighting { get; set; } + public HomeCamera Camera { get; set; } + + public string? Passcode { get; set; } + public IDictionary Permissions { get; set; } = new Dictionary(); + public List Layouts { get; set; } = []; + public List Blueprints { get; set; } = []; + + public DateTime LastModified { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Home?(Maple2.Model.Game.Home? other) { + return other == null ? null : new Home { + AccountId = other.AccountId, + + Message = other.Message, + Area = other.Area, + Height = other.Height, + CurrentArchitectScore = other.CurrentArchitectScore, + ArchitectScore = other.ArchitectScore, + Background = other.Background, + Lighting = other.Lighting, + Camera = other.Camera, + Passcode = other.Passcode, + Permissions = other.Permissions, + Layouts = other.Layouts.Select(layout => layout.Uid).ToList(), + Blueprints = other.Blueprints.Select(layout => layout.Uid).ToList(), + DecorationLevel = other.DecorationLevel, + DecorationExp = other.DecorationExp, + DecorationRewardTimestamp = other.DecorationRewardTimestamp, + InteriorRewardsClaimed = other.InteriorRewardsClaimed, + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.Home?(Home? other) { + if (other == null) { + return null; + } + + var home = new Maple2.Model.Game.Home { + AccountId = other.AccountId, + Message = other.Message, + CurrentArchitectScore = other.CurrentArchitectScore, + ArchitectScore = other.ArchitectScore, + Passcode = other.Passcode, + LastModified = other.LastModified.ToEpochSeconds(), + DecorationLevel = other.DecorationLevel, + DecorationExp = other.DecorationExp, + DecorationRewardTimestamp = other.DecorationRewardTimestamp, + InteriorRewardsClaimed = other.InteriorRewardsClaimed, + }; + + home.SetArea(other.Area); + home.SetHeight(other.Height); + home.SetBackground(other.Background); + home.SetLighting(other.Lighting); + home.SetCamera(other.Camera); + foreach ((HomePermission permission, HomePermissionSetting setting) in other.Permissions) { + home.Permissions[permission] = setting; + } + + return home; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("home"); + builder.HasKey(home => home.AccountId); + builder.OneToOne() + .HasForeignKey(home => home.AccountId); + + builder.Property(home => home.Area) + .HasDefaultValue(Constant.MinHomeArea); + builder.Property(home => home.Height) + .HasDefaultValue(Constant.MinHomeHeight); + + builder.Property(home => home.Permissions).HasJsonConversion(); + builder.Property(home => home.Layouts).HasJsonConversion(); + builder.Property(home => home.Blueprints).HasJsonConversion(); + builder.Property(home => home.InteriorRewardsClaimed).HasJsonConversion(); + + builder.Property(map => map.LastModified) + .ValueGeneratedOnAddOrUpdate(); + } +} diff --git a/Maple2.Database/Model/Map/HomeLayout.cs b/Maple2.Database/Model/Map/HomeLayout.cs index 4fa22176a..ea9882048 100644 --- a/Maple2.Database/Model/Map/HomeLayout.cs +++ b/Maple2.Database/Model/Map/HomeLayout.cs @@ -1,40 +1,40 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Model.Enum; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class HomeLayout { - public long Uid { get; set; } - public int Id { get; set; } - public string Name { get; set; } - public byte Area { get; set; } - public byte Height { get; set; } - public HomeBackground Background { get; set; } - public HomeLighting Lighting { get; set; } - public HomeCamera Camera { get; set; } - public DateTimeOffset Timestamp { get; set; } - public List Cubes { get; set; } = null!; - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator HomeLayout?(Maple2.Model.Game.HomeLayout? other) { - return other == null ? null : new HomeLayout { - Uid = other.Uid, - Id = other.Id, - Name = other.Name, - Area = other.Area, - Height = other.Height, - Timestamp = other.Timestamp, - Cubes = other.Cubes.ConvertAll(cube => (HomeLayoutCube) cube), - Background = other.Background, - Lighting = other.Lighting, - Camera = other.Camera, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("home-layout"); - builder.HasKey(layout => layout.Uid); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Model.Enum; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class HomeLayout { + public long Uid { get; set; } + public int Id { get; set; } + public string Name { get; set; } + public byte Area { get; set; } + public byte Height { get; set; } + public HomeBackground Background { get; set; } + public HomeLighting Lighting { get; set; } + public HomeCamera Camera { get; set; } + public DateTimeOffset Timestamp { get; set; } + public List Cubes { get; set; } = null!; + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator HomeLayout?(Maple2.Model.Game.HomeLayout? other) { + return other == null ? null : new HomeLayout { + Uid = other.Uid, + Id = other.Id, + Name = other.Name, + Area = other.Area, + Height = other.Height, + Timestamp = other.Timestamp, + Cubes = other.Cubes.ConvertAll(cube => (HomeLayoutCube) cube), + Background = other.Background, + Lighting = other.Lighting, + Camera = other.Camera, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("home-layout"); + builder.HasKey(layout => layout.Uid); + } +} diff --git a/Maple2.Database/Model/Map/HomeLayoutCube.cs b/Maple2.Database/Model/Map/HomeLayoutCube.cs index ede4288c8..3224d163a 100644 --- a/Maple2.Database/Model/Map/HomeLayoutCube.cs +++ b/Maple2.Database/Model/Map/HomeLayoutCube.cs @@ -1,46 +1,46 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Game; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -// ReSharper disable ReplaceConditionalExpressionWithNullCoalescing - -namespace Maple2.Database.Model; - -internal class HomeLayoutCube { - public long Id { get; set; } - public long HomeLayoutId { get; set; } - public sbyte X { get; set; } - public sbyte Y { get; set; } - public sbyte Z { get; set; } - public float Rotation { get; set; } - - public int ItemId { get; set; } - public InteractCube? Interact { get; set; } - public UgcItemLook? Template { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator HomeLayoutCube?(PlotCube? other) { - return other == null ? null : new HomeLayoutCube { - X = other.Position.X, - Y = other.Position.Y, - Z = other.Position.Z, - Rotation = other.Rotation, - ItemId = other.ItemId, - Template = other.Template, - Interact = other.Interact, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("home-layout-cube"); - builder.HasKey(cube => cube.Id); - - builder.HasOne() - .WithMany(ugcMap => ugcMap.Cubes) - .HasForeignKey(cube => cube.HomeLayoutId); - - builder.Property(cube => cube.Template).HasJsonConversion(); - builder.Property(cube => cube.Interact).HasJsonConversion(); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Game; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +// ReSharper disable ReplaceConditionalExpressionWithNullCoalescing + +namespace Maple2.Database.Model; + +internal class HomeLayoutCube { + public long Id { get; set; } + public long HomeLayoutId { get; set; } + public sbyte X { get; set; } + public sbyte Y { get; set; } + public sbyte Z { get; set; } + public float Rotation { get; set; } + + public int ItemId { get; set; } + public InteractCube? Interact { get; set; } + public UgcItemLook? Template { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator HomeLayoutCube?(PlotCube? other) { + return other == null ? null : new HomeLayoutCube { + X = other.Position.X, + Y = other.Position.Y, + Z = other.Position.Z, + Rotation = other.Rotation, + ItemId = other.ItemId, + Template = other.Template, + Interact = other.Interact, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("home-layout-cube"); + builder.HasKey(cube => cube.Id); + + builder.HasOne() + .WithMany(ugcMap => ugcMap.Cubes) + .HasForeignKey(cube => cube.HomeLayoutId); + + builder.Property(cube => cube.Template).HasJsonConversion(); + builder.Property(cube => cube.Interact).HasJsonConversion(); + } +} diff --git a/Maple2.Database/Model/Map/InteractCube.cs b/Maple2.Database/Model/Map/InteractCube.cs index cb380637a..062c46d43 100644 --- a/Maple2.Database/Model/Map/InteractCube.cs +++ b/Maple2.Database/Model/Map/InteractCube.cs @@ -1,72 +1,72 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; - -namespace Maple2.Database.Model; - -internal record InteractCube( - string Id, - int ObjectCode, - CubePortalSettings? PortalSettings, - CubeNoticeSettings? NoticeSettings) { - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator InteractCube?(Maple2.Model.Game.InteractCube? other) { - return other == null ? null : new InteractCube( - other.Id, - other.ObjectCode, - other.PortalSettings, - other.NoticeSettings); - } - - // Use explicit Convert() here because we need metadata to construct InteractCube. - public Maple2.Model.Game.InteractCube Convert(FunctionCubeMetadata metadata, CubeNoticeSettings? noticeSettings, CubePortalSettings? portalSettings) { - return new Maple2.Model.Game.InteractCube(Id, metadata, portalSettings, noticeSettings); - } -} - -internal record CubePortalSettings( - string PortalName, - PortalActionType Method, - CubePortalDestination Destination, - string DestinationTarget) { - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator CubePortalSettings?(Maple2.Model.Game.CubePortalSettings? other) { - return other == null ? null : new CubePortalSettings( - other.PortalName, - other.Method, - other.Destination, - other.DestinationTarget); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.CubePortalSettings?(CubePortalSettings? other) { - return other == null ? null : new Maple2.Model.Game.CubePortalSettings { - PortalName = other.PortalName, - Method = other.Method, - Destination = other.Destination, - DestinationTarget = other.DestinationTarget, - }; - } -} - -internal record CubeNoticeSettings( - string Notice, - byte Distance) { - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator CubeNoticeSettings?(Maple2.Model.Game.CubeNoticeSettings? other) { - return other == null ? null : new CubeNoticeSettings( - other.Notice, - other.Distance); - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.CubeNoticeSettings?(CubeNoticeSettings? other) { - return other == null ? null : new Maple2.Model.Game.CubeNoticeSettings { - Notice = other.Notice, - Distance = other.Distance, - }; - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; + +namespace Maple2.Database.Model; + +internal record InteractCube( + string Id, + int ObjectCode, + CubePortalSettings? PortalSettings, + CubeNoticeSettings? NoticeSettings) { + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator InteractCube?(Maple2.Model.Game.InteractCube? other) { + return other == null ? null : new InteractCube( + other.Id, + other.ObjectCode, + other.PortalSettings, + other.NoticeSettings); + } + + // Use explicit Convert() here because we need metadata to construct InteractCube. + public Maple2.Model.Game.InteractCube Convert(FunctionCubeMetadata metadata, CubeNoticeSettings? noticeSettings, CubePortalSettings? portalSettings) { + return new Maple2.Model.Game.InteractCube(Id, metadata, portalSettings, noticeSettings); + } +} + +internal record CubePortalSettings( + string PortalName, + PortalActionType Method, + CubePortalDestination Destination, + string DestinationTarget) { + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator CubePortalSettings?(Maple2.Model.Game.CubePortalSettings? other) { + return other == null ? null : new CubePortalSettings( + other.PortalName, + other.Method, + other.Destination, + other.DestinationTarget); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.CubePortalSettings?(CubePortalSettings? other) { + return other == null ? null : new Maple2.Model.Game.CubePortalSettings { + PortalName = other.PortalName, + Method = other.Method, + Destination = other.Destination, + DestinationTarget = other.DestinationTarget, + }; + } +} + +internal record CubeNoticeSettings( + string Notice, + byte Distance) { + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator CubeNoticeSettings?(Maple2.Model.Game.CubeNoticeSettings? other) { + return other == null ? null : new CubeNoticeSettings( + other.Notice, + other.Distance); + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.CubeNoticeSettings?(CubeNoticeSettings? other) { + return other == null ? null : new Maple2.Model.Game.CubeNoticeSettings { + Notice = other.Notice, + Distance = other.Distance, + }; + } +} diff --git a/Maple2.Database/Model/Map/Nurturing.cs b/Maple2.Database/Model/Map/Nurturing.cs index 3f305772f..4957cc564 100644 --- a/Maple2.Database/Model/Map/Nurturing.cs +++ b/Maple2.Database/Model/Map/Nurturing.cs @@ -1,25 +1,25 @@ -using Maple2.Database.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class Nurturing { - public long AccountId { get; set; } - public int InteractId { get; set; } - public long Exp { get; set; } - public short ClaimedGiftForStage { get; set; } - public DateTime CreationTime { get; set; } - public DateTime LastFeedTime { get; set; } - public long[] PlayedBy { get; set; } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("nurturing"); - builder.HasKey(nurturing => new { - nurturing.AccountId, - InteractId = nurturing.InteractId, - }); - - builder.Property(nurturing => nurturing.PlayedBy).HasJsonConversion(); - } -} +using Maple2.Database.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class Nurturing { + public long AccountId { get; set; } + public int InteractId { get; set; } + public long Exp { get; set; } + public short ClaimedGiftForStage { get; set; } + public DateTime CreationTime { get; set; } + public DateTime LastFeedTime { get; set; } + public long[] PlayedBy { get; set; } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("nurturing"); + builder.HasKey(nurturing => new { + nurturing.AccountId, + InteractId = nurturing.InteractId, + }); + + builder.Property(nurturing => nurturing.PlayedBy).HasJsonConversion(); + } +} diff --git a/Maple2.Database/Model/Map/UgcBannerSlot.cs b/Maple2.Database/Model/Map/UgcBannerSlot.cs index c6869c308..8b708df85 100644 --- a/Maple2.Database/Model/Map/UgcBannerSlot.cs +++ b/Maple2.Database/Model/Map/UgcBannerSlot.cs @@ -1,38 +1,38 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class BannerSlot { - public long Id { get; set; } - public long BannerId { get; set; } - public DateTimeOffset ActivateTime { get; set; } - - public UgcItemLook? Template { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator BannerSlot?(Maple2.Model.Game.Ugc.BannerSlot? other) { - return other == null ? null : new BannerSlot { - Id = other.Id, - ActivateTime = other.ActivateTime, - BannerId = other.BannerId, - Template = other.Template, - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.Ugc.BannerSlot?(BannerSlot? other) { - return other == null ? null : new Maple2.Model.Game.Ugc.BannerSlot(other.Id, other.ActivateTime, other.BannerId, other.Template); - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("ugc-banner-slot"); - builder.HasKey(slot => slot.Id); - - builder.HasIndex(slot => slot.BannerId); - - builder.Property(slot => slot.Template).HasJsonConversion(); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class BannerSlot { + public long Id { get; set; } + public long BannerId { get; set; } + public DateTimeOffset ActivateTime { get; set; } + + public UgcItemLook? Template { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator BannerSlot?(Maple2.Model.Game.Ugc.BannerSlot? other) { + return other == null ? null : new BannerSlot { + Id = other.Id, + ActivateTime = other.ActivateTime, + BannerId = other.BannerId, + Template = other.Template, + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.Ugc.BannerSlot?(BannerSlot? other) { + return other == null ? null : new Maple2.Model.Game.Ugc.BannerSlot(other.Id, other.ActivateTime, other.BannerId, other.Template); + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("ugc-banner-slot"); + builder.HasKey(slot => slot.Id); + + builder.HasIndex(slot => slot.BannerId); + + builder.Property(slot => slot.Template).HasJsonConversion(); + } +} diff --git a/Maple2.Database/Model/Map/UgcMap.cs b/Maple2.Database/Model/Map/UgcMap.cs index 0ce8afa3b..93e806964 100644 --- a/Maple2.Database/Model/Map/UgcMap.cs +++ b/Maple2.Database/Model/Map/UgcMap.cs @@ -1,45 +1,45 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class UgcMap { - public long Id { get; set; } - public long OwnerId { get; set; } - - public string Name { get; set; } = string.Empty; - public int MapId { get; set; } - public bool Indoor { get; set; } - public int Number { get; set; } - public int ApartmentNumber { get; set; } - - // Referenced by UgcMapCube - public ICollection? Cubes = []; - - public DateTimeOffset ExpiryTime { get; set; } - public DateTimeOffset LastModified { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator UgcMap?(Maple2.Model.Game.PlotInfo? other) { - return other == null ? null : new UgcMap { - Id = other.Id, - OwnerId = other.OwnerId, - MapId = other.MapId, - Number = other.Number, - ApartmentNumber = other.ApartmentNumber, - ExpiryTime = other.ExpiryTime.FromEpochSeconds(), - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("ugcmap"); - builder.HasKey(map => map.Id); - builder.HasIndex(map => map.OwnerId); - builder.HasIndex(map => map.MapId); - - builder.Property(map => map.LastModified) - .IsRowVersion(); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class UgcMap { + public long Id { get; set; } + public long OwnerId { get; set; } + + public string Name { get; set; } = string.Empty; + public int MapId { get; set; } + public bool Indoor { get; set; } + public int Number { get; set; } + public int ApartmentNumber { get; set; } + + // Referenced by UgcMapCube + public ICollection? Cubes = []; + + public DateTimeOffset ExpiryTime { get; set; } + public DateTimeOffset LastModified { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator UgcMap?(Maple2.Model.Game.PlotInfo? other) { + return other == null ? null : new UgcMap { + Id = other.Id, + OwnerId = other.OwnerId, + MapId = other.MapId, + Number = other.Number, + ApartmentNumber = other.ApartmentNumber, + ExpiryTime = other.ExpiryTime.FromEpochSeconds(), + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("ugcmap"); + builder.HasKey(map => map.Id); + builder.HasIndex(map => map.OwnerId); + builder.HasIndex(map => map.MapId); + + builder.Property(map => map.LastModified) + .IsRowVersion(); + } +} diff --git a/Maple2.Database/Model/Map/UgcMapCube.cs b/Maple2.Database/Model/Map/UgcMapCube.cs index cbab2592a..3f20ba98e 100644 --- a/Maple2.Database/Model/Map/UgcMapCube.cs +++ b/Maple2.Database/Model/Map/UgcMapCube.cs @@ -1,49 +1,49 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Game; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -// ReSharper disable ReplaceConditionalExpressionWithNullCoalescing - -namespace Maple2.Database.Model; - -internal class UgcMapCube { - public long Id { get; set; } - public long UgcMapId { get; set; } - public sbyte X { get; set; } - public sbyte Y { get; set; } - public sbyte Z { get; set; } - public float Rotation { get; set; } - - public int ItemId { get; set; } - public InteractCube? Interact { get; set; } - - public UgcItemLook? Template { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator UgcMapCube?(PlotCube? other) { - return other == null ? null : new UgcMapCube { - Id = other.Id, - X = other.Position.X, - Y = other.Position.Y, - Z = other.Position.Z, - Rotation = other.Rotation, - ItemId = other.ItemId, - Template = other.Template, - Interact = other.Interact, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("ugcmap-cube"); - builder.HasKey(cube => cube.Id); - - builder.HasOne() - .WithMany(ugcMap => ugcMap.Cubes) - .HasForeignKey(cube => cube.UgcMapId); - - builder.Property(cube => cube.Template).HasJsonConversion(); - builder.Property(cube => cube.Interact).HasJsonConversion(); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Game; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +// ReSharper disable ReplaceConditionalExpressionWithNullCoalescing + +namespace Maple2.Database.Model; + +internal class UgcMapCube { + public long Id { get; set; } + public long UgcMapId { get; set; } + public sbyte X { get; set; } + public sbyte Y { get; set; } + public sbyte Z { get; set; } + public float Rotation { get; set; } + + public int ItemId { get; set; } + public InteractCube? Interact { get; set; } + + public UgcItemLook? Template { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator UgcMapCube?(PlotCube? other) { + return other == null ? null : new UgcMapCube { + Id = other.Id, + X = other.Position.X, + Y = other.Position.Y, + Z = other.Position.Z, + Rotation = other.Rotation, + ItemId = other.ItemId, + Template = other.Template, + Interact = other.Interact, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("ugcmap-cube"); + builder.HasKey(cube => cube.Id); + + builder.HasOne() + .WithMany(ugcMap => ugcMap.Cubes) + .HasForeignKey(cube => cube.UgcMapId); + + builder.Property(cube => cube.Template).HasJsonConversion(); + builder.Property(cube => cube.Interact).HasJsonConversion(); + } +} diff --git a/Maple2.Database/Model/Market/BlackMarketListing.cs b/Maple2.Database/Model/Market/BlackMarketListing.cs index 90812cf47..6aed0f455 100644 --- a/Maple2.Database/Model/Market/BlackMarketListing.cs +++ b/Maple2.Database/Model/Market/BlackMarketListing.cs @@ -1,56 +1,56 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class BlackMarketListing { - public long Id { get; set; } - public long ItemUid { get; set; } - public DateTime CreationTime { get; set; } - public DateTime ExpiryTime { get; set; } - public long Price { get; set; } - public int Quantity { get; set; } - public long AccountId { get; set; } - public long CharacterId { get; set; } - public long Deposit { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator BlackMarketListing?(Maple2.Model.Game.BlackMarketListing? other) { - return other == null ? null : new BlackMarketListing { - Id = other.Id, - ExpiryTime = other.ExpiryTime.FromEpochSeconds(), - ItemUid = other.Item.Uid, - Price = other.Price, - Quantity = other.Quantity, - CharacterId = other.CharacterId, - AccountId = other.AccountId, - Deposit = other.Deposit, - CreationTime = other.CreationTime.FromEpochSeconds(), - }; - } - - public Maple2.Model.Game.BlackMarketListing Convert(Maple2.Model.Game.Item item) { - var entry = new Maple2.Model.Game.BlackMarketListing(item) { - Id = Id, - ExpiryTime = ExpiryTime.ToEpochSeconds(), - Price = Price, - Quantity = Quantity, - CharacterId = CharacterId, - AccountId = AccountId, - Deposit = Deposit, - CreationTime = CreationTime.ToEpochSeconds(), - }; - - return entry; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("black-market-listing"); - IMutableProperty creationTime = builder.Property(listing => listing.CreationTime) - .ValueGeneratedOnAdd().Metadata; - creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class BlackMarketListing { + public long Id { get; set; } + public long ItemUid { get; set; } + public DateTime CreationTime { get; set; } + public DateTime ExpiryTime { get; set; } + public long Price { get; set; } + public int Quantity { get; set; } + public long AccountId { get; set; } + public long CharacterId { get; set; } + public long Deposit { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator BlackMarketListing?(Maple2.Model.Game.BlackMarketListing? other) { + return other == null ? null : new BlackMarketListing { + Id = other.Id, + ExpiryTime = other.ExpiryTime.FromEpochSeconds(), + ItemUid = other.Item.Uid, + Price = other.Price, + Quantity = other.Quantity, + CharacterId = other.CharacterId, + AccountId = other.AccountId, + Deposit = other.Deposit, + CreationTime = other.CreationTime.FromEpochSeconds(), + }; + } + + public Maple2.Model.Game.BlackMarketListing Convert(Maple2.Model.Game.Item item) { + var entry = new Maple2.Model.Game.BlackMarketListing(item) { + Id = Id, + ExpiryTime = ExpiryTime.ToEpochSeconds(), + Price = Price, + Quantity = Quantity, + CharacterId = CharacterId, + AccountId = AccountId, + Deposit = Deposit, + CreationTime = CreationTime.ToEpochSeconds(), + }; + + return entry; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("black-market-listing"); + IMutableProperty creationTime = builder.Property(listing => listing.CreationTime) + .ValueGeneratedOnAdd().Metadata; + creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + } +} diff --git a/Maple2.Database/Model/Market/MesoListing.cs b/Maple2.Database/Model/Market/MesoListing.cs index 889453f54..7cbcdef4e 100644 --- a/Maple2.Database/Model/Market/MesoListing.cs +++ b/Maple2.Database/Model/Market/MesoListing.cs @@ -1,93 +1,93 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class MesoListingBase { - public long Id { get; set; } - public long AccountId { get; set; } - public long CharacterId { get; set; } - public long Price { get; set; } - public long Amount { get; set; } - - public DateTime LastModified { get; set; } -} - -internal class MesoListing : MesoListingBase { - public DateTime CreationTime { get; set; } - public DateTime ExpiryTime { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator MesoListing?(Maple2.Model.Game.MesoListing? other) { - return other == null ? null : new MesoListing { - Id = other.Id, - AccountId = other.AccountId, - CharacterId = other.CharacterId, - Price = other.Price, - Amount = other.Amount, - ExpiryTime = other.ExpiryTime.FromEpochSeconds(), - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.MesoListing?(MesoListing? other) { - return other == null ? null : new Maple2.Model.Game.MesoListing { - Id = other.Id, - AccountId = other.AccountId, - CharacterId = other.CharacterId, - Price = other.Price, - Amount = other.Amount, - CreationTime = other.CreationTime.ToEpochSeconds(), - ExpiryTime = other.ExpiryTime.ToEpochSeconds(), - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("meso-market"); - builder.HasKey(listing => listing.Id); - builder.HasOne() - .WithMany() - .HasForeignKey(listing => listing.AccountId) - .IsRequired(); - builder.HasOne() - .WithMany() - .HasForeignKey(listing => listing.CharacterId) - .IsRequired(); - - builder.Property(listing => listing.LastModified).IsRowVersion(); - IMutableProperty creationTime = builder.Property(listing => listing.CreationTime) - .ValueGeneratedOnAdd().Metadata; - creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); - } -} - -internal class SoldMesoListing : MesoListingBase { - public DateTime ListedTime { get; set; } - public DateTime SoldTime { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator SoldMesoListing?(MesoListing? other) { - return other == null ? null : new SoldMesoListing { - Id = other.Id, - AccountId = other.AccountId, - CharacterId = other.CharacterId, - Price = other.Price, - Amount = other.Amount, - ListedTime = other.CreationTime, - LastModified = other.LastModified, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("meso-market-sold"); - builder.HasKey(listing => listing.Id); - - builder.Property(listing => listing.LastModified).ValueGeneratedOnAdd(); - IMutableProperty soldTime = builder.Property(listing => listing.SoldTime) - .ValueGeneratedOnAdd().Metadata; - soldTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class MesoListingBase { + public long Id { get; set; } + public long AccountId { get; set; } + public long CharacterId { get; set; } + public long Price { get; set; } + public long Amount { get; set; } + + public DateTime LastModified { get; set; } +} + +internal class MesoListing : MesoListingBase { + public DateTime CreationTime { get; set; } + public DateTime ExpiryTime { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator MesoListing?(Maple2.Model.Game.MesoListing? other) { + return other == null ? null : new MesoListing { + Id = other.Id, + AccountId = other.AccountId, + CharacterId = other.CharacterId, + Price = other.Price, + Amount = other.Amount, + ExpiryTime = other.ExpiryTime.FromEpochSeconds(), + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.MesoListing?(MesoListing? other) { + return other == null ? null : new Maple2.Model.Game.MesoListing { + Id = other.Id, + AccountId = other.AccountId, + CharacterId = other.CharacterId, + Price = other.Price, + Amount = other.Amount, + CreationTime = other.CreationTime.ToEpochSeconds(), + ExpiryTime = other.ExpiryTime.ToEpochSeconds(), + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("meso-market"); + builder.HasKey(listing => listing.Id); + builder.HasOne() + .WithMany() + .HasForeignKey(listing => listing.AccountId) + .IsRequired(); + builder.HasOne() + .WithMany() + .HasForeignKey(listing => listing.CharacterId) + .IsRequired(); + + builder.Property(listing => listing.LastModified).IsRowVersion(); + IMutableProperty creationTime = builder.Property(listing => listing.CreationTime) + .ValueGeneratedOnAdd().Metadata; + creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + } +} + +internal class SoldMesoListing : MesoListingBase { + public DateTime ListedTime { get; set; } + public DateTime SoldTime { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator SoldMesoListing?(MesoListing? other) { + return other == null ? null : new SoldMesoListing { + Id = other.Id, + AccountId = other.AccountId, + CharacterId = other.CharacterId, + Price = other.Price, + Amount = other.Amount, + ListedTime = other.CreationTime, + LastModified = other.LastModified, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("meso-market-sold"); + builder.HasKey(listing => listing.Id); + + builder.Property(listing => listing.LastModified).ValueGeneratedOnAdd(); + IMutableProperty soldTime = builder.Property(listing => listing.SoldTime) + .ValueGeneratedOnAdd().Metadata; + soldTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + } +} diff --git a/Maple2.Database/Model/Market/SoldMeretMarketItem.cs b/Maple2.Database/Model/Market/SoldMeretMarketItem.cs index f2b992ff1..c981fffc8 100644 --- a/Maple2.Database/Model/Market/SoldMeretMarketItem.cs +++ b/Maple2.Database/Model/Market/SoldMeretMarketItem.cs @@ -1,32 +1,32 @@ -using System.Diagnostics.CodeAnalysis; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class SoldMeretMarketItem { - public long Id { get; set; } - public long MarketId { get; set; } - public long Price { get; set; } - public long CharacterId { get; set; } - public DateTime SoldTime { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator SoldMeretMarketItem?(Maple2.Model.Game.PremiumMarketItem? other) { - return other == null ? null : new SoldMeretMarketItem { - MarketId = other.Id, - Price = other.Metadata.SalePrice > 0 ? other.Metadata.SalePrice : other.Price, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("meret-market-sold"); - builder.HasKey(listing => listing.Id); - - builder.Property(listing => listing.SoldTime).ValueGeneratedOnAdd(); - IMutableProperty soldTime = builder.Property(listing => listing.SoldTime) - .ValueGeneratedOnAdd().Metadata; - soldTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); - } -} +using System.Diagnostics.CodeAnalysis; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class SoldMeretMarketItem { + public long Id { get; set; } + public long MarketId { get; set; } + public long Price { get; set; } + public long CharacterId { get; set; } + public DateTime SoldTime { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator SoldMeretMarketItem?(Maple2.Model.Game.PremiumMarketItem? other) { + return other == null ? null : new SoldMeretMarketItem { + MarketId = other.Id, + Price = other.Metadata.SalePrice > 0 ? other.Metadata.SalePrice : other.Price, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("meret-market-sold"); + builder.HasKey(listing => listing.Id); + + builder.Property(listing => listing.SoldTime).ValueGeneratedOnAdd(); + IMutableProperty soldTime = builder.Property(listing => listing.SoldTime) + .ValueGeneratedOnAdd().Metadata; + soldTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + } +} diff --git a/Maple2.Database/Model/Market/SoldUgcMarketItem.cs b/Maple2.Database/Model/Market/SoldUgcMarketItem.cs index cc08ef469..24de29136 100644 --- a/Maple2.Database/Model/Market/SoldUgcMarketItem.cs +++ b/Maple2.Database/Model/Market/SoldUgcMarketItem.cs @@ -1,53 +1,53 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class SoldUgcMarketItem { - public long Id { get; set; } - public long Price { get; set; } - public long Profit { get; set; } - public string Name { get; set; } - public DateTime SoldTime { get; set; } - public long AccountId { get; set; } - - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator SoldUgcMarketItem?(Maple2.Model.Game.SoldUgcMarketItem? other) { - return other == null ? null : new SoldUgcMarketItem { - Id = other.Id, - Price = other.Price, - Profit = other.Profit, - Name = other.Name, - SoldTime = other.SoldTime.FromEpochSeconds(), - AccountId = other.AccountId, - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.SoldUgcMarketItem?(SoldUgcMarketItem? other) { - return other == null ? null : new Maple2.Model.Game.SoldUgcMarketItem { - Id = other.Id, - Price = other.Price, - Profit = other.Profit, - Name = other.Name, - SoldTime = other.SoldTime.ToEpochSeconds(), - AccountId = other.AccountId, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("ugc-market-item-sold"); - builder.HasKey(entry => entry.Id); - builder.HasOne() - .WithMany() - .HasForeignKey(listing => listing.AccountId) - .IsRequired(); - IMutableProperty creationTime = builder.Property(listing => listing.SoldTime) - .ValueGeneratedOnAdd().Metadata; - creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class SoldUgcMarketItem { + public long Id { get; set; } + public long Price { get; set; } + public long Profit { get; set; } + public string Name { get; set; } + public DateTime SoldTime { get; set; } + public long AccountId { get; set; } + + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator SoldUgcMarketItem?(Maple2.Model.Game.SoldUgcMarketItem? other) { + return other == null ? null : new SoldUgcMarketItem { + Id = other.Id, + Price = other.Price, + Profit = other.Profit, + Name = other.Name, + SoldTime = other.SoldTime.FromEpochSeconds(), + AccountId = other.AccountId, + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.SoldUgcMarketItem?(SoldUgcMarketItem? other) { + return other == null ? null : new Maple2.Model.Game.SoldUgcMarketItem { + Id = other.Id, + Price = other.Price, + Profit = other.Profit, + Name = other.Name, + SoldTime = other.SoldTime.ToEpochSeconds(), + AccountId = other.AccountId, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("ugc-market-item-sold"); + builder.HasKey(entry => entry.Id); + builder.HasOne() + .WithMany() + .HasForeignKey(listing => listing.AccountId) + .IsRequired(); + IMutableProperty creationTime = builder.Property(listing => listing.SoldTime) + .ValueGeneratedOnAdd().Metadata; + creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + } +} diff --git a/Maple2.Database/Model/Market/UgcMarketItem.cs b/Maple2.Database/Model/Market/UgcMarketItem.cs index cfce44053..ae121a5d9 100644 --- a/Maple2.Database/Model/Market/UgcMarketItem.cs +++ b/Maple2.Database/Model/Market/UgcMarketItem.cs @@ -1,84 +1,84 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class UgcMarketItem { - public long Id { get; set; } - public int ItemId { get; set; } - public long Price { get; set; } - public int SalesCount { get; set; } - public int TabId { get; set; } - public UgcMarketListingStatus Status { get; set; } - public DateTime ListingEndTime { get; set; } - public DateTime PromotionEndTime { get; set; } - public long AccountId { get; set; } - public long CharacterId { get; set; } - public string CharacterName { get; set; } - public string Description { get; set; } - public string[] Tags { get; set; } = []; - public UgcItemLook Look { get; set; } - public ItemBlueprint Blueprint { get; set; } - public DateTime CreationTime { get; set; } - - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator UgcMarketItem?(Maple2.Model.Game.UgcMarketItem? other) { - return other == null ? null : new UgcMarketItem { - Id = other.Id, - ItemId = other.ItemMetadata.Id, - Price = other.Price, - SalesCount = other.SalesCount, - TabId = other.TabId, - Status = other.Status, - ListingEndTime = other.ListingEndTime.FromEpochSeconds(), - PromotionEndTime = other.PromotionEndTime.FromEpochSeconds(), - AccountId = other.SellerAccountId, - CharacterId = other.SellerCharacterId, - CharacterName = other.SellerCharacterName, - Description = other.Description, - Tags = other.Tags, - Look = other.Look, - Blueprint = other.Blueprint, - CreationTime = other.CreationTime.FromEpochSeconds(), - }; - } - - public Maple2.Model.Game.UgcMarketItem Convert(ItemMetadata metadata) { - var entry = new Maple2.Model.Game.UgcMarketItem(metadata) { - Id = Id, - Price = Price, - SalesCount = SalesCount, - TabId = TabId, - Status = Status, - ListingEndTime = ListingEndTime.ToEpochSeconds(), - PromotionEndTime = PromotionEndTime.ToEpochSeconds(), - SellerAccountId = AccountId, - SellerCharacterId = CharacterId, - SellerCharacterName = CharacterName, - CreationTime = CreationTime.ToEpochSeconds(), - Description = Description, - Tags = Tags, - Look = Look, - Blueprint = Blueprint, - }; - - return entry; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("ugc-market-item"); - builder.HasKey(entry => entry.Id); - builder.Property(entry => entry.Look).HasJsonConversion(); - builder.Property(entry => entry.Blueprint).HasJsonConversion(); - builder.Property(entry => entry.Tags).HasJsonConversion(); - IMutableProperty creationTime = builder.Property(listing => listing.CreationTime) - .ValueGeneratedOnAdd().Metadata; - creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class UgcMarketItem { + public long Id { get; set; } + public int ItemId { get; set; } + public long Price { get; set; } + public int SalesCount { get; set; } + public int TabId { get; set; } + public UgcMarketListingStatus Status { get; set; } + public DateTime ListingEndTime { get; set; } + public DateTime PromotionEndTime { get; set; } + public long AccountId { get; set; } + public long CharacterId { get; set; } + public string CharacterName { get; set; } + public string Description { get; set; } + public string[] Tags { get; set; } = []; + public UgcItemLook Look { get; set; } + public ItemBlueprint Blueprint { get; set; } + public DateTime CreationTime { get; set; } + + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator UgcMarketItem?(Maple2.Model.Game.UgcMarketItem? other) { + return other == null ? null : new UgcMarketItem { + Id = other.Id, + ItemId = other.ItemMetadata.Id, + Price = other.Price, + SalesCount = other.SalesCount, + TabId = other.TabId, + Status = other.Status, + ListingEndTime = other.ListingEndTime.FromEpochSeconds(), + PromotionEndTime = other.PromotionEndTime.FromEpochSeconds(), + AccountId = other.SellerAccountId, + CharacterId = other.SellerCharacterId, + CharacterName = other.SellerCharacterName, + Description = other.Description, + Tags = other.Tags, + Look = other.Look, + Blueprint = other.Blueprint, + CreationTime = other.CreationTime.FromEpochSeconds(), + }; + } + + public Maple2.Model.Game.UgcMarketItem Convert(ItemMetadata metadata) { + var entry = new Maple2.Model.Game.UgcMarketItem(metadata) { + Id = Id, + Price = Price, + SalesCount = SalesCount, + TabId = TabId, + Status = Status, + ListingEndTime = ListingEndTime.ToEpochSeconds(), + PromotionEndTime = PromotionEndTime.ToEpochSeconds(), + SellerAccountId = AccountId, + SellerCharacterId = CharacterId, + SellerCharacterName = CharacterName, + CreationTime = CreationTime.ToEpochSeconds(), + Description = Description, + Tags = Tags, + Look = Look, + Blueprint = Blueprint, + }; + + return entry; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("ugc-market-item"); + builder.HasKey(entry => entry.Id); + builder.Property(entry => entry.Look).HasJsonConversion(); + builder.Property(entry => entry.Blueprint).HasJsonConversion(); + builder.Property(entry => entry.Tags).HasJsonConversion(); + IMutableProperty creationTime = builder.Property(listing => listing.CreationTime) + .ValueGeneratedOnAdd().Metadata; + creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + } +} diff --git a/Maple2.Database/Model/Marriage.cs b/Maple2.Database/Model/Marriage.cs index 734908897..7f799248e 100644 --- a/Maple2.Database/Model/Marriage.cs +++ b/Maple2.Database/Model/Marriage.cs @@ -1,77 +1,77 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class Marriage { - public long Id { get; set; } - public long Partner1Id { get; set; } - public long Partner2Id { get; set; } - public MaritalStatus Status { get; set; } - public IList ExpHistory { get; set; } = new List(); - public required string Profile { get; set; } - public required string Partner1Message { get; set; } - public required string Partner2Message { get; set; } - public DateTime CreationTime { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Marriage?(Maple2.Model.Game.Marriage? other) { - return other == null ? null : new Marriage { - Partner1Id = other.Partner1.CharacterId, - Partner2Id = other.Partner2.CharacterId, - Status = other.Status, - Profile = other.Profile, - Partner1Message = other.Partner1.Message, - Partner2Message = other.Partner2.Message, - CreationTime = other.CreationTime.FromEpochSeconds(), - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("marriage"); - builder.HasKey(marriage => marriage.Id); - builder.Property(marriage => marriage.ExpHistory).HasJsonConversion().IsRequired(); - - builder.HasOne() - .WithMany() - .HasForeignKey(marriage => marriage.Partner1Id) - .IsRequired(); - builder.HasOne() - .WithMany() - .HasForeignKey(marriage => marriage.Partner2Id) - .IsRequired(); - builder.Property(marriage => marriage.CreationTime) - .ValueGeneratedOnAdd(); - } -} - -internal class MarriageExp { - public MarriageExpType Type { get; set; } - public long Amount { get; set; } - public DateTime Time { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator MarriageExp?(Maple2.Model.Game.MarriageExp? other) { - return other == null ? null : new MarriageExp { - Type = other.Type, - Amount = other.Amount, - Time = other.Time.FromEpochSeconds(), - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.MarriageExp?(MarriageExp? other) { - if (other == null) { - return null; - } - - return new Maple2.Model.Game.MarriageExp { - Type = other.Type, - Amount = other.Amount, - Time = other.Time.ToEpochSeconds(), - }; - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class Marriage { + public long Id { get; set; } + public long Partner1Id { get; set; } + public long Partner2Id { get; set; } + public MaritalStatus Status { get; set; } + public IList ExpHistory { get; set; } = new List(); + public required string Profile { get; set; } + public required string Partner1Message { get; set; } + public required string Partner2Message { get; set; } + public DateTime CreationTime { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Marriage?(Maple2.Model.Game.Marriage? other) { + return other == null ? null : new Marriage { + Partner1Id = other.Partner1.CharacterId, + Partner2Id = other.Partner2.CharacterId, + Status = other.Status, + Profile = other.Profile, + Partner1Message = other.Partner1.Message, + Partner2Message = other.Partner2.Message, + CreationTime = other.CreationTime.FromEpochSeconds(), + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("marriage"); + builder.HasKey(marriage => marriage.Id); + builder.Property(marriage => marriage.ExpHistory).HasJsonConversion().IsRequired(); + + builder.HasOne() + .WithMany() + .HasForeignKey(marriage => marriage.Partner1Id) + .IsRequired(); + builder.HasOne() + .WithMany() + .HasForeignKey(marriage => marriage.Partner2Id) + .IsRequired(); + builder.Property(marriage => marriage.CreationTime) + .ValueGeneratedOnAdd(); + } +} + +internal class MarriageExp { + public MarriageExpType Type { get; set; } + public long Amount { get; set; } + public DateTime Time { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator MarriageExp?(Maple2.Model.Game.MarriageExp? other) { + return other == null ? null : new MarriageExp { + Type = other.Type, + Amount = other.Amount, + Time = other.Time.FromEpochSeconds(), + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.MarriageExp?(MarriageExp? other) { + if (other == null) { + return null; + } + + return new Maple2.Model.Game.MarriageExp { + Type = other.Type, + Amount = other.Amount, + Time = other.Time.ToEpochSeconds(), + }; + } +} diff --git a/Maple2.Database/Model/Medal.cs b/Maple2.Database/Model/Medal.cs index 48a50d574..37c5c208c 100644 --- a/Maple2.Database/Model/Medal.cs +++ b/Maple2.Database/Model/Medal.cs @@ -1,39 +1,39 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class Medal { - public int Id { get; set; } - public long OwnerId { get; set; } - public short Slot { get; set; } = -1; - public DateTime ExpiryTime { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Medal?(Maple2.Model.Game.Medal? other) { - return other == null ? null : new Medal { - Id = other.Id, - Slot = other.Slot, - ExpiryTime = other.ExpiryTime.FromEpochSeconds(), - }; - } - - // Use explicit Convert() here because we need medal type to construct the medal. - public Maple2.Model.Game.Medal Convert(MedalType type) { - var medal = new Maple2.Model.Game.Medal(Id, type) { - Slot = Slot, - ExpiryTime = ExpiryTime.ToEpochSeconds(), - }; - - return medal; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("medal"); - builder.HasKey(medal => new { medal.OwnerId, medal.Id }); - - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class Medal { + public int Id { get; set; } + public long OwnerId { get; set; } + public short Slot { get; set; } = -1; + public DateTime ExpiryTime { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Medal?(Maple2.Model.Game.Medal? other) { + return other == null ? null : new Medal { + Id = other.Id, + Slot = other.Slot, + ExpiryTime = other.ExpiryTime.FromEpochSeconds(), + }; + } + + // Use explicit Convert() here because we need medal type to construct the medal. + public Maple2.Model.Game.Medal Convert(MedalType type) { + var medal = new Maple2.Model.Game.Medal(Id, type) { + Slot = Slot, + ExpiryTime = ExpiryTime.ToEpochSeconds(), + }; + + return medal; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("medal"); + builder.HasKey(medal => new { medal.OwnerId, medal.Id }); + + } +} diff --git a/Maple2.Database/Model/Metadata/SchemaVersion.cs b/Maple2.Database/Model/Metadata/SchemaVersion.cs index ed0683ada..7b6ba7b6f 100644 --- a/Maple2.Database/Model/Metadata/SchemaVersion.cs +++ b/Maple2.Database/Model/Metadata/SchemaVersion.cs @@ -1,16 +1,16 @@ -using System.ComponentModel.DataAnnotations.Schema; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model.Metadata; - -[Table("schema_version")] -public class SchemaVersion { - public int Id { get; set; } - public string SchemaHash { get; set; } = null!; - public DateTime UpdatedAt { get; set; } - - internal static void Configure(EntityTypeBuilder builder) { - builder.HasKey(entry => entry.Id); - builder.Property(entry => entry.UpdatedAt).IsRowVersion(); - } -} +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model.Metadata; + +[Table("schema_version")] +public class SchemaVersion { + public int Id { get; set; } + public string SchemaHash { get; set; } = null!; + public DateTime UpdatedAt { get; set; } + + internal static void Configure(EntityTypeBuilder builder) { + builder.HasKey(entry => entry.Id); + builder.Property(entry => entry.UpdatedAt).IsRowVersion(); + } +} diff --git a/Maple2.Database/Model/Metadata/TableChecksum.cs b/Maple2.Database/Model/Metadata/TableChecksum.cs index 1d29aa90a..218f0592c 100644 --- a/Maple2.Database/Model/Metadata/TableChecksum.cs +++ b/Maple2.Database/Model/Metadata/TableChecksum.cs @@ -1,19 +1,19 @@ -using System.ComponentModel.DataAnnotations.Schema; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model.Metadata; - -[Table("checksum")] -public class TableChecksum { - - public required string TableName { get; set; } - - public uint Crc32C { get; set; } - - public DateTime LastModified { get; set; } - - internal static void Configure(EntityTypeBuilder builder) { - builder.HasKey(entry => entry.TableName); - builder.Property(entry => entry.LastModified).IsRowVersion(); - } -} +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model.Metadata; + +[Table("checksum")] +public class TableChecksum { + + public required string TableName { get; set; } + + public uint Crc32C { get; set; } + + public DateTime LastModified { get; set; } + + internal static void Configure(EntityTypeBuilder builder) { + builder.HasKey(entry => entry.TableName); + builder.Property(entry => entry.LastModified).IsRowVersion(); + } +} diff --git a/Maple2.Database/Model/PetConfig.cs b/Maple2.Database/Model/PetConfig.cs index d142a047f..56e0bc27a 100644 --- a/Maple2.Database/Model/PetConfig.cs +++ b/Maple2.Database/Model/PetConfig.cs @@ -1,37 +1,37 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Game; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class PetConfig { - public long ItemUid { get; set; } - - public required PetPotionConfig[] PotionConfigs { get; set; } - public PetLootConfig LootConfig { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator PetConfig?(Maple2.Model.Game.PetConfig? other) { - return other == null ? null : new PetConfig { - PotionConfigs = other.PotionConfig, - LootConfig = other.LootConfig, - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.PetConfig?(PetConfig? other) { - return other == null ? null : new Maple2.Model.Game.PetConfig(other.PotionConfigs, other.LootConfig); - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("pet-config"); - builder.HasKey(config => config.ItemUid); - builder.OneToOne() - .HasForeignKey(config => config.ItemUid); - - builder.Property(character => character.PotionConfigs).HasJsonConversion().IsRequired(); - builder.Property(character => character.LootConfig).HasJsonConversion().IsRequired(); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Game; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class PetConfig { + public long ItemUid { get; set; } + + public required PetPotionConfig[] PotionConfigs { get; set; } + public PetLootConfig LootConfig { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator PetConfig?(Maple2.Model.Game.PetConfig? other) { + return other == null ? null : new PetConfig { + PotionConfigs = other.PotionConfig, + LootConfig = other.LootConfig, + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.PetConfig?(PetConfig? other) { + return other == null ? null : new Maple2.Model.Game.PetConfig(other.PotionConfigs, other.LootConfig); + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("pet-config"); + builder.HasKey(config => config.ItemUid); + builder.OneToOne() + .HasForeignKey(config => config.ItemUid); + + builder.Property(character => character.PotionConfigs).HasJsonConversion().IsRequired(); + builder.Property(character => character.LootConfig).HasJsonConversion().IsRequired(); + } +} diff --git a/Maple2.Database/Model/PlayerReport.cs b/Maple2.Database/Model/PlayerReport.cs index 0cdac446f..75b150ca3 100644 --- a/Maple2.Database/Model/PlayerReport.cs +++ b/Maple2.Database/Model/PlayerReport.cs @@ -1,42 +1,42 @@ -using System.Text.Json.Serialization; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class PlayerReport { - public long Id { get; set; } - public long CharacterId { get; set; } - public string PlayerName { get; set; } = string.Empty; - public long ReporterCharacterId { get; set; } - public string ReporterName { get; set; } = string.Empty; - public string Reason { get; set; } = string.Empty; - public ReportCategory Category { get; set; } - public ReportInfo ReportInfo { get; set; } - public DateTime CreateTime { get; set; } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("player-reports"); - builder.HasKey(report => report.Id); - builder.Property(report => report.ReportInfo).HasJsonConversion().IsRequired(); - - } -} - -[JsonPolymorphic(TypeDiscriminatorPropertyName = "!")] -[JsonDerivedType(typeof(PlayerReportInfo), typeDiscriminator: "player")] -[JsonDerivedType(typeof(HomeReportInfo), typeDiscriminator: "home")] -[JsonDerivedType(typeof(ChatReportInfo), typeDiscriminator: "chat")] -[JsonDerivedType(typeof(PetReportInfo), typeDiscriminator: "pet")] -[JsonDerivedType(typeof(PosterReportInfo), typeDiscriminator: "poster")] -[JsonDerivedType(typeof(ItemReportInfo), typeDiscriminator: "item")] -internal abstract record ReportInfo; - -internal record PlayerReportInfo(string Flag) : ReportInfo; -internal record HomeReportInfo(string Flag, long HomeId, int MapId, int PlotId) : ReportInfo; -internal record ChatReportInfo(string Flag, string Message) : ReportInfo; -internal record PetReportInfo(string Flag, string PetName) : ReportInfo; -internal record PosterReportInfo(string Flag, long PosterId, string TemplateId) : ReportInfo; -internal record ItemReportInfo(string Flag, long ListingId) : ReportInfo; +using System.Text.Json.Serialization; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class PlayerReport { + public long Id { get; set; } + public long CharacterId { get; set; } + public string PlayerName { get; set; } = string.Empty; + public long ReporterCharacterId { get; set; } + public string ReporterName { get; set; } = string.Empty; + public string Reason { get; set; } = string.Empty; + public ReportCategory Category { get; set; } + public ReportInfo ReportInfo { get; set; } + public DateTime CreateTime { get; set; } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("player-reports"); + builder.HasKey(report => report.Id); + builder.Property(report => report.ReportInfo).HasJsonConversion().IsRequired(); + + } +} + +[JsonPolymorphic(TypeDiscriminatorPropertyName = "!")] +[JsonDerivedType(typeof(PlayerReportInfo), typeDiscriminator: "player")] +[JsonDerivedType(typeof(HomeReportInfo), typeDiscriminator: "home")] +[JsonDerivedType(typeof(ChatReportInfo), typeDiscriminator: "chat")] +[JsonDerivedType(typeof(PetReportInfo), typeDiscriminator: "pet")] +[JsonDerivedType(typeof(PosterReportInfo), typeDiscriminator: "poster")] +[JsonDerivedType(typeof(ItemReportInfo), typeDiscriminator: "item")] +internal abstract record ReportInfo; + +internal record PlayerReportInfo(string Flag) : ReportInfo; +internal record HomeReportInfo(string Flag, long HomeId, int MapId, int PlotId) : ReportInfo; +internal record ChatReportInfo(string Flag, string Message) : ReportInfo; +internal record PetReportInfo(string Flag, string PetName) : ReportInfo; +internal record PosterReportInfo(string Flag, long PosterId, string TemplateId) : ReportInfo; +internal record ItemReportInfo(string Flag, long ListingId) : ReportInfo; diff --git a/Maple2.Database/Model/Quest.cs b/Maple2.Database/Model/Quest.cs index 1e272e44b..d69a9595a 100644 --- a/Maple2.Database/Model/Quest.cs +++ b/Maple2.Database/Model/Quest.cs @@ -1,82 +1,82 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class Quest { - public long OwnerId { get; set; } - public int Id { get; set; } - public QuestState State { get; set; } - public int CompletionCount { get; set; } - public long StartTime { get; set; } - public long EndTime { get; set; } - public bool Track { get; set; } - public SortedDictionary Conditions { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Quest?(Maple2.Model.Game.Quest? other) { - if (other == null) { - return null; - } - var quest = new Quest { - Id = other.Id, - State = other.State, - CompletionCount = other.CompletionCount, - StartTime = other.StartTime, - EndTime = other.EndTime, - Track = other.Track, - Conditions = new SortedDictionary(), - }; - - foreach ((int index, QuestCondition condition) in other.Conditions) { - quest.Conditions.Add(index, condition); - } - - return quest; - } - - // Use explicit Convert() here because we need metadata to construct Quest. - public Maple2.Model.Game.Quest Convert(QuestMetadata metadata) { - var quest = new Maple2.Model.Game.Quest(metadata) { - State = State, - CompletionCount = CompletionCount, - StartTime = StartTime, - EndTime = EndTime, - Track = Track, - }; - - for (int i = 0; i < Conditions.Count; i++) { - quest.Conditions.Add(i, Conditions[i].Convert(metadata.Conditions[i])); - } - - return quest; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("quest"); - builder.HasKey(quest => new { quest.OwnerId, quest.Id }); - builder.Property(quest => quest.Conditions).HasJsonConversion().IsRequired(); - } -} - -internal class QuestCondition { - public int Counter { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator QuestCondition?(Maple2.Model.Game.Quest.Condition? other) { - return other == null ? null : new QuestCondition { - Counter = other.Counter, - }; - } - - // Use explicit Convert() here because we need metadata to construct Quest. - public Maple2.Model.Game.Quest.Condition Convert(ConditionMetadata metadata) { - return new Maple2.Model.Game.Quest.Condition(metadata) { - Counter = Counter, - }; - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class Quest { + public long OwnerId { get; set; } + public int Id { get; set; } + public QuestState State { get; set; } + public int CompletionCount { get; set; } + public long StartTime { get; set; } + public long EndTime { get; set; } + public bool Track { get; set; } + public SortedDictionary Conditions { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Quest?(Maple2.Model.Game.Quest? other) { + if (other == null) { + return null; + } + var quest = new Quest { + Id = other.Id, + State = other.State, + CompletionCount = other.CompletionCount, + StartTime = other.StartTime, + EndTime = other.EndTime, + Track = other.Track, + Conditions = new SortedDictionary(), + }; + + foreach ((int index, QuestCondition condition) in other.Conditions) { + quest.Conditions.Add(index, condition); + } + + return quest; + } + + // Use explicit Convert() here because we need metadata to construct Quest. + public Maple2.Model.Game.Quest Convert(QuestMetadata metadata) { + var quest = new Maple2.Model.Game.Quest(metadata) { + State = State, + CompletionCount = CompletionCount, + StartTime = StartTime, + EndTime = EndTime, + Track = Track, + }; + + for (int i = 0; i < Conditions.Count; i++) { + quest.Conditions.Add(i, Conditions[i].Convert(metadata.Conditions[i])); + } + + return quest; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("quest"); + builder.HasKey(quest => new { quest.OwnerId, quest.Id }); + builder.Property(quest => quest.Conditions).HasJsonConversion().IsRequired(); + } +} + +internal class QuestCondition { + public int Counter { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator QuestCondition?(Maple2.Model.Game.Quest.Condition? other) { + return other == null ? null : new QuestCondition { + Counter = other.Counter, + }; + } + + // Use explicit Convert() here because we need metadata to construct Quest. + public Maple2.Model.Game.Quest.Condition Convert(ConditionMetadata metadata) { + return new Maple2.Model.Game.Quest.Condition(metadata) { + Counter = Counter, + }; + } +} diff --git a/Maple2.Database/Model/Ranking/TrophyRankInfo.cs b/Maple2.Database/Model/Ranking/TrophyRankInfo.cs index 3df4e6725..cf8491f85 100644 --- a/Maple2.Database/Model/Ranking/TrophyRankInfo.cs +++ b/Maple2.Database/Model/Ranking/TrophyRankInfo.cs @@ -1,5 +1,5 @@ -using Maple2.Model.Game; - -namespace Maple2.Database.Model.Ranking; - -public record TrophyRankInfo(int Rank, long CharacterId, string Name, string Profile, AchievementInfo Trophy); +using Maple2.Model.Game; + +namespace Maple2.Database.Model.Ranking; + +public record TrophyRankInfo(int Rank, long CharacterId, string Name, string Profile, AchievementInfo Trophy); diff --git a/Maple2.Database/Model/ServerInfo.cs b/Maple2.Database/Model/ServerInfo.cs index 19d7a1b30..048fd7edc 100644 --- a/Maple2.Database/Model/ServerInfo.cs +++ b/Maple2.Database/Model/ServerInfo.cs @@ -1,14 +1,14 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class ServerInfo { - public required string Key { get; set; } - public DateTime LastModified { get; set; } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("server-info"); - builder.HasKey(info => info.Key); - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class ServerInfo { + public required string Key { get; set; } + public DateTime LastModified { get; set; } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("server-info"); + builder.HasKey(info => info.Key); + } +} diff --git a/Maple2.Database/Model/Shop/CharacterShopData.cs b/Maple2.Database/Model/Shop/CharacterShopData.cs index 98bb1940b..a9fef4861 100644 --- a/Maple2.Database/Model/Shop/CharacterShopData.cs +++ b/Maple2.Database/Model/Shop/CharacterShopData.cs @@ -1,40 +1,40 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model.Shop; - -internal class CharacterShopData { - public int ShopId { get; set; } - public long OwnerId { get; set; } - public DateTime RestockTime { get; set; } - public int RestockCount { get; set; } - public ResetType Interval { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator CharacterShopData?(Maple2.Model.Game.Shop.CharacterShopData? other) { - return other == null ? null : new CharacterShopData { - ShopId = other.ShopId, - RestockTime = other.RestockTime.FromEpochSeconds(), - RestockCount = other.RestockCount, - Interval = other.Interval, - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.Shop.CharacterShopData?(CharacterShopData? other) { - return other == null ? null : new Maple2.Model.Game.Shop.CharacterShopData { - ShopId = other.ShopId, - RestockTime = other.RestockTime.ToEpochSeconds(), - RestockCount = other.RestockCount, - Interval = other.Interval, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("character-shop-data"); - builder.HasKey(info => new { info.ShopId, info.OwnerId }); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model.Shop; + +internal class CharacterShopData { + public int ShopId { get; set; } + public long OwnerId { get; set; } + public DateTime RestockTime { get; set; } + public int RestockCount { get; set; } + public ResetType Interval { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator CharacterShopData?(Maple2.Model.Game.Shop.CharacterShopData? other) { + return other == null ? null : new CharacterShopData { + ShopId = other.ShopId, + RestockTime = other.RestockTime.FromEpochSeconds(), + RestockCount = other.RestockCount, + Interval = other.Interval, + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.Shop.CharacterShopData?(CharacterShopData? other) { + return other == null ? null : new Maple2.Model.Game.Shop.CharacterShopData { + ShopId = other.ShopId, + RestockTime = other.RestockTime.ToEpochSeconds(), + RestockCount = other.RestockCount, + Interval = other.Interval, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("character-shop-data"); + builder.HasKey(info => new { info.ShopId, info.OwnerId }); + } +} diff --git a/Maple2.Database/Model/Shop/CharacterShopItemData.cs b/Maple2.Database/Model/Shop/CharacterShopItemData.cs index 0592dc135..0d2219e66 100644 --- a/Maple2.Database/Model/Shop/CharacterShopItemData.cs +++ b/Maple2.Database/Model/Shop/CharacterShopItemData.cs @@ -1,39 +1,39 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model.Shop; - -internal class CharacterShopItemData { - public int ShopId { get; set; } - public int ShopItemId { get; set; } - public long OwnerId { get; set; } - public int StockPurchased { get; set; } - public Item Item { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator CharacterShopItemData?(Maple2.Model.Game.Shop.CharacterShopItemData? other) { - return other == null ? null : new CharacterShopItemData { - ShopId = other.ShopId, - ShopItemId = other.ShopItemId, - StockPurchased = other.StockPurchased, - Item = other.Item, - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.Shop.CharacterShopItemData?(CharacterShopItemData? other) { - return other == null ? null : new Maple2.Model.Game.Shop.CharacterShopItemData { - ShopId = other.ShopId, - ShopItemId = other.ShopItemId, - StockPurchased = other.StockPurchased, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("character-shop-item-data"); - builder.HasKey(info => new { info.ShopItemId, info.ShopId, info.OwnerId }); - builder.Property(data => data.Item).HasJsonConversion(); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model.Shop; + +internal class CharacterShopItemData { + public int ShopId { get; set; } + public int ShopItemId { get; set; } + public long OwnerId { get; set; } + public int StockPurchased { get; set; } + public Item Item { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator CharacterShopItemData?(Maple2.Model.Game.Shop.CharacterShopItemData? other) { + return other == null ? null : new CharacterShopItemData { + ShopId = other.ShopId, + ShopItemId = other.ShopItemId, + StockPurchased = other.StockPurchased, + Item = other.Item, + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.Shop.CharacterShopItemData?(CharacterShopItemData? other) { + return other == null ? null : new Maple2.Model.Game.Shop.CharacterShopItemData { + ShopId = other.ShopId, + ShopItemId = other.ShopItemId, + StockPurchased = other.StockPurchased, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("character-shop-item-data"); + builder.HasKey(info => new { info.ShopItemId, info.ShopId, info.OwnerId }); + builder.Property(data => data.Item).HasJsonConversion(); + } +} diff --git a/Maple2.Database/Model/SkillTab.cs b/Maple2.Database/Model/SkillTab.cs index 13a49405a..4de666d8c 100644 --- a/Maple2.Database/Model/SkillTab.cs +++ b/Maple2.Database/Model/SkillTab.cs @@ -1,52 +1,52 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class SkillTab { - public long CharacterId { get; set; } - public long Id { get; set; } - public required string Name { get; set; } - public required IDictionary Skills; - - public DateTime CreationTime { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator SkillTab?(Maple2.Model.Game.SkillTab? other) { - return other == null ? null : new SkillTab { - Id = other.Id, - Name = other.Name, - Skills = new Dictionary(other.Skills), - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.SkillTab?(SkillTab? other) { - if (other == null) { - return null; - } - - return new Maple2.Model.Game.SkillTab(other.Name) { - Id = other.Id, - Skills = new Dictionary(other.Skills), - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("skill-tab"); - builder.HasKey(tab => new { tab.CharacterId, tab.Id }); - builder.HasIndex(tab => tab.CharacterId); - builder.Property(tab => tab.Skills).HasJsonConversion().IsRequired(); - - builder.HasOne() - .WithMany() - .HasForeignKey(tab => tab.CharacterId); - - IMutableProperty creationTime = builder.Property(club => club.CreationTime) - .ValueGeneratedOnAdd().Metadata; - creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class SkillTab { + public long CharacterId { get; set; } + public long Id { get; set; } + public required string Name { get; set; } + public required IDictionary Skills; + + public DateTime CreationTime { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator SkillTab?(Maple2.Model.Game.SkillTab? other) { + return other == null ? null : new SkillTab { + Id = other.Id, + Name = other.Name, + Skills = new Dictionary(other.Skills), + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.SkillTab?(SkillTab? other) { + if (other == null) { + return null; + } + + return new Maple2.Model.Game.SkillTab(other.Name) { + Id = other.Id, + Skills = new Dictionary(other.Skills), + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("skill-tab"); + builder.HasKey(tab => new { tab.CharacterId, tab.Id }); + builder.HasIndex(tab => tab.CharacterId); + builder.Property(tab => tab.Skills).HasJsonConversion().IsRequired(); + + builder.HasOne() + .WithMany() + .HasForeignKey(tab => tab.CharacterId); + + IMutableProperty creationTime = builder.Property(club => club.CreationTime) + .ValueGeneratedOnAdd().Metadata; + creationTime.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + } +} diff --git a/Maple2.Database/Model/SystemBanner.cs b/Maple2.Database/Model/SystemBanner.cs index 92a90cb10..7f688e14e 100644 --- a/Maple2.Database/Model/SystemBanner.cs +++ b/Maple2.Database/Model/SystemBanner.cs @@ -1,38 +1,38 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class SystemBanner { - public int Id { get; set; } - public string Name { get; set; } - public SystemBannerType Type { get; set; } - public SystemBannerFunction Function { get; set; } - public string FunctionParameter { get; set; } - public string Url { get; set; } - public SystemBannerLanguage Language { get; set; } - public DateTime BeginTime { get; set; } - public DateTime EndTime { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.SystemBanner?(SystemBanner? other) { - return other == null ? null : new Maple2.Model.Game.SystemBanner(other.Id) { - Name = other.Name, - Type = other.Type, - Function = other.Function, - FunctionParameter = other.FunctionParameter, - Url = other.Url, - Language = other.Language, - BeginTime = other.BeginTime.ToEpochSeconds(), - EndTime = other.EndTime.ToEpochSeconds(), - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("system-banner"); - builder.HasKey(banner => banner.Id); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class SystemBanner { + public int Id { get; set; } + public string Name { get; set; } + public SystemBannerType Type { get; set; } + public SystemBannerFunction Function { get; set; } + public string FunctionParameter { get; set; } + public string Url { get; set; } + public SystemBannerLanguage Language { get; set; } + public DateTime BeginTime { get; set; } + public DateTime EndTime { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.SystemBanner?(SystemBanner? other) { + return other == null ? null : new Maple2.Model.Game.SystemBanner(other.Id) { + Name = other.Name, + Type = other.Type, + Function = other.Function, + FunctionParameter = other.FunctionParameter, + Url = other.Url, + Language = other.Language, + BeginTime = other.BeginTime.ToEpochSeconds(), + EndTime = other.EndTime.ToEpochSeconds(), + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("system-banner"); + builder.HasKey(banner => banner.Id); + } +} diff --git a/Maple2.Database/Model/UgcResource.cs b/Maple2.Database/Model/UgcResource.cs index 413dc5d4e..b13f45c95 100644 --- a/Maple2.Database/Model/UgcResource.cs +++ b/Maple2.Database/Model/UgcResource.cs @@ -1,42 +1,42 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Model.Enum; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class UgcResource { - public long Id { get; set; } - public long OwnerId { get; set; } - public string Path { get; set; } = string.Empty; - public UgcType Type { get; set; } - - public DateTime LastModified { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator Maple2.Model.Game.UgcResource?(UgcResource? other) { - return other == null ? null : new Maple2.Model.Game.UgcResource { - Id = other.Id, - Path = other.Path, - Type = other.Type, - }; - } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator UgcResource?(Maple2.Model.Game.UgcResource? other) { - return other == null ? null : new UgcResource { - Id = other.Id, - Path = other.Path, - Type = other.Type, - }; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("ugcresource"); - builder.HasKey(ugc => ugc.Id); - builder.HasIndex(ugc => ugc.OwnerId); - - builder.Property(ugc => ugc.LastModified) - .IsRowVersion(); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Model.Enum; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class UgcResource { + public long Id { get; set; } + public long OwnerId { get; set; } + public string Path { get; set; } = string.Empty; + public UgcType Type { get; set; } + + public DateTime LastModified { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator Maple2.Model.Game.UgcResource?(UgcResource? other) { + return other == null ? null : new Maple2.Model.Game.UgcResource { + Id = other.Id, + Path = other.Path, + Type = other.Type, + }; + } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator UgcResource?(Maple2.Model.Game.UgcResource? other) { + return other == null ? null : new UgcResource { + Id = other.Id, + Path = other.Path, + Type = other.Type, + }; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("ugcresource"); + builder.HasKey(ugc => ugc.Id); + builder.HasIndex(ugc => ugc.OwnerId); + + builder.Property(ugc => ugc.LastModified) + .IsRowVersion(); + } +} diff --git a/Maple2.Database/Model/WeddingHall.cs b/Maple2.Database/Model/WeddingHall.cs index 864e0cba9..eb2b67a64 100644 --- a/Maple2.Database/Model/WeddingHall.cs +++ b/Maple2.Database/Model/WeddingHall.cs @@ -1,66 +1,66 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Maple2.Database.Model; - -internal class WeddingHall { - public long Id { get; set; } - public long MarriageId { get; set; } - public DateTime CeremonyTime { get; set; } - public int PackageId { get; set; } - public int PackageHallId { get; set; } - public long OwnerId { get; set; } - public bool Public { get; set; } - public Dictionary GuestList { get; set; } = new(); // CharacterId, AccountId - public DateTime CreationTime { get; set; } - - [return: NotNullIfNotNull(nameof(other))] - public static implicit operator WeddingHall?(Maple2.Model.Game.WeddingHall? other) { - return other == null ? null : new WeddingHall { - Id = other.Id, - MarriageId = other.MarriageId, - CeremonyTime = other.CeremonyTime.FromEpochSeconds(), - PackageId = other.PackageId, - PackageHallId = other.PackageHallId, - Public = other.Public, - CreationTime = other.CreationTime.FromEpochSeconds(), - GuestList = other.GuestList, - OwnerId = other.ReserverCharacterId, - }; - } - - // Use explicit Convert() here because we need marriage data to construct Wedding Hall. - public Maple2.Model.Game.WeddingHall Convert(Maple2.Model.Game.Marriage marriage) { - Maple2.Model.Game.MarriagePartner reserver = marriage.Partner1.Info!.CharacterId == OwnerId ? marriage.Partner1 : marriage.Partner2; - Maple2.Model.Game.MarriagePartner partner = marriage.Partner1.Info!.CharacterId == OwnerId ? marriage.Partner2 : marriage.Partner1; - var hall = new Maple2.Model.Game.WeddingHall { - Id = Id, - MarriageId = MarriageId, - CeremonyTime = CeremonyTime.ToEpochSeconds(), - PackageId = PackageId, - PackageHallId = PackageHallId, - Public = Public, - ReserverCharacterId = reserver.CharacterId, - ReserverAccountId = reserver.AccountId, - ReserverName = reserver.Info!.Name, - PartnerName = partner.Info!.Name, - CreationTime = CreationTime.ToEpochSeconds(), - GuestList = GuestList, - }; - - return hall; - } - - public static void Configure(EntityTypeBuilder builder) { - builder.ToTable("wedding-hall"); - builder.HasKey(hall => hall.Id); - builder.Property(hall => hall.GuestList).HasJsonConversion().IsRequired(); - - builder.OneToOne() - .HasForeignKey(hall => hall.MarriageId); - builder.Property(hall => hall.CreationTime) - .ValueGeneratedOnAdd(); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Maple2.Database.Model; + +internal class WeddingHall { + public long Id { get; set; } + public long MarriageId { get; set; } + public DateTime CeremonyTime { get; set; } + public int PackageId { get; set; } + public int PackageHallId { get; set; } + public long OwnerId { get; set; } + public bool Public { get; set; } + public Dictionary GuestList { get; set; } = new(); // CharacterId, AccountId + public DateTime CreationTime { get; set; } + + [return: NotNullIfNotNull(nameof(other))] + public static implicit operator WeddingHall?(Maple2.Model.Game.WeddingHall? other) { + return other == null ? null : new WeddingHall { + Id = other.Id, + MarriageId = other.MarriageId, + CeremonyTime = other.CeremonyTime.FromEpochSeconds(), + PackageId = other.PackageId, + PackageHallId = other.PackageHallId, + Public = other.Public, + CreationTime = other.CreationTime.FromEpochSeconds(), + GuestList = other.GuestList, + OwnerId = other.ReserverCharacterId, + }; + } + + // Use explicit Convert() here because we need marriage data to construct Wedding Hall. + public Maple2.Model.Game.WeddingHall Convert(Maple2.Model.Game.Marriage marriage) { + Maple2.Model.Game.MarriagePartner reserver = marriage.Partner1.Info!.CharacterId == OwnerId ? marriage.Partner1 : marriage.Partner2; + Maple2.Model.Game.MarriagePartner partner = marriage.Partner1.Info!.CharacterId == OwnerId ? marriage.Partner2 : marriage.Partner1; + var hall = new Maple2.Model.Game.WeddingHall { + Id = Id, + MarriageId = MarriageId, + CeremonyTime = CeremonyTime.ToEpochSeconds(), + PackageId = PackageId, + PackageHallId = PackageHallId, + Public = Public, + ReserverCharacterId = reserver.CharacterId, + ReserverAccountId = reserver.AccountId, + ReserverName = reserver.Info!.Name, + PartnerName = partner.Info!.Name, + CreationTime = CreationTime.ToEpochSeconds(), + GuestList = GuestList, + }; + + return hall; + } + + public static void Configure(EntityTypeBuilder builder) { + builder.ToTable("wedding-hall"); + builder.HasKey(hall => hall.Id); + builder.Property(hall => hall.GuestList).HasJsonConversion().IsRequired(); + + builder.OneToOne() + .HasForeignKey(hall => hall.MarriageId); + builder.Property(hall => hall.CreationTime) + .ValueGeneratedOnAdd(); + } +} diff --git a/Maple2.Database/Storage/Game/DatabaseRequest.cs b/Maple2.Database/Storage/Game/DatabaseRequest.cs index 78950bb97..3c06db08b 100644 --- a/Maple2.Database/Storage/Game/DatabaseRequest.cs +++ b/Maple2.Database/Storage/Game/DatabaseRequest.cs @@ -1,38 +1,38 @@ -using Maple2.Database.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.Extensions.Logging; - -namespace Maple2.Database.Storage; - -public abstract class DatabaseRequest(TContext context, ILogger logger) : IDisposable - where TContext : DbContext { - protected readonly TContext Context = context; - protected readonly ILogger Logger = logger; - - private IDbContextTransaction? transaction; - public bool IsTransaction => transaction != null; - - public void BeginTransaction() { - transaction = Context.Database.BeginTransaction(); - } - - public bool Commit() { - if (transaction == null) { - return false; - } - - transaction.Commit(); - transaction = null; // transaction is completed. - return true; - } - - public bool SaveChanges() { - return Context.TrySaveChanges(); - } - - public void Dispose() { - Commit(); - Context.Dispose(); - } -} +using Maple2.Database.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.Logging; + +namespace Maple2.Database.Storage; + +public abstract class DatabaseRequest(TContext context, ILogger logger) : IDisposable + where TContext : DbContext { + protected readonly TContext Context = context; + protected readonly ILogger Logger = logger; + + private IDbContextTransaction? transaction; + public bool IsTransaction => transaction != null; + + public void BeginTransaction() { + transaction = Context.Database.BeginTransaction(); + } + + public bool Commit() { + if (transaction == null) { + return false; + } + + transaction.Commit(); + transaction = null; // transaction is completed. + return true; + } + + public bool SaveChanges() { + return Context.TrySaveChanges(); + } + + public void Dispose() { + Commit(); + Context.Dispose(); + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Achievement.cs b/Maple2.Database/Storage/Game/GameStorage.Achievement.cs index aa4a1e748..ead3d15be 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Achievement.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Achievement.cs @@ -1,65 +1,65 @@ -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Maple2.Model.Game; -using Maple2.Model.Metadata; -using Achievement = Maple2.Model.Game.Achievement; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public Achievement? CreateAchievement(long ownerId, Achievement achievement) { - Model.Achievement model = achievement; - model.OwnerId = ownerId; - Context.Achievement.Add(model); - - return Context.TrySaveChanges() ? ToAchievement(model) : null; - } - - public IDictionary GetAchievements(long ownerId) { - return Context.Achievement.Where(achievement => achievement.OwnerId == ownerId) - .AsEnumerable() - .Select(ToAchievement) - .Where(achievement => achievement != null) - .ToDictionary(achievement => achievement!.Id, achievement => achievement!); - } - - public AchievementInfo GetAchievementInfo(long accountId, long characterId) { - Dictionary achievementCounts = Context.Achievement - .Where(achievement => achievement.OwnerId == accountId || achievement.OwnerId == characterId) - .GroupBy(achievement => achievement.Category) - .Select(group => new { - Category = group.Key, - Count = group.Sum(g => g.CompletedCount), - }) - .ToDictionary(entry => entry.Category, entry => entry.Count); - - return new AchievementInfo { - Combat = achievementCounts.GetValueOrDefault(AchievementCategory.Combat, 0), - Adventure = achievementCounts.GetValueOrDefault(AchievementCategory.Adventure, 0), - Lifestyle = achievementCounts.GetValueOrDefault(AchievementCategory.Life, 0) - + achievementCounts.GetValueOrDefault(AchievementCategory.None, 0), - }; - } - - public bool SaveAchievements(long ownerId, ICollection achievements) { - foreach (Achievement achievement in achievements) { - Model.Achievement model = achievement; - model.OwnerId = ownerId; - - Context.Achievement.Update(model); - } - - return Context.TrySaveChanges(); - } - - // Converts model to item if possible, otherwise returns null. - private Achievement? ToAchievement(Model.Achievement? model) { - if (model == null) { - return null; - } - - return game.achievementMetadata.TryGet(model.Id, out AchievementMetadata? metadata) ? model.Convert(metadata) : null; - } - } -} +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Maple2.Model.Game; +using Maple2.Model.Metadata; +using Achievement = Maple2.Model.Game.Achievement; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public Achievement? CreateAchievement(long ownerId, Achievement achievement) { + Model.Achievement model = achievement; + model.OwnerId = ownerId; + Context.Achievement.Add(model); + + return Context.TrySaveChanges() ? ToAchievement(model) : null; + } + + public IDictionary GetAchievements(long ownerId) { + return Context.Achievement.Where(achievement => achievement.OwnerId == ownerId) + .AsEnumerable() + .Select(ToAchievement) + .Where(achievement => achievement != null) + .ToDictionary(achievement => achievement!.Id, achievement => achievement!); + } + + public AchievementInfo GetAchievementInfo(long accountId, long characterId) { + Dictionary achievementCounts = Context.Achievement + .Where(achievement => achievement.OwnerId == accountId || achievement.OwnerId == characterId) + .GroupBy(achievement => achievement.Category) + .Select(group => new { + Category = group.Key, + Count = group.Sum(g => g.CompletedCount), + }) + .ToDictionary(entry => entry.Category, entry => entry.Count); + + return new AchievementInfo { + Combat = achievementCounts.GetValueOrDefault(AchievementCategory.Combat, 0), + Adventure = achievementCounts.GetValueOrDefault(AchievementCategory.Adventure, 0), + Lifestyle = achievementCounts.GetValueOrDefault(AchievementCategory.Life, 0) + + achievementCounts.GetValueOrDefault(AchievementCategory.None, 0), + }; + } + + public bool SaveAchievements(long ownerId, ICollection achievements) { + foreach (Achievement achievement in achievements) { + Model.Achievement model = achievement; + model.OwnerId = ownerId; + + Context.Achievement.Update(model); + } + + return Context.TrySaveChanges(); + } + + // Converts model to item if possible, otherwise returns null. + private Achievement? ToAchievement(Model.Achievement? model) { + if (model == null) { + return null; + } + + return game.achievementMetadata.TryGet(model.Id, out AchievementMetadata? metadata) ? model.Convert(metadata) : null; + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Buddy.cs b/Maple2.Database/Storage/Game/GameStorage.Buddy.cs index 8d1064f03..856b06a43 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Buddy.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Buddy.cs @@ -1,53 +1,53 @@ -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Maple2.Model.Game; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public IList ListBuddies(long ownerId) { - return Context.Buddy.Where(buddy => buddy.OwnerId == ownerId) - .Select(buddy => buddy) - .ToList(); - } - - public BuddyEntry? GetBuddy(long id) { - return Context.Buddy.Find(id); - } - - public BuddyEntry? GetBuddy(long ownerId, long buddyId) { - return Context.Buddy.FirstOrDefault(buddy => buddy.OwnerId == ownerId && buddy.BuddyId == buddyId); - } - - public BuddyType? GetBuddyType(long ownerId, long buddyId) { - return Context.Buddy.FirstOrDefault(buddy => buddy.OwnerId == ownerId && buddy.BuddyId == buddyId)?.Type; - } - - public BuddyEntry? CreateBuddy(long ownerId, long buddyId, BuddyType type, string message = "") { - var model = new Model.Buddy { - OwnerId = ownerId, - BuddyId = buddyId, - Type = type, - Message = message, - }; - Context.Buddy.Add(model); - - return Context.TrySaveChanges() ? model : null; - } - - public int CountBuddy(long ownerId) { - return Context.Buddy.Count(buddy => buddy.OwnerId == ownerId && buddy.Type != BuddyType.Blocked); - } - - public bool UpdateBuddy(params BuddyEntry[] buddies) { - Context.Buddy.UpdateRange(buddies.Select(buddy => buddy)); - return Context.TrySaveChanges(); - } - - public bool RemoveBuddy(params BuddyEntry[] buddies) { - Context.Buddy.RemoveRange(buddies.Select(buddy => buddy)); - return Context.TrySaveChanges(); - } - } -} +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Maple2.Model.Game; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public IList ListBuddies(long ownerId) { + return Context.Buddy.Where(buddy => buddy.OwnerId == ownerId) + .Select(buddy => buddy) + .ToList(); + } + + public BuddyEntry? GetBuddy(long id) { + return Context.Buddy.Find(id); + } + + public BuddyEntry? GetBuddy(long ownerId, long buddyId) { + return Context.Buddy.FirstOrDefault(buddy => buddy.OwnerId == ownerId && buddy.BuddyId == buddyId); + } + + public BuddyType? GetBuddyType(long ownerId, long buddyId) { + return Context.Buddy.FirstOrDefault(buddy => buddy.OwnerId == ownerId && buddy.BuddyId == buddyId)?.Type; + } + + public BuddyEntry? CreateBuddy(long ownerId, long buddyId, BuddyType type, string message = "") { + var model = new Model.Buddy { + OwnerId = ownerId, + BuddyId = buddyId, + Type = type, + Message = message, + }; + Context.Buddy.Add(model); + + return Context.TrySaveChanges() ? model : null; + } + + public int CountBuddy(long ownerId) { + return Context.Buddy.Count(buddy => buddy.OwnerId == ownerId && buddy.Type != BuddyType.Blocked); + } + + public bool UpdateBuddy(params BuddyEntry[] buddies) { + Context.Buddy.UpdateRange(buddies.Select(buddy => buddy)); + return Context.TrySaveChanges(); + } + + public bool RemoveBuddy(params BuddyEntry[] buddies) { + Context.Buddy.RemoveRange(buddies.Select(buddy => buddy)); + return Context.TrySaveChanges(); + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Club.cs b/Maple2.Database/Storage/Game/GameStorage.Club.cs index 537af590f..b8bf42726 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Club.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Club.cs @@ -1,141 +1,141 @@ -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Maple2.Model.Game; -using Maple2.Tools.Extensions; -using Z.EntityFramework.Plus; -using Club = Maple2.Model.Game.Club.Club; -using ClubMember = Maple2.Model.Game.Club.ClubMember; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public Club? GetClub(IPlayerInfoProvider provider, long clubId) { - Club? club = Context.Club.Find(clubId); - if (club == null) { - return null; - } - - List members = GetClubMembers(provider, clubId); - club.Leader = members.First(member => member.Info.CharacterId == club.LeaderId); - foreach (ClubMember member in members) { - club.Members.TryAdd(member.Info.CharacterId, member); - } - return club; - } - - public bool ClubExists(string clubName) { - return Context.Club.Any(club => club.Name == clubName); - } - - public bool ClubExists(long clubId) { - return Context.Club.Any(club => club.Id == clubId); - } - - public IList ListClubs(long characterId) { - return Context.ClubMember.Where(member => member.CharacterId == characterId) - .Select(member => member.ClubId) - .ToList(); - } - - public Club? CreateClub(IPlayerInfoProvider provider, string name, long leaderId, List members) { - BeginTransaction(); - var club = new Model.Club { - Name = name, - LeaderId = leaderId, - CreationTime = DateTime.UtcNow, - State = ClubState.Staged, - }; - Context.Club.Add(club); - if (!SaveChanges()) { - return null; - } - - foreach (PlayerInfo info in members) { - CreateClubMember(club.Id, info); - } - - return Commit() ? GetClub(provider, club.Id) : null; - } - - public ClubMember? CreateClubMember(long clubId, PlayerInfo info) { - var member = new Model.ClubMember { - ClubId = clubId, - CharacterId = info.CharacterId, - }; - Context.ClubMember.Add(member); - if (!SaveChanges()) { - return null; - } - - return new ClubMember { - ClubId = member.ClubId, - Info = info, - JoinTime = member.CreationTime.ToEpochSeconds(), - }; - } - - public bool DeleteClub(long clubId) { - BeginTransaction(); - - int count = Context.Club.Where(club => club.Id == clubId).Delete(); - if (count == 0) { - return false; - } - - Context.ClubMember.Where(member => member.ClubId == clubId).Delete(); - - return Commit(); - } - - public bool DeleteClubMember(long clubId, long characterId) { - int count = Context.ClubMember.Where(member => member.ClubId == clubId && member.CharacterId == characterId).Delete(); - return SaveChanges() && count > 0; - } - - private List GetClubMembers(IPlayerInfoProvider provider, long clubId) { - return Context.ClubMember.Where(member => member.ClubId == clubId) - .AsEnumerable() - .Select(member => { - PlayerInfo? info = provider.GetPlayerInfo(member.CharacterId); - return info == null ? null : new ClubMember { - Info = info, - JoinTime = member.CreationTime.ToEpochSeconds(), - ClubId = member.ClubId, - }; - }).WhereNotNull().ToList(); - } - - public bool SaveClub(Club club) { - BeginTransaction(); - if (!Context.Club.Any(model => model.Id == club.Id)) { - return false; - } - Context.Club.Update(club); - - // Update club members - Dictionary saveMembers = club.Members.Values - .ToDictionary(member => member.Info.CharacterId, member => member); - IEnumerable existingMembers = Context.ClubMember - .Where(member => member.ClubId == club.Id) - .Select(member => new Model.ClubMember { - CharacterId = member.CharacterId, - }); - - foreach (Model.ClubMember member in existingMembers) { - if (saveMembers.Remove(member.CharacterId, out ClubMember? gameMember)) { - Model.ClubMember model = gameMember; - Context.ClubMember.Update(model); - } else { - Context.ClubMember.Remove(member); - } - } - Context.ClubMember.AddRange(saveMembers.Values.Select(member => member)); - - if (!SaveChanges()) { - return false; - } - return Commit(); - } - } -} +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Maple2.Model.Game; +using Maple2.Tools.Extensions; +using Z.EntityFramework.Plus; +using Club = Maple2.Model.Game.Club.Club; +using ClubMember = Maple2.Model.Game.Club.ClubMember; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public Club? GetClub(IPlayerInfoProvider provider, long clubId) { + Club? club = Context.Club.Find(clubId); + if (club == null) { + return null; + } + + List members = GetClubMembers(provider, clubId); + club.Leader = members.First(member => member.Info.CharacterId == club.LeaderId); + foreach (ClubMember member in members) { + club.Members.TryAdd(member.Info.CharacterId, member); + } + return club; + } + + public bool ClubExists(string clubName) { + return Context.Club.Any(club => club.Name == clubName); + } + + public bool ClubExists(long clubId) { + return Context.Club.Any(club => club.Id == clubId); + } + + public IList ListClubs(long characterId) { + return Context.ClubMember.Where(member => member.CharacterId == characterId) + .Select(member => member.ClubId) + .ToList(); + } + + public Club? CreateClub(IPlayerInfoProvider provider, string name, long leaderId, List members) { + BeginTransaction(); + var club = new Model.Club { + Name = name, + LeaderId = leaderId, + CreationTime = DateTime.UtcNow, + State = ClubState.Staged, + }; + Context.Club.Add(club); + if (!SaveChanges()) { + return null; + } + + foreach (PlayerInfo info in members) { + CreateClubMember(club.Id, info); + } + + return Commit() ? GetClub(provider, club.Id) : null; + } + + public ClubMember? CreateClubMember(long clubId, PlayerInfo info) { + var member = new Model.ClubMember { + ClubId = clubId, + CharacterId = info.CharacterId, + }; + Context.ClubMember.Add(member); + if (!SaveChanges()) { + return null; + } + + return new ClubMember { + ClubId = member.ClubId, + Info = info, + JoinTime = member.CreationTime.ToEpochSeconds(), + }; + } + + public bool DeleteClub(long clubId) { + BeginTransaction(); + + int count = Context.Club.Where(club => club.Id == clubId).Delete(); + if (count == 0) { + return false; + } + + Context.ClubMember.Where(member => member.ClubId == clubId).Delete(); + + return Commit(); + } + + public bool DeleteClubMember(long clubId, long characterId) { + int count = Context.ClubMember.Where(member => member.ClubId == clubId && member.CharacterId == characterId).Delete(); + return SaveChanges() && count > 0; + } + + private List GetClubMembers(IPlayerInfoProvider provider, long clubId) { + return Context.ClubMember.Where(member => member.ClubId == clubId) + .AsEnumerable() + .Select(member => { + PlayerInfo? info = provider.GetPlayerInfo(member.CharacterId); + return info == null ? null : new ClubMember { + Info = info, + JoinTime = member.CreationTime.ToEpochSeconds(), + ClubId = member.ClubId, + }; + }).WhereNotNull().ToList(); + } + + public bool SaveClub(Club club) { + BeginTransaction(); + if (!Context.Club.Any(model => model.Id == club.Id)) { + return false; + } + Context.Club.Update(club); + + // Update club members + Dictionary saveMembers = club.Members.Values + .ToDictionary(member => member.Info.CharacterId, member => member); + IEnumerable existingMembers = Context.ClubMember + .Where(member => member.ClubId == club.Id) + .Select(member => new Model.ClubMember { + CharacterId = member.CharacterId, + }); + + foreach (Model.ClubMember member in existingMembers) { + if (saveMembers.Remove(member.CharacterId, out ClubMember? gameMember)) { + Model.ClubMember model = gameMember; + Context.ClubMember.Update(model); + } else { + Context.ClubMember.Remove(member); + } + } + Context.ClubMember.AddRange(saveMembers.Values.Select(member => member)); + + if (!SaveChanges()) { + return false; + } + return Commit(); + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Dungeon.cs b/Maple2.Database/Storage/Game/GameStorage.Dungeon.cs index 14957ee1b..dd583f68b 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Dungeon.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Dungeon.cs @@ -1,34 +1,34 @@ -using Maple2.Database.Extensions; -using Maple2.Model.Game.Dungeon; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public Dictionary GetDungeonRecords(long ownerId) { - return Context.DungeonRecord.Where(record => record.OwnerId == ownerId) - .AsEnumerable() - .Select(record => record) - .ToDictionary(record => record.DungeonId); - } - - public DungeonRecord? CreateDungeonRecord(DungeonRecord dungeonRecord, long ownerId) { - Model.DungeonRecord model = dungeonRecord; - model.OwnerId = ownerId; - Context.DungeonRecord.Add(model); - - return Context.TrySaveChanges() ? model : null; - } - - public bool SaveDungeonRecords(long ownerId, params DungeonRecord[] records) { - var models = new Model.DungeonRecord[records.Length]; - for (int i = 0; i < records.Length; i++) { - models[i] = records[i]; - models[i].OwnerId = ownerId; - Context.DungeonRecord.Update(models[i]); - } - - return Context.TrySaveChanges(); - } - } -} +using Maple2.Database.Extensions; +using Maple2.Model.Game.Dungeon; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public Dictionary GetDungeonRecords(long ownerId) { + return Context.DungeonRecord.Where(record => record.OwnerId == ownerId) + .AsEnumerable() + .Select(record => record) + .ToDictionary(record => record.DungeonId); + } + + public DungeonRecord? CreateDungeonRecord(DungeonRecord dungeonRecord, long ownerId) { + Model.DungeonRecord model = dungeonRecord; + model.OwnerId = ownerId; + Context.DungeonRecord.Add(model); + + return Context.TrySaveChanges() ? model : null; + } + + public bool SaveDungeonRecords(long ownerId, params DungeonRecord[] records) { + var models = new Model.DungeonRecord[records.Length]; + for (int i = 0; i < records.Length; i++) { + models[i] = records[i]; + models[i].OwnerId = ownerId; + Context.DungeonRecord.Update(models[i]); + } + + return Context.TrySaveChanges(); + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.GameEventUserValue.cs b/Maple2.Database/Storage/Game/GameStorage.GameEventUserValue.cs index aa860e113..e919409bf 100644 --- a/Maple2.Database/Storage/Game/GameStorage.GameEventUserValue.cs +++ b/Maple2.Database/Storage/Game/GameStorage.GameEventUserValue.cs @@ -1,60 +1,60 @@ -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Microsoft.EntityFrameworkCore; -using GameEventUserValue = Maple2.Model.Game.GameEventUserValue; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public IList GetEventUserValues(long characterId) { - return Context.GameEventUserValue.Where(model => model.CharacterId == characterId) - .Select(userValue => userValue) - .ToList(); - } - - public void RemoveGameEventUserValue(long characterId, int eventId) { - List list = Context.GameEventUserValue - .Where(model => model.CharacterId == characterId && model.EventId == eventId) - .ToList(); - - foreach (Model.GameEventUserValue model in list) { - Context.GameEventUserValue.Remove(model); - } - } - - public bool RemoveGameEventUserValue(GameEventUserValue userValue, long characterId) { - Model.GameEventUserValue model = userValue; - model.CharacterId = characterId; - Context.GameEventUserValue.Remove(model); - return Context.TrySaveChanges(); - } - - public bool SaveGameEventUserValues(long characterId, IList values) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - Dictionary> existing = Context.GameEventUserValue - .Where(model => model.CharacterId == characterId) - .GroupBy(model => model.EventId) - .ToDictionary( - group => group.Key, - group => group.ToDictionary(model => model.Type, model => model) - ); - - foreach (GameEventUserValue value in values) { - if (existing.TryGetValue(value.EventId, out Dictionary? modelDictionary) && - modelDictionary.TryGetValue(value.Type, out Model.GameEventUserValue? model)) { - model.Value = value.Value; - model.ExpirationTime = value.ExpirationTime; - Context.GameEventUserValue.Update(model); - } else { - model = value; - model.CharacterId = characterId; - Context.GameEventUserValue.Add(model); - } - } - - return Context.TrySaveChanges(); - } - } -} +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Microsoft.EntityFrameworkCore; +using GameEventUserValue = Maple2.Model.Game.GameEventUserValue; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public IList GetEventUserValues(long characterId) { + return Context.GameEventUserValue.Where(model => model.CharacterId == characterId) + .Select(userValue => userValue) + .ToList(); + } + + public void RemoveGameEventUserValue(long characterId, int eventId) { + List list = Context.GameEventUserValue + .Where(model => model.CharacterId == characterId && model.EventId == eventId) + .ToList(); + + foreach (Model.GameEventUserValue model in list) { + Context.GameEventUserValue.Remove(model); + } + } + + public bool RemoveGameEventUserValue(GameEventUserValue userValue, long characterId) { + Model.GameEventUserValue model = userValue; + model.CharacterId = characterId; + Context.GameEventUserValue.Remove(model); + return Context.TrySaveChanges(); + } + + public bool SaveGameEventUserValues(long characterId, IList values) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + Dictionary> existing = Context.GameEventUserValue + .Where(model => model.CharacterId == characterId) + .GroupBy(model => model.EventId) + .ToDictionary( + group => group.Key, + group => group.ToDictionary(model => model.Type, model => model) + ); + + foreach (GameEventUserValue value in values) { + if (existing.TryGetValue(value.EventId, out Dictionary? modelDictionary) && + modelDictionary.TryGetValue(value.Type, out Model.GameEventUserValue? model)) { + model.Value = value.Value; + model.ExpirationTime = value.ExpirationTime; + Context.GameEventUserValue.Update(model); + } else { + model = value; + model.CharacterId = characterId; + Context.GameEventUserValue.Add(model); + } + } + + return Context.TrySaveChanges(); + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Guild.cs b/Maple2.Database/Storage/Game/GameStorage.Guild.cs index f4f771d38..54246bfb4 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Guild.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Guild.cs @@ -1,232 +1,232 @@ -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Maple2.Model.Game; -using Maple2.Tools.Extensions; -using Z.EntityFramework.Plus; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public Guild? GetGuild(long guildId) { - return LoadGuild(guildId, string.Empty); - } - - public Guild? GetGuild(string guildName) { - return LoadGuild(0, guildName); - } - - public bool GuildExists(long guildId = 0, string guildName = "") { - return Context.Guild.Any(guild => guild.Id == guildId || guild.Name == guildName); - } - - public IList GetGuildMembers(IPlayerInfoProvider provider, long guildId) { - return Context.GuildMember.Where(member => member.GuildId == guildId) - .AsEnumerable() - .Select(member => { - PlayerInfo? info = provider.GetPlayerInfo(member.CharacterId); - return info == null ? null : new GuildMember { - GuildId = member.GuildId, - Info = info, - Message = member.Message, - Rank = member.Rank, - WeeklyContribution = member.WeeklyContribution, - TotalContribution = member.TotalContribution, - DailyDonationCount = member.DailyDonationCount, - JoinTime = member.CreationTime.ToEpochSeconds(), - CheckinTime = member.CheckinTime.ToEpochSeconds(), - DonationTime = member.DonationTime.ToEpochSeconds(), - }; - }) - .WhereNotNull() - .ToList(); - } - - public Guild? CreateGuild(string name, long leaderId) { - BeginTransaction(); - - var guild = new Model.Guild { - Name = name, - LeaderId = leaderId, - HouseRank = 1, - HouseTheme = 1, - Ranks = [ - new Model.GuildRank {Name = "Master", Permission = GuildPermission.All}, - new Model.GuildRank {Name = "Jr. Master", Permission = GuildPermission.Default}, - new Model.GuildRank {Name = "Member 1", Permission = GuildPermission.Default}, - new Model.GuildRank {Name = "Member 2", Permission = GuildPermission.Default}, - new Model.GuildRank {Name = "New Member 1", Permission = GuildPermission.Default}, - new Model.GuildRank {Name = "New Member 2", Permission = GuildPermission.Default}, - ], - Buffs = [ - new Model.GuildBuff {Id = 1, Level = 1}, - new Model.GuildBuff {Id = 2, Level = 1}, - new Model.GuildBuff {Id = 3, Level = 1}, - new Model.GuildBuff {Id = 4, Level = 1}, - new Model.GuildBuff {Id = 10001, Level = 1}, - new Model.GuildBuff {Id = 10002, Level = 1}, - new Model.GuildBuff {Id = 10003, Level = 1}, - new Model.GuildBuff {Id = 10004, Level = 1}, - new Model.GuildBuff {Id = 10005, Level = 1}, - ], - Posters = [], - Npcs = [], - }; - Context.Guild.Add(guild); - if (!SaveChanges()) { - return null; - } - - var guildLeader = new Model.GuildMember { - GuildId = guild.Id, - CharacterId = leaderId, - Rank = 0, - }; - Context.GuildMember.Add(guildLeader); - if (!SaveChanges()) { - return null; - } - - return Commit() ? LoadGuild(guild.Id, string.Empty) : null; - } - - public GuildMember? CreateGuildMember(long guildId, PlayerInfo info) { - var member = new Model.GuildMember { - GuildId = guildId, - CharacterId = info.CharacterId, - Rank = 5, - }; - Context.GuildMember.Add(member); - if (!SaveChanges()) { - return null; - } - - return new GuildMember { - GuildId = member.GuildId, - Info = info, - Rank = member.Rank, - JoinTime = member.CreationTime.ToEpochSeconds(), - }; - } - - public bool SaveGuild(Guild guild) { - // Don't save guild if it was disbanded. - if (!Context.Guild.Any(model => model.Id == guild.Id)) { - return false; - } - - BeginTransaction(); - - Context.Guild.Update(guild); - SaveGuildMembers(guild.Id, guild.Members.Values); - - return Commit(); - } - - public bool DeleteGuild(long guildId) { - BeginTransaction(); - - int count = Context.Guild.Where(guild => guild.Id == guildId).Delete(); - if (count == 0) { - return false; - } - - Context.GuildMember.Where(member => member.GuildId == guildId).Delete(); - Context.GuildApplication.Where(app => app.GuildId == guildId).Delete(); - - return Commit(); - } - - public bool DeleteGuildMember(long guildId, long characterId) { - int count = Context.GuildMember.Where(member => member.GuildId == guildId && member.CharacterId == characterId).Delete(); - return SaveChanges() && count > 0; - } - - public bool DeleteGuildApplication(long applicationId) { - int count = Context.GuildApplication.Where(app => app.Id == applicationId).Delete(); - return SaveChanges() && count > 0; - } - - public bool DeleteGuildApplications(long characterId) { - int count = Context.GuildApplication.Where(app => app.ApplicantId == characterId).Delete(); - return SaveChanges() && count > 0; - } - - public bool SaveGuildMembers(long guildId, ICollection members) { - Dictionary saveMembers = members - .ToDictionary(member => member.CharacterId, member => member); - IEnumerable existingMembers = Context.GuildMember - .Where(member => member.GuildId == guildId) - .Select(member => new Model.GuildMember { - CharacterId = member.CharacterId, - }); - - foreach (Model.GuildMember member in existingMembers) { - if (saveMembers.Remove(member.CharacterId, out GuildMember? gameMember)) { - Context.GuildMember.Update(gameMember); - } else { - Context.GuildMember.Remove(member); - } - } - Context.GuildMember.AddRange(saveMembers.Values.Select(member => member)); - - return SaveChanges(); - } - - public bool SaveGuildMember(GuildMember member) { - Model.GuildMember? model = Context.GuildMember.Find(member.GuildId, member.CharacterId); - if (model == null) { - return false; - } - - Context.GuildMember.Update(member); - return SaveChanges(); - } - - // Note: GuildMembers must be loaded separately. - private Guild? LoadGuild(long guildId, string guildName) { - IQueryable query = guildId > 0 - ? Context.Guild.Where(guild => guild.Id == guildId) - : Context.Guild.Where(guild => guild.Name == guildName); - return query - .Join(Context.Character, guild => guild.LeaderId, character => character.Id, - (guild, character) => new Tuple(guild, character)) - .AsEnumerable() - .Select(entry => { - Model.Guild guild = entry.Item1; - Character character = entry.Item2; - return new Guild(guild.Id, guild.Name, character.AccountId, character.Id, character.Name) { - Emblem = guild.Emblem, - Notice = guild.Notice, - CreationTime = guild.CreationTime.ToEpochSeconds(), - Focus = guild.Focus, - Experience = guild.Experience, - Funds = guild.Funds, - HouseRank = guild.HouseRank, - HouseTheme = guild.HouseTheme, - Ranks = guild.Ranks.Select((rank, i) => new GuildRank { - Id = (byte) i, - Name = rank.Name, - Permission = rank.Permission, - }).ToArray(), - Buffs = guild.Buffs.Select(skill => new GuildBuff { - Id = skill.Id, - Level = skill.Level, - ExpiryTime = skill.ExpiryTime, - }).ToList(), - Posters = guild.Posters.Select(poster => new GuildPoster { - Id = poster.Id, - Picture = poster.Picture, - OwnerId = poster.OwnerId, - OwnerName = poster.OwnerName, - }).ToList(), - Npcs = guild.Npcs.Select(npc => new GuildNpc { - Type = npc.Type, - Level = npc.Level, - }).ToList(), - }; - }) - .FirstOrDefault(); - } - } -} +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Maple2.Model.Game; +using Maple2.Tools.Extensions; +using Z.EntityFramework.Plus; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public Guild? GetGuild(long guildId) { + return LoadGuild(guildId, string.Empty); + } + + public Guild? GetGuild(string guildName) { + return LoadGuild(0, guildName); + } + + public bool GuildExists(long guildId = 0, string guildName = "") { + return Context.Guild.Any(guild => guild.Id == guildId || guild.Name == guildName); + } + + public IList GetGuildMembers(IPlayerInfoProvider provider, long guildId) { + return Context.GuildMember.Where(member => member.GuildId == guildId) + .AsEnumerable() + .Select(member => { + PlayerInfo? info = provider.GetPlayerInfo(member.CharacterId); + return info == null ? null : new GuildMember { + GuildId = member.GuildId, + Info = info, + Message = member.Message, + Rank = member.Rank, + WeeklyContribution = member.WeeklyContribution, + TotalContribution = member.TotalContribution, + DailyDonationCount = member.DailyDonationCount, + JoinTime = member.CreationTime.ToEpochSeconds(), + CheckinTime = member.CheckinTime.ToEpochSeconds(), + DonationTime = member.DonationTime.ToEpochSeconds(), + }; + }) + .WhereNotNull() + .ToList(); + } + + public Guild? CreateGuild(string name, long leaderId) { + BeginTransaction(); + + var guild = new Model.Guild { + Name = name, + LeaderId = leaderId, + HouseRank = 1, + HouseTheme = 1, + Ranks = [ + new Model.GuildRank { Name = "Master", Permission = GuildPermission.All }, + new Model.GuildRank { Name = "Jr. Master", Permission = GuildPermission.Default }, + new Model.GuildRank { Name = "Member 1", Permission = GuildPermission.Default }, + new Model.GuildRank { Name = "Member 2", Permission = GuildPermission.Default }, + new Model.GuildRank { Name = "New Member 1", Permission = GuildPermission.Default }, + new Model.GuildRank { Name = "New Member 2", Permission = GuildPermission.Default }, + ], + Buffs = [ + new Model.GuildBuff { Id = 1, Level = 1 }, + new Model.GuildBuff { Id = 2, Level = 1 }, + new Model.GuildBuff { Id = 3, Level = 1 }, + new Model.GuildBuff { Id = 4, Level = 1 }, + new Model.GuildBuff { Id = 10001, Level = 1 }, + new Model.GuildBuff { Id = 10002, Level = 1 }, + new Model.GuildBuff { Id = 10003, Level = 1 }, + new Model.GuildBuff { Id = 10004, Level = 1 }, + new Model.GuildBuff { Id = 10005, Level = 1 }, + ], + Posters = [], + Npcs = [], + }; + Context.Guild.Add(guild); + if (!SaveChanges()) { + return null; + } + + var guildLeader = new Model.GuildMember { + GuildId = guild.Id, + CharacterId = leaderId, + Rank = 0, + }; + Context.GuildMember.Add(guildLeader); + if (!SaveChanges()) { + return null; + } + + return Commit() ? LoadGuild(guild.Id, string.Empty) : null; + } + + public GuildMember? CreateGuildMember(long guildId, PlayerInfo info) { + var member = new Model.GuildMember { + GuildId = guildId, + CharacterId = info.CharacterId, + Rank = 5, + }; + Context.GuildMember.Add(member); + if (!SaveChanges()) { + return null; + } + + return new GuildMember { + GuildId = member.GuildId, + Info = info, + Rank = member.Rank, + JoinTime = member.CreationTime.ToEpochSeconds(), + }; + } + + public bool SaveGuild(Guild guild) { + // Don't save guild if it was disbanded. + if (!Context.Guild.Any(model => model.Id == guild.Id)) { + return false; + } + + BeginTransaction(); + + Context.Guild.Update(guild); + SaveGuildMembers(guild.Id, guild.Members.Values); + + return Commit(); + } + + public bool DeleteGuild(long guildId) { + BeginTransaction(); + + int count = Context.Guild.Where(guild => guild.Id == guildId).Delete(); + if (count == 0) { + return false; + } + + Context.GuildMember.Where(member => member.GuildId == guildId).Delete(); + Context.GuildApplication.Where(app => app.GuildId == guildId).Delete(); + + return Commit(); + } + + public bool DeleteGuildMember(long guildId, long characterId) { + int count = Context.GuildMember.Where(member => member.GuildId == guildId && member.CharacterId == characterId).Delete(); + return SaveChanges() && count > 0; + } + + public bool DeleteGuildApplication(long applicationId) { + int count = Context.GuildApplication.Where(app => app.Id == applicationId).Delete(); + return SaveChanges() && count > 0; + } + + public bool DeleteGuildApplications(long characterId) { + int count = Context.GuildApplication.Where(app => app.ApplicantId == characterId).Delete(); + return SaveChanges() && count > 0; + } + + public bool SaveGuildMembers(long guildId, ICollection members) { + Dictionary saveMembers = members + .ToDictionary(member => member.CharacterId, member => member); + IEnumerable existingMembers = Context.GuildMember + .Where(member => member.GuildId == guildId) + .Select(member => new Model.GuildMember { + CharacterId = member.CharacterId, + }); + + foreach (Model.GuildMember member in existingMembers) { + if (saveMembers.Remove(member.CharacterId, out GuildMember? gameMember)) { + Context.GuildMember.Update(gameMember); + } else { + Context.GuildMember.Remove(member); + } + } + Context.GuildMember.AddRange(saveMembers.Values.Select(member => member)); + + return SaveChanges(); + } + + public bool SaveGuildMember(GuildMember member) { + Model.GuildMember? model = Context.GuildMember.Find(member.GuildId, member.CharacterId); + if (model == null) { + return false; + } + + Context.GuildMember.Update(member); + return SaveChanges(); + } + + // Note: GuildMembers must be loaded separately. + private Guild? LoadGuild(long guildId, string guildName) { + IQueryable query = guildId > 0 + ? Context.Guild.Where(guild => guild.Id == guildId) + : Context.Guild.Where(guild => guild.Name == guildName); + return query + .Join(Context.Character, guild => guild.LeaderId, character => character.Id, + (guild, character) => new Tuple(guild, character)) + .AsEnumerable() + .Select(entry => { + Model.Guild guild = entry.Item1; + Character character = entry.Item2; + return new Guild(guild.Id, guild.Name, character.AccountId, character.Id, character.Name) { + Emblem = guild.Emblem, + Notice = guild.Notice, + CreationTime = guild.CreationTime.ToEpochSeconds(), + Focus = guild.Focus, + Experience = guild.Experience, + Funds = guild.Funds, + HouseRank = guild.HouseRank, + HouseTheme = guild.HouseTheme, + Ranks = guild.Ranks.Select((rank, i) => new GuildRank { + Id = (byte) i, + Name = rank.Name, + Permission = rank.Permission, + }).ToArray(), + Buffs = guild.Buffs.Select(skill => new GuildBuff { + Id = skill.Id, + Level = skill.Level, + ExpiryTime = skill.ExpiryTime, + }).ToList(), + Posters = guild.Posters.Select(poster => new GuildPoster { + Id = poster.Id, + Picture = poster.Picture, + OwnerId = poster.OwnerId, + OwnerName = poster.OwnerName, + }).ToList(), + Npcs = guild.Npcs.Select(npc => new GuildNpc { + Type = npc.Type, + Level = npc.Level, + }).ToList(), + }; + }) + .FirstOrDefault(); + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.HomeLayout.cs b/Maple2.Database/Storage/Game/GameStorage.HomeLayout.cs index 82c24ce09..f4a6308e5 100644 --- a/Maple2.Database/Storage/Game/GameStorage.HomeLayout.cs +++ b/Maple2.Database/Storage/Game/GameStorage.HomeLayout.cs @@ -1,37 +1,37 @@ -using Maple2.Database.Extensions; -using Maple2.Database.Model; -using Microsoft.EntityFrameworkCore; -using HomeLayout = Maple2.Model.Game.HomeLayout; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public HomeLayout? SaveHomeLayout(HomeLayout layout) { - Model.HomeLayout homeLayout = layout; - Context.HomeLayout.Add(homeLayout); - foreach (HomeLayoutCube cubes in homeLayout.Cubes) { - Context.UgcCubeLayout.Add(cubes); - } - bool success = Context.TrySaveChanges(); - - return success ? ToHomeLayout(homeLayout) : null; - } - - public void RemoveHomeLayout(HomeLayout layout) { - Model.HomeLayout homeLayout = layout; - Context.HomeLayout.Remove(homeLayout); - Context.TrySaveChanges(); - } - - public HomeLayout? GetHomeLayout(long layoutUid) { - HomeLayout? layout = Context.HomeLayout - .Where(homeLayout => homeLayout.Uid == layoutUid) - .Include(homeLayout => homeLayout.Cubes) - .Select(ToHomeLayout) - .FirstOrDefault(); - - return layout; - } - } -} +using Maple2.Database.Extensions; +using Maple2.Database.Model; +using Microsoft.EntityFrameworkCore; +using HomeLayout = Maple2.Model.Game.HomeLayout; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public HomeLayout? SaveHomeLayout(HomeLayout layout) { + Model.HomeLayout homeLayout = layout; + Context.HomeLayout.Add(homeLayout); + foreach (HomeLayoutCube cubes in homeLayout.Cubes) { + Context.UgcCubeLayout.Add(cubes); + } + bool success = Context.TrySaveChanges(); + + return success ? ToHomeLayout(homeLayout) : null; + } + + public void RemoveHomeLayout(HomeLayout layout) { + Model.HomeLayout homeLayout = layout; + Context.HomeLayout.Remove(homeLayout); + Context.TrySaveChanges(); + } + + public HomeLayout? GetHomeLayout(long layoutUid) { + HomeLayout? layout = Context.HomeLayout + .Where(homeLayout => homeLayout.Uid == layoutUid) + .Include(homeLayout => homeLayout.Cubes) + .Select(ToHomeLayout) + .FirstOrDefault(); + + return layout; + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Item.cs b/Maple2.Database/Storage/Game/GameStorage.Item.cs index 596f41c57..8b73ee6d4 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Item.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Item.cs @@ -1,192 +1,192 @@ -using Maple2.Database.Extensions; -using Maple2.Database.Model; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Item = Maple2.Model.Game.Item; -using PetConfig = Maple2.Model.Game.PetConfig; -using UgcItemLook = Maple2.Model.Game.UgcItemLook; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public Item? CreateItem(long ownerId, Item item) { - Model.Item model = item; - model.OwnerId = ownerId; - model.Id = 0; - Context.Item.Add(model); - - return Context.TrySaveChanges() ? ToItem(model) : null; - } - - public Item? SplitItem(long ownerId, Item item, int amount) { - Model.Item model = item; - model.Amount = amount; - model.OwnerId = ownerId; - model.Slot = -1; - model.Group = ItemGroup.Default; - model.Id = 0; - Context.Item.Add(model); - - return Context.TrySaveChanges() ? ToItem(model) : null; - } - - public List? CreateItems(long ownerId, params Item[] items) { - var models = new Model.Item[items.Length]; - for (int i = 0; i < items.Length; i++) { - models[i] = items[i]; - models[i].OwnerId = ownerId; - models[i].Id = 0; - Context.Item.Add(models[i]); - } - - if (!Context.TrySaveChanges()) { - return null; - } - - return models.Select(ToItem).Where(item => item != null).ToList()!; - } - - public Item? GetItem(long itemUid) { - Model.Item? model = Context.Item.Find(itemUid); - if (model == null) { - return null; - } - - return game.itemMetadata.TryGet(model.ItemId, out ItemMetadata? metadata) ? model.Convert(metadata) : null; - } - - public UgcItemLook? GetTemplate(long itemUid) { - ItemSubType? model = Context.Item.Select(item => new { item.Id, item.SubType }) - .FirstOrDefault(result => result.Id == itemUid)?.SubType; - if (model is not ItemUgc ugcModel) { - return null; - } - - return ugcModel.Template; - } - public IDictionary> GetItemGroups(long ownerId, params ItemGroup[] groups) { - return Context.Item.Where(item => item.OwnerId == ownerId && groups.Contains(item.Group)) - .AsEnumerable() - .GroupBy(item => item.Group) - .ToDictionary( - group => group.Key, - group => group.Select(ToItem).Where(item => item != null).ToList() - )!; - } - - public Dictionary> GetInventory(long characterId) { - return Context.Item.Where(item => item.OwnerId == characterId && item.Group == ItemGroup.Default) - .AsEnumerable() - .Select(ToItem) - .Where(item => item != null) - .GroupBy(item => item!.Inventory) - .ToDictionary( - group => group.Key, - group => group.ToList() - )!; - } - - public (long Mesos, short Expand) GetStorageInfo(long accountId) { - ItemStorage? info = Context.ItemStorage.Find(accountId); - if (info == null) { - return (0, 0); - } - - return (info.Meso, info.Expand); - } - - public PetConfig GetPetConfig(long itemUid) { - return Context.PetConfig.Find(itemUid) ?? new PetConfig(); - } - - public List GetStorage(long accountId) { - return Context.Item.Where(item => item.OwnerId == accountId && item.Group == ItemGroup.Default) - .AsEnumerable() - .Select(ToItem) - .Where(item => item != null) - .ToList()!; - } - - public List GetSavedHairs(long characterId) { - return Context.Item.Where(item => item.OwnerId == characterId && item.Group == ItemGroup.SavedHair) - .AsEnumerable() - .Select(ToItem) - .Where(item => item != null) - .ToList()!; - } - - public List GetAllItems(long ownerId) { - return Context.Item.Where(item => item.OwnerId == ownerId) - .AsEnumerable() - .Select(ToItem) - .Where(item => item != null) - .ToList()!; - } - - public bool SaveItems(long ownerId, params Item[] items) { - var models = new Model.Item[items.Length]; - for (int i = 0; i < items.Length; i++) { - if (items[i].Uid == 0) { - continue; - } - - models[i] = items[i]; - models[i].OwnerId = ownerId; - Context.Item.Update(models[i]); - } - - return Context.TrySaveChanges(); - } - - public bool UpdateItem(Item item) { - Model.Item model = item; - Context.Item.Update(model); - - return Context.TrySaveChanges(); - } - - public bool SaveStorageInfo(long accountId, long mesos, short expand) { - ItemStorage? info = Context.ItemStorage.Find(accountId); - if (info == null) { - Context.Add(new ItemStorage { - AccountId = accountId, - Meso = mesos, - Expand = expand, - }); - } else { - info.Meso = mesos; - info.Expand = expand; - Context.ItemStorage.Update(info); - } - - return Context.TrySaveChanges(); - } - - public bool SavePetConfig(long itemUid, PetConfig config) { - Model.PetConfig? model = Context.PetConfig.Find(itemUid); - if (model == null) { - model = config; - model.ItemUid = itemUid; - - Context.Add(model); - } else { - model = config; - model.ItemUid = itemUid; - - Context.Update(model); - } - - return Context.TrySaveChanges(); - } - - // Converts model to item if possible, otherwise returns null. - private Item? ToItem(Model.Item? model) { - if (model == null) { - return null; - } - - return game.itemMetadata.TryGet(model.ItemId, out ItemMetadata? metadata) ? model.Convert(metadata) : null; - } - } -} +using Maple2.Database.Extensions; +using Maple2.Database.Model; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Item = Maple2.Model.Game.Item; +using PetConfig = Maple2.Model.Game.PetConfig; +using UgcItemLook = Maple2.Model.Game.UgcItemLook; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public Item? CreateItem(long ownerId, Item item) { + Model.Item model = item; + model.OwnerId = ownerId; + model.Id = 0; + Context.Item.Add(model); + + return Context.TrySaveChanges() ? ToItem(model) : null; + } + + public Item? SplitItem(long ownerId, Item item, int amount) { + Model.Item model = item; + model.Amount = amount; + model.OwnerId = ownerId; + model.Slot = -1; + model.Group = ItemGroup.Default; + model.Id = 0; + Context.Item.Add(model); + + return Context.TrySaveChanges() ? ToItem(model) : null; + } + + public List? CreateItems(long ownerId, params Item[] items) { + var models = new Model.Item[items.Length]; + for (int i = 0; i < items.Length; i++) { + models[i] = items[i]; + models[i].OwnerId = ownerId; + models[i].Id = 0; + Context.Item.Add(models[i]); + } + + if (!Context.TrySaveChanges()) { + return null; + } + + return models.Select(ToItem).Where(item => item != null).ToList()!; + } + + public Item? GetItem(long itemUid) { + Model.Item? model = Context.Item.Find(itemUid); + if (model == null) { + return null; + } + + return game.itemMetadata.TryGet(model.ItemId, out ItemMetadata? metadata) ? model.Convert(metadata) : null; + } + + public UgcItemLook? GetTemplate(long itemUid) { + ItemSubType? model = Context.Item.Select(item => new { item.Id, item.SubType }) + .FirstOrDefault(result => result.Id == itemUid)?.SubType; + if (model is not ItemUgc ugcModel) { + return null; + } + + return ugcModel.Template; + } + public IDictionary> GetItemGroups(long ownerId, params ItemGroup[] groups) { + return Context.Item.Where(item => item.OwnerId == ownerId && groups.Contains(item.Group)) + .AsEnumerable() + .GroupBy(item => item.Group) + .ToDictionary( + group => group.Key, + group => group.Select(ToItem).Where(item => item != null).ToList() + )!; + } + + public Dictionary> GetInventory(long characterId) { + return Context.Item.Where(item => item.OwnerId == characterId && item.Group == ItemGroup.Default) + .AsEnumerable() + .Select(ToItem) + .Where(item => item != null) + .GroupBy(item => item!.Inventory) + .ToDictionary( + group => group.Key, + group => group.ToList() + )!; + } + + public (long Mesos, short Expand) GetStorageInfo(long accountId) { + ItemStorage? info = Context.ItemStorage.Find(accountId); + if (info == null) { + return (0, 0); + } + + return (info.Meso, info.Expand); + } + + public PetConfig GetPetConfig(long itemUid) { + return Context.PetConfig.Find(itemUid) ?? new PetConfig(); + } + + public List GetStorage(long accountId) { + return Context.Item.Where(item => item.OwnerId == accountId && item.Group == ItemGroup.Default) + .AsEnumerable() + .Select(ToItem) + .Where(item => item != null) + .ToList()!; + } + + public List GetSavedHairs(long characterId) { + return Context.Item.Where(item => item.OwnerId == characterId && item.Group == ItemGroup.SavedHair) + .AsEnumerable() + .Select(ToItem) + .Where(item => item != null) + .ToList()!; + } + + public List GetAllItems(long ownerId) { + return Context.Item.Where(item => item.OwnerId == ownerId) + .AsEnumerable() + .Select(ToItem) + .Where(item => item != null) + .ToList()!; + } + + public bool SaveItems(long ownerId, params Item[] items) { + var models = new Model.Item[items.Length]; + for (int i = 0; i < items.Length; i++) { + if (items[i].Uid == 0) { + continue; + } + + models[i] = items[i]; + models[i].OwnerId = ownerId; + Context.Item.Update(models[i]); + } + + return Context.TrySaveChanges(); + } + + public bool UpdateItem(Item item) { + Model.Item model = item; + Context.Item.Update(model); + + return Context.TrySaveChanges(); + } + + public bool SaveStorageInfo(long accountId, long mesos, short expand) { + ItemStorage? info = Context.ItemStorage.Find(accountId); + if (info == null) { + Context.Add(new ItemStorage { + AccountId = accountId, + Meso = mesos, + Expand = expand, + }); + } else { + info.Meso = mesos; + info.Expand = expand; + Context.ItemStorage.Update(info); + } + + return Context.TrySaveChanges(); + } + + public bool SavePetConfig(long itemUid, PetConfig config) { + Model.PetConfig? model = Context.PetConfig.Find(itemUid); + if (model == null) { + model = config; + model.ItemUid = itemUid; + + Context.Add(model); + } else { + model = config; + model.ItemUid = itemUid; + + Context.Update(model); + } + + return Context.TrySaveChanges(); + } + + // Converts model to item if possible, otherwise returns null. + private Item? ToItem(Model.Item? model) { + if (model == null) { + return null; + } + + return game.itemMetadata.TryGet(model.ItemId, out ItemMetadata? metadata) ? model.Convert(metadata) : null; + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Mail.cs b/Maple2.Database/Storage/Game/GameStorage.Mail.cs index d94f30a4f..a0a564b86 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Mail.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Mail.cs @@ -1,141 +1,141 @@ -using Microsoft.EntityFrameworkCore; -using Mail = Maple2.Model.Game.Mail; -using Item = Maple2.Model.Game.Item; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public Mail? GetMail(long mailId, long characterId) { - Model.Mail? model = Context.Mail.Find(characterId, mailId); - if (model == null) { - return null; - } - - Mail mail = model; - foreach (Item item in GetAllItems(mailId)) { - mail.Items.Add(item); - } - - return mail; - } - - public ICollection GetSentMail(long characterId) { - Mail[] mails = Context.Mail.Where(mail => mail.SenderId == characterId) - .AsEnumerable() - .Select(mail => mail) - .ToArray(); - - foreach (Mail mail in mails) { - foreach (Item item in GetAllItems(mail.Id)) { - mail.Items.Add(item); - } - } - - return mails; - } - - // Binds all mails from an account to the first character that access them - public void BindAccountMailsToCharacter(long accountId, long characterId) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - BeginTransaction(); - - List mails = Context.Mail.Where(mail => mail.ReceiverId == accountId).ToList(); - if (mails.Count == 0) { - Commit(); - return; - } - - foreach (Model.Mail mail in mails) { - Context.Mail.Remove(mail); - } - - Context.SaveChanges(); - - foreach (Model.Mail mail in mails) { - mail.ReceiverId = characterId; - Context.Mail.Add(mail); - } - - Context.SaveChanges(); - - if (!Commit()) { - throw new Exception("Failed to bind account mails to character"); - } - } - - public ICollection GetAllMail(long characterId, long minId = 0) { - Mail[] mails = Context.Mail.Where(mail => mail.ReceiverId == characterId) - .Where(mail => mail.Id > minId) - .AsEnumerable() - .Select(mail => mail) - .ToArray(); - - foreach (Mail mail in mails) { - foreach (Item item in GetAllItems(mail.Id)) { - mail.Items.Add(item); - } - } - - return mails; - } - - public Mail? CreateMail(Mail mail) { - Model.Mail model = mail; - model.Id = 0; - if (mail.Items.Count == 0) { - Context.Mail.Add(model); - return SaveChanges() ? model : null; - } - - BeginTransaction(); - Context.Mail.Add(model); - if (!SaveChanges()) { - return null; - } - - SaveItems(model.Id, mail.Items.ToArray()); - if (!Commit()) { - return null; - } - - Mail updatedMail = model; - foreach (Item item in mail.Items) { - updatedMail.Items.Add(item); - } - - return updatedMail; - } - - public Mail? MarkMailRead(long mailId, long characterId) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - Model.Mail? mail = Context.Mail.Find(characterId, mailId); - if (mail == null || mail.ReadTime > DateTime.Now) { - return null; - } - - mail.ReadTime = DateTime.Now; - Context.Mail.Update(mail); - return SaveChanges() ? mail : null; - } - - public bool DeleteMail(long mailId, long characterId) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - Model.Mail? mail = Context.Mail.Find(characterId, mailId); - if (mail == null) { - return false; - } - - Context.Mail.Remove(mail); - return SaveChanges(); - } - - public Mail? UpdateMail(Mail mail) { - Model.Mail model = mail; - Context.Mail.Update(model); - return SaveChanges() ? model : null; - } - } -} +using Microsoft.EntityFrameworkCore; +using Mail = Maple2.Model.Game.Mail; +using Item = Maple2.Model.Game.Item; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public Mail? GetMail(long mailId, long characterId) { + Model.Mail? model = Context.Mail.Find(characterId, mailId); + if (model == null) { + return null; + } + + Mail mail = model; + foreach (Item item in GetAllItems(mailId)) { + mail.Items.Add(item); + } + + return mail; + } + + public ICollection GetSentMail(long characterId) { + Mail[] mails = Context.Mail.Where(mail => mail.SenderId == characterId) + .AsEnumerable() + .Select(mail => mail) + .ToArray(); + + foreach (Mail mail in mails) { + foreach (Item item in GetAllItems(mail.Id)) { + mail.Items.Add(item); + } + } + + return mails; + } + + // Binds all mails from an account to the first character that access them + public void BindAccountMailsToCharacter(long accountId, long characterId) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + BeginTransaction(); + + List mails = Context.Mail.Where(mail => mail.ReceiverId == accountId).ToList(); + if (mails.Count == 0) { + Commit(); + return; + } + + foreach (Model.Mail mail in mails) { + Context.Mail.Remove(mail); + } + + Context.SaveChanges(); + + foreach (Model.Mail mail in mails) { + mail.ReceiverId = characterId; + Context.Mail.Add(mail); + } + + Context.SaveChanges(); + + if (!Commit()) { + throw new Exception("Failed to bind account mails to character"); + } + } + + public ICollection GetAllMail(long characterId, long minId = 0) { + Mail[] mails = Context.Mail.Where(mail => mail.ReceiverId == characterId) + .Where(mail => mail.Id > minId) + .AsEnumerable() + .Select(mail => mail) + .ToArray(); + + foreach (Mail mail in mails) { + foreach (Item item in GetAllItems(mail.Id)) { + mail.Items.Add(item); + } + } + + return mails; + } + + public Mail? CreateMail(Mail mail) { + Model.Mail model = mail; + model.Id = 0; + if (mail.Items.Count == 0) { + Context.Mail.Add(model); + return SaveChanges() ? model : null; + } + + BeginTransaction(); + Context.Mail.Add(model); + if (!SaveChanges()) { + return null; + } + + SaveItems(model.Id, mail.Items.ToArray()); + if (!Commit()) { + return null; + } + + Mail updatedMail = model; + foreach (Item item in mail.Items) { + updatedMail.Items.Add(item); + } + + return updatedMail; + } + + public Mail? MarkMailRead(long mailId, long characterId) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + Model.Mail? mail = Context.Mail.Find(characterId, mailId); + if (mail == null || mail.ReadTime > DateTime.Now) { + return null; + } + + mail.ReadTime = DateTime.Now; + Context.Mail.Update(mail); + return SaveChanges() ? mail : null; + } + + public bool DeleteMail(long mailId, long characterId) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + Model.Mail? mail = Context.Mail.Find(characterId, mailId); + if (mail == null) { + return false; + } + + Context.Mail.Remove(mail); + return SaveChanges(); + } + + public Mail? UpdateMail(Mail mail) { + Model.Mail model = mail; + Context.Mail.Update(model); + return SaveChanges() ? model : null; + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Map.cs b/Maple2.Database/Storage/Game/GameStorage.Map.cs index 42f65d3dc..b38798371 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Map.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Map.cs @@ -1,337 +1,337 @@ -using System.Diagnostics; -using Maple2.Database.Extensions; -using Maple2.Database.Model; -using Maple2.Model.Common; -using Maple2.Model.Game; -using Maple2.Model.Metadata; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using Z.EntityFramework.Plus; -using Home = Maple2.Model.Game.Home; -using InteractCube = Maple2.Model.Game.InteractCube; -using HomeLayout = Maple2.Model.Game.HomeLayout; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public IList LoadPlotsForMap(int mapId, long ownerId = -1) { - IQueryable query; - if (ownerId >= 0) { - query = Context.UgcMap.Include(map => map.Cubes) - .Where(map => map.MapId == mapId && map.OwnerId == ownerId); - } else { - query = Context.UgcMap.Include(map => map.Cubes) - .Where(map => map.MapId == mapId && map.MapId != Constant.DefaultHomeMapId); - } - - return query.AsEnumerable() - .ToList() // ToList before Select so 'This MySqlConnection is already in use.' exception doesn't occur. - .Select(ToPlot) - .ToList()!; - } - - public IList LoadCubesForOwner(long ownerId) { - List plotCubes = Context.UgcMap.Where(map => map.OwnerId == ownerId) - .Join(Context.UgcMapCube, ugcMap => ugcMap.Id, cube => cube.UgcMapId, (ugcMap, cube) => cube) - .AsEnumerable() - .Select(ToPlotCube) - .Where(cube => cube != null) - .ToList()!; - foreach (PlotCube cube in plotCubes) { - if (cube.Interact?.Metadata.Nurturing is null) continue; - - cube.Interact!.Nurturing = GetNurturing(ownerId, cube.ItemId, cube.Interact.Metadata.Nurturing); - } - - return plotCubes; - } - - public PlotInfo? BuyPlot(string characterName, long ownerId, PlotInfo plot, TimeSpan days) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - UgcMap? ugcMap = Context.UgcMap.FirstOrDefault(map => map.Id == plot.Id && !map.Indoor); - if (ugcMap == null) { - return null; - } - - Debug.Assert(ugcMap.MapId == plot.MapId && ugcMap.Number == plot.Number && ugcMap.ApartmentNumber == plot.ApartmentNumber); - if (ugcMap.OwnerId != 0 || ugcMap.ExpiryTime >= DateTime.Now) { - return null; - } - - ugcMap.OwnerId = ownerId; - ugcMap.ExpiryTime = DateTime.UtcNow + days; - ugcMap.Name = characterName; - Context.UgcMap.Update(ugcMap); - Context.UgcMapCube.Where(cube => cube.UgcMapId == ugcMap.Id).Delete(); - - return Context.TrySaveChanges() ? ToPlotInfo(ugcMap) : null; - } - - public PlotInfo? ExtendPlot(PlotInfo plot, TimeSpan days) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - UgcMap? model = Context.UgcMap.Find(plot.Id); - if (model == null) { - return null; - } - - Debug.Assert(model.MapId == plot.MapId && model.Number == plot.Number && model.ApartmentNumber == plot.ApartmentNumber); - if (model.ExpiryTime < DateTime.Now) { - return null; - } - - model.ExpiryTime += days; - Context.UgcMap.Update(model); - - return Context.TrySaveChanges() ? ToPlotInfo(model) : null; - } - - public PlotInfo? ForfeitPlot(long ownerId, PlotInfo plot) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - UgcMap? model = Context.UgcMap.Find(plot.Id); - if (model == null || model.OwnerId != ownerId) { - return null; - } - - Debug.Assert(model.MapId == plot.MapId && model.Number == plot.Number && model.ApartmentNumber == plot.ApartmentNumber); - if (model.ExpiryTime < DateTime.Now) { - return null; - } - - model.OwnerId = 0; - model.Name = string.Empty; - model.ExpiryTime = DateTimeOffset.UtcNow; - Context.UgcMapCube.Where(cube => cube.UgcMapId == model.Id).Delete(); - Context.UgcMap.Update(model); - - return Context.TrySaveChanges() ? ToPlotInfo(model) : null; - } - - public bool SaveHome(Home home) { - Model.Home model = home; - Context.Home.Update(model); - if (!Context.TrySaveChanges()) { - return false; - } - - home.LastModified = model.LastModified.ToEpochSeconds(); - return true; - } - - public bool SavePlotInfo(params PlotInfo[] plotInfos) { - foreach (PlotInfo plotInfo in plotInfos) { - UgcMap? model = Context.UgcMap.Find(plotInfo.Id); - if (model == null) { - return false; - } - - model.OwnerId = plotInfo.OwnerId; - model.MapId = plotInfo.MapId; - model.Number = plotInfo.Number; - model.ApartmentNumber = plotInfo.ApartmentNumber; - model.ExpiryTime = plotInfo.ExpiryTime.FromEpochSeconds(); - model.Name = plotInfo.Name; - Context.UgcMap.Update(model); - } - - return Context.TrySaveChanges(); - } - - public ICollection? SaveCubes(PlotInfo plotInfo, IEnumerable cubes) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - var results = new List(); - var updated = new HashSet(); - foreach (PlotCube cube in cubes) { - UgcMapCube model = cube; - model.UgcMapId = plotInfo.Id; - if (model.Id >= Constant.FurnishingBaseId) { - model.Id = 0; // This needs to be auto-generated. - results.Add(model); - Context.UgcMapCube.Add(model); - } else { - updated.Add(model.Id); - results.Add(model); - Context.UgcMapCube.Update(model); - } - } - foreach (UgcMapCube cube in Context.UgcMapCube.Where(cube => cube.UgcMapId == plotInfo.Id)) { - if (!updated.Contains(cube.Id)) { - Context.UgcMapCube.Remove(cube); - } - } - - if (!Context.TrySaveChanges()) { - return null; - } - - PlotCube[] plotCubes = results - .Select(ToPlotCube) - .Where(cube => cube != null) - .ToArray()!; - foreach (PlotCube cube in plotCubes) { - if (cube.Interact?.Metadata.Nurturing is null) continue; - - cube.Interact!.Nurturing = GetNurturing(plotInfo.OwnerId, cube.ItemId, cube.Interact.Metadata.Nurturing); - } - - return plotCubes; - } - - public bool InitUgcMap(IEnumerable maps) { - // If there are entries, we assume it's already initialized. - if (Context.UgcMap.Any()) { - return true; - } - - foreach (UgcMapMetadata map in maps) { - if (map.Id == Constant.DefaultHomeMapId) { - continue; - } - - foreach (UgcMapGroup group in map.Plots.Values) { - Context.UgcMap.Add(new UgcMap { - MapId = map.Id, - Number = group.Number, - ApartmentNumber = group.ApartmentNumber, - }); - } - } - - return Context.TrySaveChanges(); - } - - private Plot? ToPlot(UgcMap? ugcMap) { - if (ugcMap == null || !game.mapMetadata.TryGetUgc(ugcMap.MapId, out UgcMapMetadata? metadata)) { - return null; - } - - if (!metadata.Plots.TryGetValue(ugcMap.Number, out UgcMapGroup? group)) { - return null; - } - - if (!ValidateAndFixHomeExpiryTime(ugcMap)) { - return null; - } - - var plot = new Plot(group) { - Id = ugcMap.Id, - OwnerId = ugcMap.OwnerId, - MapId = ugcMap.MapId, - Number = ugcMap.Number, - ApartmentNumber = 0, - ExpiryTime = ugcMap.ExpiryTime.ToUnixTimeSeconds(), - }; - - if (ugcMap.Cubes == null) return plot; - - foreach (UgcMapCube cube in ugcMap.Cubes) { - PlotCube? plotCube = ToPlotCube(cube); - if (plotCube == null) { - continue; - } - - if (plotCube.Interact?.Metadata.Nurturing is not null) { - plotCube.Interact.Nurturing = GetNurturing(ugcMap.OwnerId, cube.Interact!.ObjectCode, plotCube.Interact.Metadata.Nurturing); - } - - plot.Cubes.Add(plotCube.Position, plotCube); - } - - return plot; - } - - private PlotInfo? ToPlotInfo(UgcMap? ugcMap) { - if (ugcMap == null || !game.mapMetadata.TryGetUgc(ugcMap.MapId, out UgcMapMetadata? metadata)) { - return null; - } - - if (!metadata.Plots.TryGetValue(ugcMap.Number, out UgcMapGroup? group)) { - return null; - } - - if (!ValidateAndFixHomeExpiryTime(ugcMap)) { - return null; - } - - return new PlotInfo(group) { - Id = ugcMap.Id, - OwnerId = ugcMap.OwnerId, - MapId = ugcMap.MapId, - Number = ugcMap.Number, - Name = ugcMap.Name, - ApartmentNumber = 0, - ExpiryTime = ugcMap.ExpiryTime.ToUnixTimeSeconds(), - }; - } - - private HomeLayout? ToHomeLayout(Model.HomeLayout? model) { - if (model == null) { - return null; - } - - List cubes = model.Cubes.Select(ToPlotCube) - .Where(cube => cube != null) - .ToList()!; - - return new HomeLayout(model.Uid, model.Id, model.Name, model.Area, model.Height, model.Timestamp, cubes); - } - - // Converts model to interact cube if possible, otherwise returns null. - private InteractCube? ToInteractCube(Model.InteractCube? model) { - if (model == null) { - return null; - } - - return game.functionCubeMetadata.TryGet(model.ObjectCode, out FunctionCubeMetadata? metadata) ? model.Convert(metadata, model.NoticeSettings, model.PortalSettings) : null; - } - - private PlotCube? ToPlotCube(HomeLayoutCube? model) { - if (model == null) { - return null; - } - - if (!game.itemMetadata.TryGet(model.ItemId, out ItemMetadata? itemMetadata)) { - return null; - } - - return new PlotCube(itemMetadata, model.Id, model.Template) { - Position = new Vector3B(model.X, model.Y, model.Z), - Rotation = model.Rotation, - Interact = ToInteractCube(model.Interact), - Type = PlotCube.CubeType.Construction, - }; - } - - private PlotCube? ToPlotCube(UgcMapCube? model) { - if (model == null) { - return null; - } - - if (!game.itemMetadata.TryGet(model.ItemId, out ItemMetadata? itemMetadata)) { - return null; - } - - return new PlotCube(itemMetadata, model.Id, model.Template) { - Position = new Vector3B(model.X, model.Y, model.Z), - Rotation = model.Rotation, - Interact = ToInteractCube(model.Interact), - Type = PlotCube.CubeType.Construction, - }; - } - - private bool ValidateAndFixHomeExpiryTime(UgcMap ugcMap) { - if (!string.IsNullOrEmpty(ugcMap.Name) && ugcMap.MapId is Constant.DefaultHomeMapId && ugcMap.ExpiryTime != Home.HomeExpiryTime) { - Logger.LogError("Plot {Id} for {OwnerId} is initialized but it's ExpiryTime is not set to Home.HomeExpiryTime. Why?", ugcMap.Id, ugcMap.OwnerId); - - ugcMap.ExpiryTime = Home.HomeExpiryTime; - Context.UgcMap.Update(ugcMap); - return Context.TrySaveChanges(); - } - return true; - } - } -} +using System.Diagnostics; +using Maple2.Database.Extensions; +using Maple2.Database.Model; +using Maple2.Model.Common; +using Maple2.Model.Game; +using Maple2.Model.Metadata; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Z.EntityFramework.Plus; +using Home = Maple2.Model.Game.Home; +using InteractCube = Maple2.Model.Game.InteractCube; +using HomeLayout = Maple2.Model.Game.HomeLayout; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public IList LoadPlotsForMap(int mapId, long ownerId = -1) { + IQueryable query; + if (ownerId >= 0) { + query = Context.UgcMap.Include(map => map.Cubes) + .Where(map => map.MapId == mapId && map.OwnerId == ownerId); + } else { + query = Context.UgcMap.Include(map => map.Cubes) + .Where(map => map.MapId == mapId && map.MapId != Constant.DefaultHomeMapId); + } + + return query.AsEnumerable() + .ToList() // ToList before Select so 'This MySqlConnection is already in use.' exception doesn't occur. + .Select(ToPlot) + .ToList()!; + } + + public IList LoadCubesForOwner(long ownerId) { + List plotCubes = Context.UgcMap.Where(map => map.OwnerId == ownerId) + .Join(Context.UgcMapCube, ugcMap => ugcMap.Id, cube => cube.UgcMapId, (ugcMap, cube) => cube) + .AsEnumerable() + .Select(ToPlotCube) + .Where(cube => cube != null) + .ToList()!; + foreach (PlotCube cube in plotCubes) { + if (cube.Interact?.Metadata.Nurturing is null) continue; + + cube.Interact!.Nurturing = GetNurturing(ownerId, cube.ItemId, cube.Interact.Metadata.Nurturing); + } + + return plotCubes; + } + + public PlotInfo? BuyPlot(string characterName, long ownerId, PlotInfo plot, TimeSpan days) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + UgcMap? ugcMap = Context.UgcMap.FirstOrDefault(map => map.Id == plot.Id && !map.Indoor); + if (ugcMap == null) { + return null; + } + + Debug.Assert(ugcMap.MapId == plot.MapId && ugcMap.Number == plot.Number && ugcMap.ApartmentNumber == plot.ApartmentNumber); + if (ugcMap.OwnerId != 0 || ugcMap.ExpiryTime >= DateTime.Now) { + return null; + } + + ugcMap.OwnerId = ownerId; + ugcMap.ExpiryTime = DateTime.UtcNow + days; + ugcMap.Name = characterName; + Context.UgcMap.Update(ugcMap); + Context.UgcMapCube.Where(cube => cube.UgcMapId == ugcMap.Id).Delete(); + + return Context.TrySaveChanges() ? ToPlotInfo(ugcMap) : null; + } + + public PlotInfo? ExtendPlot(PlotInfo plot, TimeSpan days) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + UgcMap? model = Context.UgcMap.Find(plot.Id); + if (model == null) { + return null; + } + + Debug.Assert(model.MapId == plot.MapId && model.Number == plot.Number && model.ApartmentNumber == plot.ApartmentNumber); + if (model.ExpiryTime < DateTime.Now) { + return null; + } + + model.ExpiryTime += days; + Context.UgcMap.Update(model); + + return Context.TrySaveChanges() ? ToPlotInfo(model) : null; + } + + public PlotInfo? ForfeitPlot(long ownerId, PlotInfo plot) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + UgcMap? model = Context.UgcMap.Find(plot.Id); + if (model == null || model.OwnerId != ownerId) { + return null; + } + + Debug.Assert(model.MapId == plot.MapId && model.Number == plot.Number && model.ApartmentNumber == plot.ApartmentNumber); + if (model.ExpiryTime < DateTime.Now) { + return null; + } + + model.OwnerId = 0; + model.Name = string.Empty; + model.ExpiryTime = DateTimeOffset.UtcNow; + Context.UgcMapCube.Where(cube => cube.UgcMapId == model.Id).Delete(); + Context.UgcMap.Update(model); + + return Context.TrySaveChanges() ? ToPlotInfo(model) : null; + } + + public bool SaveHome(Home home) { + Model.Home model = home; + Context.Home.Update(model); + if (!Context.TrySaveChanges()) { + return false; + } + + home.LastModified = model.LastModified.ToEpochSeconds(); + return true; + } + + public bool SavePlotInfo(params PlotInfo[] plotInfos) { + foreach (PlotInfo plotInfo in plotInfos) { + UgcMap? model = Context.UgcMap.Find(plotInfo.Id); + if (model == null) { + return false; + } + + model.OwnerId = plotInfo.OwnerId; + model.MapId = plotInfo.MapId; + model.Number = plotInfo.Number; + model.ApartmentNumber = plotInfo.ApartmentNumber; + model.ExpiryTime = plotInfo.ExpiryTime.FromEpochSeconds(); + model.Name = plotInfo.Name; + Context.UgcMap.Update(model); + } + + return Context.TrySaveChanges(); + } + + public ICollection? SaveCubes(PlotInfo plotInfo, IEnumerable cubes) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + var results = new List(); + var updated = new HashSet(); + foreach (PlotCube cube in cubes) { + UgcMapCube model = cube; + model.UgcMapId = plotInfo.Id; + if (model.Id >= Constant.FurnishingBaseId) { + model.Id = 0; // This needs to be auto-generated. + results.Add(model); + Context.UgcMapCube.Add(model); + } else { + updated.Add(model.Id); + results.Add(model); + Context.UgcMapCube.Update(model); + } + } + foreach (UgcMapCube cube in Context.UgcMapCube.Where(cube => cube.UgcMapId == plotInfo.Id)) { + if (!updated.Contains(cube.Id)) { + Context.UgcMapCube.Remove(cube); + } + } + + if (!Context.TrySaveChanges()) { + return null; + } + + PlotCube[] plotCubes = results + .Select(ToPlotCube) + .Where(cube => cube != null) + .ToArray()!; + foreach (PlotCube cube in plotCubes) { + if (cube.Interact?.Metadata.Nurturing is null) continue; + + cube.Interact!.Nurturing = GetNurturing(plotInfo.OwnerId, cube.ItemId, cube.Interact.Metadata.Nurturing); + } + + return plotCubes; + } + + public bool InitUgcMap(IEnumerable maps) { + // If there are entries, we assume it's already initialized. + if (Context.UgcMap.Any()) { + return true; + } + + foreach (UgcMapMetadata map in maps) { + if (map.Id == Constant.DefaultHomeMapId) { + continue; + } + + foreach (UgcMapGroup group in map.Plots.Values) { + Context.UgcMap.Add(new UgcMap { + MapId = map.Id, + Number = group.Number, + ApartmentNumber = group.ApartmentNumber, + }); + } + } + + return Context.TrySaveChanges(); + } + + private Plot? ToPlot(UgcMap? ugcMap) { + if (ugcMap == null || !game.mapMetadata.TryGetUgc(ugcMap.MapId, out UgcMapMetadata? metadata)) { + return null; + } + + if (!metadata.Plots.TryGetValue(ugcMap.Number, out UgcMapGroup? group)) { + return null; + } + + if (!ValidateAndFixHomeExpiryTime(ugcMap)) { + return null; + } + + var plot = new Plot(group) { + Id = ugcMap.Id, + OwnerId = ugcMap.OwnerId, + MapId = ugcMap.MapId, + Number = ugcMap.Number, + ApartmentNumber = 0, + ExpiryTime = ugcMap.ExpiryTime.ToUnixTimeSeconds(), + }; + + if (ugcMap.Cubes == null) return plot; + + foreach (UgcMapCube cube in ugcMap.Cubes) { + PlotCube? plotCube = ToPlotCube(cube); + if (plotCube == null) { + continue; + } + + if (plotCube.Interact?.Metadata.Nurturing is not null) { + plotCube.Interact.Nurturing = GetNurturing(ugcMap.OwnerId, cube.Interact!.ObjectCode, plotCube.Interact.Metadata.Nurturing); + } + + plot.Cubes.Add(plotCube.Position, plotCube); + } + + return plot; + } + + private PlotInfo? ToPlotInfo(UgcMap? ugcMap) { + if (ugcMap == null || !game.mapMetadata.TryGetUgc(ugcMap.MapId, out UgcMapMetadata? metadata)) { + return null; + } + + if (!metadata.Plots.TryGetValue(ugcMap.Number, out UgcMapGroup? group)) { + return null; + } + + if (!ValidateAndFixHomeExpiryTime(ugcMap)) { + return null; + } + + return new PlotInfo(group) { + Id = ugcMap.Id, + OwnerId = ugcMap.OwnerId, + MapId = ugcMap.MapId, + Number = ugcMap.Number, + Name = ugcMap.Name, + ApartmentNumber = 0, + ExpiryTime = ugcMap.ExpiryTime.ToUnixTimeSeconds(), + }; + } + + private HomeLayout? ToHomeLayout(Model.HomeLayout? model) { + if (model == null) { + return null; + } + + List cubes = model.Cubes.Select(ToPlotCube) + .Where(cube => cube != null) + .ToList()!; + + return new HomeLayout(model.Uid, model.Id, model.Name, model.Area, model.Height, model.Timestamp, cubes); + } + + // Converts model to interact cube if possible, otherwise returns null. + private InteractCube? ToInteractCube(Model.InteractCube? model) { + if (model == null) { + return null; + } + + return game.functionCubeMetadata.TryGet(model.ObjectCode, out FunctionCubeMetadata? metadata) ? model.Convert(metadata, model.NoticeSettings, model.PortalSettings) : null; + } + + private PlotCube? ToPlotCube(HomeLayoutCube? model) { + if (model == null) { + return null; + } + + if (!game.itemMetadata.TryGet(model.ItemId, out ItemMetadata? itemMetadata)) { + return null; + } + + return new PlotCube(itemMetadata, model.Id, model.Template) { + Position = new Vector3B(model.X, model.Y, model.Z), + Rotation = model.Rotation, + Interact = ToInteractCube(model.Interact), + Type = PlotCube.CubeType.Construction, + }; + } + + private PlotCube? ToPlotCube(UgcMapCube? model) { + if (model == null) { + return null; + } + + if (!game.itemMetadata.TryGet(model.ItemId, out ItemMetadata? itemMetadata)) { + return null; + } + + return new PlotCube(itemMetadata, model.Id, model.Template) { + Position = new Vector3B(model.X, model.Y, model.Z), + Rotation = model.Rotation, + Interact = ToInteractCube(model.Interact), + Type = PlotCube.CubeType.Construction, + }; + } + + private bool ValidateAndFixHomeExpiryTime(UgcMap ugcMap) { + if (!string.IsNullOrEmpty(ugcMap.Name) && ugcMap.MapId is Constant.DefaultHomeMapId && ugcMap.ExpiryTime != Home.HomeExpiryTime) { + Logger.LogError("Plot {Id} for {OwnerId} is initialized but it's ExpiryTime is not set to Home.HomeExpiryTime. Why?", ugcMap.Id, ugcMap.OwnerId); + + ugcMap.ExpiryTime = Home.HomeExpiryTime; + Context.UgcMap.Update(ugcMap); + return Context.TrySaveChanges(); + } + return true; + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Market.cs b/Maple2.Database/Storage/Game/GameStorage.Market.cs index 7597b8e52..006421a2c 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Market.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Market.cs @@ -1,317 +1,317 @@ -using Maple2.Database.Extensions; -using Maple2.Database.Model; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Microsoft.EntityFrameworkCore; -using BlackMarketListing = Maple2.Model.Game.BlackMarketListing; -using Item = Maple2.Model.Game.Item; -using MesoListing = Maple2.Model.Game.MesoListing; -using PremiumMarketItem = Maple2.Model.Game.PremiumMarketItem; -using SoldUgcMarketItem = Maple2.Model.Game.SoldUgcMarketItem; -using UgcMarketItem = Maple2.Model.Game.UgcMarketItem; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - // TODO: Should this filter out your own listings? - public ICollection SearchMesoListings(int pageSize, long minAmount = 0, long maxAmount = long.MaxValue) { - return Context.MesoMarket.Where(listing => listing.ExpiryTime > DateTime.Now) - .Where(listing => listing.Amount >= minAmount) - .Where(listing => listing.Amount <= maxAmount) - .OrderBy(listing => listing.Price) - .Take(pageSize) - .AsEnumerable() - .Select(listing => listing) - .ToList(); - } - - public ICollection GetMyMesoListingsByAccountId(long accountId) { - return Context.MesoMarket.Where(listing => listing.AccountId == accountId) - .AsEnumerable() - .Select(listing => listing) - .ToList(); - } - - public ICollection GetMyMesoListingsByCharacterId(long characterId) { - return Context.MesoMarket.Where(listing => listing.CharacterId == characterId) - .AsEnumerable() - .Select(listing => listing) - .ToList(); - } - - public MesoListing? GetMesoListing(long listingId) { - return Context.MesoMarket.Find(listingId); - } - - public MesoListing? CreateMesoListing(MesoListing listing) { - Model.MesoListing model = listing; - Context.MesoMarket.Add(model); - - return Context.TrySaveChanges() ? model : null; - } - - public bool DeleteMesoListing(long listingId, bool sold = false) { - Model.MesoListing? listing = Context.MesoMarket.Find(listingId); - if (listing == null) { - return false; - } - - if (sold) { - SoldMesoListing soldListing = listing; - Context.MesoMarket.Remove(listing); - Context.MesoMarketSold.Add(soldListing); - return Context.TrySaveChanges(); - } - - Context.MesoMarket.Remove(listing); - return Context.TrySaveChanges(); - } - - public IDictionary GetUgcListingsByAccountId(long accountId) { - return Context.UgcMarketItem.Where(listing => listing.AccountId == accountId) - .AsEnumerable() - .Select(ToMarketEntry) - .Where(x => x is not null) - .ToDictionary(entry => entry!.Id, entry => entry!); - } - - /// - /// Get active UGC listings by character Id - /// - public IList GetUgcListingsByCharacterId(long characterId) { - return Context.UgcMarketItem.Where(listing => listing.CharacterId == characterId && listing.ListingEndTime > DateTime.Now) - .AsEnumerable() - .Select(ToMarketEntry) - .ToList()!; - } - - public IDictionary GetMySoldUgcListings(long accountId) { - return Context.SoldUgcMarketItem.Where(listing => listing.AccountId == accountId) - .AsEnumerable() - .Select(listing => listing) - .ToDictionary(entry => entry.Id, entry => entry); - } - - public UgcMarketItem? CreateUgcMarketItem(UgcMarketItem item) { - Model.UgcMarketItem model = item; - Context.UgcMarketItem.Add(model); - - return Context.TrySaveChanges() ? ToMarketEntry(model) : null; - } - - public bool SaveUgcMarketItems(ICollection items) { - foreach (UgcMarketItem item in items) { - Model.UgcMarketItem model = item; - Context.UgcMarketItem.Update(model); - } - - return Context.TrySaveChanges(); - } - - public bool SaveUgcMarketItem(UgcMarketItem item) { - Model.UgcMarketItem model = item; - Context.UgcMarketItem.Update(model); - return Context.TrySaveChanges(); - } - - public bool DeleteUgcMarketItem(long id) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - Model.UgcMarketItem? listing = Context.UgcMarketItem.Find(id); - if (listing == null) { - return false; - } - - Context.UgcMarketItem.Remove(listing); - return SaveChanges(); - } - - public SoldUgcMarketItem? CreateSoldUgcMarketItem(SoldUgcMarketItem item) { - Model.SoldUgcMarketItem model = item; - Context.SoldUgcMarketItem.Add(model); - - return Context.TrySaveChanges() ? model : null; - } - - public bool SaveSoldUgcMarketItems(ICollection items) { - foreach (SoldUgcMarketItem item in items) { - Model.SoldUgcMarketItem model = item; - Context.SoldUgcMarketItem.Update(model); - } - - return Context.TrySaveChanges(); - } - - public bool DeleteSoldUgcMarketItem(long id) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - Model.SoldUgcMarketItem? item = Context.SoldUgcMarketItem.Find(id); - if (item == null) { - return false; - } - - Context.SoldUgcMarketItem.Remove(item); - return SaveChanges(); - } - - public ICollection GetUgcMarketItems(params int[] tabIds) { - if (tabIds.Length == 0) { - return Context.UgcMarketItem - .Where(item => item.ListingEndTime > DateTime.Now) - .AsEnumerable() - .Select(ToMarketEntry) - .ToList()!; - } - - return Context.UgcMarketItem - .Where(item => item.ListingEndTime > DateTime.Now && tabIds.Contains(item.TabId)) - .AsEnumerable() - .Select(ToMarketEntry) - .ToList()!; - } - - public ICollection GetUgcMarketPromotedItems() { - ICollection items = Context.UgcMarketItem - .Where(item => item.PromotionEndTime > DateTime.Now) - .OrderBy(item => EF.Functions.Random()) - .Take(12) - .AsEnumerable() - .Select(ToMarketEntry) - .ToList()!; - - foreach (UgcMarketItem item in items) { - item.Category = UgcMarketHomeCategory.Promoted; - } - return items; - } - - public ICollection GetUgcMarketNewItems() { - ICollection items = Context.UgcMarketItem - .Where(item => item.ListingEndTime > DateTime.Now) - .OrderByDescending(item => item.CreationTime) - .Take(6) - .AsEnumerable() - .Select(ToMarketEntry) - .ToList()!; - - - foreach (UgcMarketItem item in items) { - item.Category = UgcMarketHomeCategory.New; - } - return items; - } - - public UgcMarketItem? GetUgcMarketItem(long id) { - Model.UgcMarketItem? item = Context.UgcMarketItem.Find(id); - return ToMarketEntry(item); - } - - private UgcMarketItem? ToMarketEntry(Model.UgcMarketItem? model) { - if (model == null) { - return null; - } - - return game.itemMetadata.TryGet(model.ItemId, out ItemMetadata? metadata) ? model.Convert(metadata) : null; - } - - public bool CreateSoldMeretMarketItem(PremiumMarketItem item, long characterId) { - SoldMeretMarketItem model = item; - model.CharacterId = characterId; - Context.SoldMeretMarketItem.Add(model); - return Context.TrySaveChanges(); - } - - public BlackMarketListing? CreateBlackMarketingListing(BlackMarketListing listing) { - Model.BlackMarketListing model = listing; - model.Id = 0; - Context.BlackMarketListing.Add(model); - if (!SaveChanges()) { - return null; - } - - BlackMarketListing? created = ToBlackMarketingListing(model); - if (created == null) { - return null; - } - SaveItems(created.Id, created.Item); - return Context.TrySaveChanges() ? created : null; - } - - public IEnumerable GetBlackMarketListings(long characterId) { - Model.BlackMarketListing[] models = Context.BlackMarketListing.Where(listing => listing.CharacterId == characterId) - .AsEnumerable() - .ToArray(); - - - foreach (Model.BlackMarketListing model in models) { - BlackMarketListing? listing = ToBlackMarketingListing(model); - if (listing != null) { - yield return listing; - } - } - } - - public BlackMarketListing? GetBlackMarketListing(long listingId) { - Model.BlackMarketListing? model = Context.BlackMarketListing.Find(listingId); - return ToBlackMarketingListing(model); - } - - public IEnumerable GetBlackMarketListings(params long[] listingIds) { - Model.BlackMarketListing[] models = Context.BlackMarketListing.Where(listing => listingIds.Contains(listing.Id)) - .AsEnumerable() - .ToArray(); - - foreach (Model.BlackMarketListing model in models) { - BlackMarketListing? listing = ToBlackMarketingListing(model); - if (listing != null) { - yield return listing; - } - } - } - - public IEnumerable GetAllBlackMarketListings() { - Model.BlackMarketListing[] models = Context.BlackMarketListing - .AsEnumerable() - .ToArray(); - - foreach (Model.BlackMarketListing model in models) { - BlackMarketListing? listing = ToBlackMarketingListing(model); - if (listing != null) { - yield return listing; - } - } - } - - public bool DeleteBlackMarketListing(long listingId) { - Model.BlackMarketListing? listing = Context.BlackMarketListing.Find(listingId); - if (listing == null) { - return false; - } - - Context.BlackMarketListing.Remove(listing); - return Context.TrySaveChanges(); - } - - public bool SaveBlackMarketListing(BlackMarketListing listing) { - Model.BlackMarketListing model = listing; - - Context.BlackMarketListing.Update(model); - return Context.TrySaveChanges(); - } - - private BlackMarketListing? ToBlackMarketingListing(Model.BlackMarketListing? model) { - if (model == null) { - return null; - } - - Model.Item? itemModel = Context.Item.Find(model.ItemUid); - if (itemModel == null) { - return null; - } - - Item? item = ToItem(itemModel); - return item == null ? null : model.Convert(item); - } - } -} +using Maple2.Database.Extensions; +using Maple2.Database.Model; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Microsoft.EntityFrameworkCore; +using BlackMarketListing = Maple2.Model.Game.BlackMarketListing; +using Item = Maple2.Model.Game.Item; +using MesoListing = Maple2.Model.Game.MesoListing; +using PremiumMarketItem = Maple2.Model.Game.PremiumMarketItem; +using SoldUgcMarketItem = Maple2.Model.Game.SoldUgcMarketItem; +using UgcMarketItem = Maple2.Model.Game.UgcMarketItem; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + // TODO: Should this filter out your own listings? + public ICollection SearchMesoListings(int pageSize, long minAmount = 0, long maxAmount = long.MaxValue) { + return Context.MesoMarket.Where(listing => listing.ExpiryTime > DateTime.Now) + .Where(listing => listing.Amount >= minAmount) + .Where(listing => listing.Amount <= maxAmount) + .OrderBy(listing => listing.Price) + .Take(pageSize) + .AsEnumerable() + .Select(listing => listing) + .ToList(); + } + + public ICollection GetMyMesoListingsByAccountId(long accountId) { + return Context.MesoMarket.Where(listing => listing.AccountId == accountId) + .AsEnumerable() + .Select(listing => listing) + .ToList(); + } + + public ICollection GetMyMesoListingsByCharacterId(long characterId) { + return Context.MesoMarket.Where(listing => listing.CharacterId == characterId) + .AsEnumerable() + .Select(listing => listing) + .ToList(); + } + + public MesoListing? GetMesoListing(long listingId) { + return Context.MesoMarket.Find(listingId); + } + + public MesoListing? CreateMesoListing(MesoListing listing) { + Model.MesoListing model = listing; + Context.MesoMarket.Add(model); + + return Context.TrySaveChanges() ? model : null; + } + + public bool DeleteMesoListing(long listingId, bool sold = false) { + Model.MesoListing? listing = Context.MesoMarket.Find(listingId); + if (listing == null) { + return false; + } + + if (sold) { + SoldMesoListing soldListing = listing; + Context.MesoMarket.Remove(listing); + Context.MesoMarketSold.Add(soldListing); + return Context.TrySaveChanges(); + } + + Context.MesoMarket.Remove(listing); + return Context.TrySaveChanges(); + } + + public IDictionary GetUgcListingsByAccountId(long accountId) { + return Context.UgcMarketItem.Where(listing => listing.AccountId == accountId) + .AsEnumerable() + .Select(ToMarketEntry) + .Where(x => x is not null) + .ToDictionary(entry => entry!.Id, entry => entry!); + } + + /// + /// Get active UGC listings by character Id + /// + public IList GetUgcListingsByCharacterId(long characterId) { + return Context.UgcMarketItem.Where(listing => listing.CharacterId == characterId && listing.ListingEndTime > DateTime.Now) + .AsEnumerable() + .Select(ToMarketEntry) + .ToList()!; + } + + public IDictionary GetMySoldUgcListings(long accountId) { + return Context.SoldUgcMarketItem.Where(listing => listing.AccountId == accountId) + .AsEnumerable() + .Select(listing => listing) + .ToDictionary(entry => entry.Id, entry => entry); + } + + public UgcMarketItem? CreateUgcMarketItem(UgcMarketItem item) { + Model.UgcMarketItem model = item; + Context.UgcMarketItem.Add(model); + + return Context.TrySaveChanges() ? ToMarketEntry(model) : null; + } + + public bool SaveUgcMarketItems(ICollection items) { + foreach (UgcMarketItem item in items) { + Model.UgcMarketItem model = item; + Context.UgcMarketItem.Update(model); + } + + return Context.TrySaveChanges(); + } + + public bool SaveUgcMarketItem(UgcMarketItem item) { + Model.UgcMarketItem model = item; + Context.UgcMarketItem.Update(model); + return Context.TrySaveChanges(); + } + + public bool DeleteUgcMarketItem(long id) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + Model.UgcMarketItem? listing = Context.UgcMarketItem.Find(id); + if (listing == null) { + return false; + } + + Context.UgcMarketItem.Remove(listing); + return SaveChanges(); + } + + public SoldUgcMarketItem? CreateSoldUgcMarketItem(SoldUgcMarketItem item) { + Model.SoldUgcMarketItem model = item; + Context.SoldUgcMarketItem.Add(model); + + return Context.TrySaveChanges() ? model : null; + } + + public bool SaveSoldUgcMarketItems(ICollection items) { + foreach (SoldUgcMarketItem item in items) { + Model.SoldUgcMarketItem model = item; + Context.SoldUgcMarketItem.Update(model); + } + + return Context.TrySaveChanges(); + } + + public bool DeleteSoldUgcMarketItem(long id) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + Model.SoldUgcMarketItem? item = Context.SoldUgcMarketItem.Find(id); + if (item == null) { + return false; + } + + Context.SoldUgcMarketItem.Remove(item); + return SaveChanges(); + } + + public ICollection GetUgcMarketItems(params int[] tabIds) { + if (tabIds.Length == 0) { + return Context.UgcMarketItem + .Where(item => item.ListingEndTime > DateTime.Now) + .AsEnumerable() + .Select(ToMarketEntry) + .ToList()!; + } + + return Context.UgcMarketItem + .Where(item => item.ListingEndTime > DateTime.Now && tabIds.Contains(item.TabId)) + .AsEnumerable() + .Select(ToMarketEntry) + .ToList()!; + } + + public ICollection GetUgcMarketPromotedItems() { + ICollection items = Context.UgcMarketItem + .Where(item => item.PromotionEndTime > DateTime.Now) + .OrderBy(item => EF.Functions.Random()) + .Take(12) + .AsEnumerable() + .Select(ToMarketEntry) + .ToList()!; + + foreach (UgcMarketItem item in items) { + item.Category = UgcMarketHomeCategory.Promoted; + } + return items; + } + + public ICollection GetUgcMarketNewItems() { + ICollection items = Context.UgcMarketItem + .Where(item => item.ListingEndTime > DateTime.Now) + .OrderByDescending(item => item.CreationTime) + .Take(6) + .AsEnumerable() + .Select(ToMarketEntry) + .ToList()!; + + + foreach (UgcMarketItem item in items) { + item.Category = UgcMarketHomeCategory.New; + } + return items; + } + + public UgcMarketItem? GetUgcMarketItem(long id) { + Model.UgcMarketItem? item = Context.UgcMarketItem.Find(id); + return ToMarketEntry(item); + } + + private UgcMarketItem? ToMarketEntry(Model.UgcMarketItem? model) { + if (model == null) { + return null; + } + + return game.itemMetadata.TryGet(model.ItemId, out ItemMetadata? metadata) ? model.Convert(metadata) : null; + } + + public bool CreateSoldMeretMarketItem(PremiumMarketItem item, long characterId) { + SoldMeretMarketItem model = item; + model.CharacterId = characterId; + Context.SoldMeretMarketItem.Add(model); + return Context.TrySaveChanges(); + } + + public BlackMarketListing? CreateBlackMarketingListing(BlackMarketListing listing) { + Model.BlackMarketListing model = listing; + model.Id = 0; + Context.BlackMarketListing.Add(model); + if (!SaveChanges()) { + return null; + } + + BlackMarketListing? created = ToBlackMarketingListing(model); + if (created == null) { + return null; + } + SaveItems(created.Id, created.Item); + return Context.TrySaveChanges() ? created : null; + } + + public IEnumerable GetBlackMarketListings(long characterId) { + Model.BlackMarketListing[] models = Context.BlackMarketListing.Where(listing => listing.CharacterId == characterId) + .AsEnumerable() + .ToArray(); + + + foreach (Model.BlackMarketListing model in models) { + BlackMarketListing? listing = ToBlackMarketingListing(model); + if (listing != null) { + yield return listing; + } + } + } + + public BlackMarketListing? GetBlackMarketListing(long listingId) { + Model.BlackMarketListing? model = Context.BlackMarketListing.Find(listingId); + return ToBlackMarketingListing(model); + } + + public IEnumerable GetBlackMarketListings(params long[] listingIds) { + Model.BlackMarketListing[] models = Context.BlackMarketListing.Where(listing => listingIds.Contains(listing.Id)) + .AsEnumerable() + .ToArray(); + + foreach (Model.BlackMarketListing model in models) { + BlackMarketListing? listing = ToBlackMarketingListing(model); + if (listing != null) { + yield return listing; + } + } + } + + public IEnumerable GetAllBlackMarketListings() { + Model.BlackMarketListing[] models = Context.BlackMarketListing + .AsEnumerable() + .ToArray(); + + foreach (Model.BlackMarketListing model in models) { + BlackMarketListing? listing = ToBlackMarketingListing(model); + if (listing != null) { + yield return listing; + } + } + } + + public bool DeleteBlackMarketListing(long listingId) { + Model.BlackMarketListing? listing = Context.BlackMarketListing.Find(listingId); + if (listing == null) { + return false; + } + + Context.BlackMarketListing.Remove(listing); + return Context.TrySaveChanges(); + } + + public bool SaveBlackMarketListing(BlackMarketListing listing) { + Model.BlackMarketListing model = listing; + + Context.BlackMarketListing.Update(model); + return Context.TrySaveChanges(); + } + + private BlackMarketListing? ToBlackMarketingListing(Model.BlackMarketListing? model) { + if (model == null) { + return null; + } + + Model.Item? itemModel = Context.Item.Find(model.ItemUid); + if (itemModel == null) { + return null; + } + + Item? item = ToItem(itemModel); + return item == null ? null : model.Convert(item); + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Medal.cs b/Maple2.Database/Storage/Game/GameStorage.Medal.cs index 53a4b07e5..76f1ebbd0 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Medal.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Medal.cs @@ -1,46 +1,46 @@ -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Maple2.Model.Game; -using Maple2.Tools.Extensions; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public List GetMedals(long ownerId) { - return Context.Medal.Where(medal => medal.OwnerId == ownerId) - .AsEnumerable() - .Select(ToMedal) - .WhereNotNull() - .ToList(); - } - - public Medal? CreateMedal(long ownerId, Medal medal) { - Model.Medal model = medal; - model.OwnerId = ownerId; - Context.Medal.Add(model); - - return Context.TrySaveChanges() ? ToMedal(model) : null; - } - - public bool SaveMedals(long ownerId, params Medal[] medals) { - var models = new Model.Medal[medals.Length]; - for (int i = 0; i < medals.Length; i++) { - models[i] = medals[i]; - models[i].OwnerId = ownerId; - Context.Medal.Update(models[i]); - } - - return Context.TrySaveChanges(); - } - - // Converts model to medal if possible, otherwise returns null. - private Medal? ToMedal(Model.Medal? model) { - if (model == null) { - return null; - } - - return game.tableMetadata.SurvivalSkinInfoTable.Entries.TryGetValue(model.Id, out MedalType medalType) ? model.Convert(medalType) : null; - } - } -} +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Maple2.Model.Game; +using Maple2.Tools.Extensions; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public List GetMedals(long ownerId) { + return Context.Medal.Where(medal => medal.OwnerId == ownerId) + .AsEnumerable() + .Select(ToMedal) + .WhereNotNull() + .ToList(); + } + + public Medal? CreateMedal(long ownerId, Medal medal) { + Model.Medal model = medal; + model.OwnerId = ownerId; + Context.Medal.Add(model); + + return Context.TrySaveChanges() ? ToMedal(model) : null; + } + + public bool SaveMedals(long ownerId, params Medal[] medals) { + var models = new Model.Medal[medals.Length]; + for (int i = 0; i < medals.Length; i++) { + models[i] = medals[i]; + models[i].OwnerId = ownerId; + Context.Medal.Update(models[i]); + } + + return Context.TrySaveChanges(); + } + + // Converts model to medal if possible, otherwise returns null. + private Medal? ToMedal(Model.Medal? model) { + if (model == null) { + return null; + } + + return game.tableMetadata.SurvivalSkinInfoTable.Entries.TryGetValue(model.Id, out MedalType medalType) ? model.Convert(medalType) : null; + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Nurturing.cs b/Maple2.Database/Storage/Game/GameStorage.Nurturing.cs index 0637a2d52..6e4922431 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Nurturing.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Nurturing.cs @@ -1,56 +1,56 @@ -using Maple2.Database.Extensions; -using Maple2.Model.Game; -using Maple2.Model.Metadata; -using Nurturing = Maple2.Database.Model.Nurturing; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public Maple2.Model.Game.Nurturing? GetNurturing(long characterId, int interactObjectCode, FunctionCubeMetadata.NurturingData metadata) { - Nurturing? result = Context.Nurturing.Find(characterId, interactObjectCode); - return result is null ? null : new Maple2.Model.Game.Nurturing(result.Exp, result.ClaimedGiftForStage, result.PlayedBy, result.CreationTime, result.LastFeedTime, metadata); - } - - public Maple2.Model.Game.Nurturing? CreateNurturing(long accountId, FunctionCubeMetadata.NurturingData metadata, int interactId) { - var nurturing = new Nurturing { - AccountId = accountId, - InteractId = interactId, - Exp = 0, - ClaimedGiftForStage = 1, - CreationTime = DateTime.Now, - LastFeedTime = DateTime.MinValue, - PlayedBy = [], - }; - Context.Nurturing.Add(nurturing); - if (!Context.TrySaveChanges()) { - return null; - } - - return new Maple2.Model.Game.Nurturing(nurturing.Exp, nurturing.ClaimedGiftForStage, nurturing.PlayedBy, nurturing.CreationTime, nurturing.LastFeedTime, metadata); - } - - public void UpdateNurturing(long accountId, InteractCube cube) { - Nurturing? result = Context.Nurturing.Find(accountId, cube.Metadata.Id); - if (result is null) { - return; - } - Maple2.Model.Game.Nurturing? interactNurturing = cube.Nurturing; - if (interactNurturing is null) { - return; - } - result.Exp = interactNurturing.Exp; - result.ClaimedGiftForStage = interactNurturing.ClaimedGiftForStage; - result.PlayedBy = interactNurturing.PlayedBy.ToArray(); - result.LastFeedTime = interactNurturing.LastFeedTime.DateTime; - - Context.Nurturing.Update(result); - Context.TrySaveChanges(); - } - - // Count the number of nurturing items for the given account ID in petBy - public int CountNurturingForAccount(int itemId, long accountId) { - return Context.Nurturing.AsEnumerable().Count(x => x.InteractId == itemId && x.PlayedBy.Contains(accountId)); - } - } -} +using Maple2.Database.Extensions; +using Maple2.Model.Game; +using Maple2.Model.Metadata; +using Nurturing = Maple2.Database.Model.Nurturing; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public Maple2.Model.Game.Nurturing? GetNurturing(long characterId, int interactObjectCode, FunctionCubeMetadata.NurturingData metadata) { + Nurturing? result = Context.Nurturing.Find(characterId, interactObjectCode); + return result is null ? null : new Maple2.Model.Game.Nurturing(result.Exp, result.ClaimedGiftForStage, result.PlayedBy, result.CreationTime, result.LastFeedTime, metadata); + } + + public Maple2.Model.Game.Nurturing? CreateNurturing(long accountId, FunctionCubeMetadata.NurturingData metadata, int interactId) { + var nurturing = new Nurturing { + AccountId = accountId, + InteractId = interactId, + Exp = 0, + ClaimedGiftForStage = 1, + CreationTime = DateTime.Now, + LastFeedTime = DateTime.MinValue, + PlayedBy = [], + }; + Context.Nurturing.Add(nurturing); + if (!Context.TrySaveChanges()) { + return null; + } + + return new Maple2.Model.Game.Nurturing(nurturing.Exp, nurturing.ClaimedGiftForStage, nurturing.PlayedBy, nurturing.CreationTime, nurturing.LastFeedTime, metadata); + } + + public void UpdateNurturing(long accountId, InteractCube cube) { + Nurturing? result = Context.Nurturing.Find(accountId, cube.Metadata.Id); + if (result is null) { + return; + } + Maple2.Model.Game.Nurturing? interactNurturing = cube.Nurturing; + if (interactNurturing is null) { + return; + } + result.Exp = interactNurturing.Exp; + result.ClaimedGiftForStage = interactNurturing.ClaimedGiftForStage; + result.PlayedBy = interactNurturing.PlayedBy.ToArray(); + result.LastFeedTime = interactNurturing.LastFeedTime.DateTime; + + Context.Nurturing.Update(result); + Context.TrySaveChanges(); + } + + // Count the number of nurturing items for the given account ID in petBy + public int CountNurturingForAccount(int itemId, long accountId) { + return Context.Nurturing.AsEnumerable().Count(x => x.InteractId == itemId && x.PlayedBy.Contains(accountId)); + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Quest.cs b/Maple2.Database/Storage/Game/GameStorage.Quest.cs index 757600a35..6c79c9754 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Quest.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Quest.cs @@ -1,58 +1,58 @@ -using Maple2.Database.Extensions; -using Maple2.Model.Game; -using Maple2.Model.Metadata; -using Microsoft.EntityFrameworkCore; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public Quest? CreateQuest(long ownerId, Quest quest) { - Model.Quest model = quest; - model.OwnerId = ownerId; - Context.Quest.Add(model); - - return Context.TrySaveChanges() ? ToQuest(model) : null; - } - - public IDictionary GetQuests(long ownerId) { - return Context.Quest.Where(quest => quest.OwnerId == ownerId) - .AsEnumerable() - .Select(ToQuest) - .Where(quest => quest != null) - .ToDictionary(quest => quest!.Id, quest => quest!); - } - - public bool DeleteQuest(long ownerId, int questId) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - Model.Quest? quest = Context.Quest.Find(ownerId, questId); - if (quest == null) { - return false; - } - - Context.Quest.Remove(quest); - return SaveChanges(); - } - - public bool SaveQuests(long ownerId, ICollection quests) { - foreach (Quest quest in quests) { - Model.Quest model = quest; - model.OwnerId = ownerId; - - Context.Quest.Update(model); - } - - return Context.TrySaveChanges(); - } - - // Converts model to quest if possible, otherwise returns null. - private Quest? ToQuest(Model.Quest? model) { - if (model == null) { - return null; - } - - return game.questMetadata.TryGet(model.Id, out QuestMetadata? metadata) ? model.Convert(metadata) : null; - } - } -} +using Maple2.Database.Extensions; +using Maple2.Model.Game; +using Maple2.Model.Metadata; +using Microsoft.EntityFrameworkCore; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public Quest? CreateQuest(long ownerId, Quest quest) { + Model.Quest model = quest; + model.OwnerId = ownerId; + Context.Quest.Add(model); + + return Context.TrySaveChanges() ? ToQuest(model) : null; + } + + public IDictionary GetQuests(long ownerId) { + return Context.Quest.Where(quest => quest.OwnerId == ownerId) + .AsEnumerable() + .Select(ToQuest) + .Where(quest => quest != null) + .ToDictionary(quest => quest!.Id, quest => quest!); + } + + public bool DeleteQuest(long ownerId, int questId) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + Model.Quest? quest = Context.Quest.Find(ownerId, questId); + if (quest == null) { + return false; + } + + Context.Quest.Remove(quest); + return SaveChanges(); + } + + public bool SaveQuests(long ownerId, ICollection quests) { + foreach (Quest quest in quests) { + Model.Quest model = quest; + model.OwnerId = ownerId; + + Context.Quest.Update(model); + } + + return Context.TrySaveChanges(); + } + + // Converts model to quest if possible, otherwise returns null. + private Quest? ToQuest(Model.Quest? model) { + if (model == null) { + return null; + } + + return game.questMetadata.TryGet(model.Id, out QuestMetadata? metadata) ? model.Convert(metadata) : null; + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Report.cs b/Maple2.Database/Storage/Game/GameStorage.Report.cs index bf73e1039..7ed2a213f 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Report.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Report.cs @@ -1,93 +1,93 @@ -using Maple2.Database.Extensions; -using Maple2.Database.Model; -using Maple2.Model.Enum; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public void ReportPlayer(long characterId, string playerName, long reporterCharacterId, string reporterName, string reason, PlayerReportFlag flag) { - Context.PlayerReports.Add(new PlayerReport { - ReporterCharacterId = reporterCharacterId, - ReporterName = reporterName, - CharacterId = characterId, - PlayerName = playerName, - Reason = reason, - Category = ReportCategory.Player, - CreateTime = DateTime.Now, - ReportInfo = new PlayerReportInfo(flag.ToString()), - }); - Context.TrySaveChanges(); - } - - public void ReportChat(long characterId, string playerName, long reporterCharacterId, string reporterName, string reason, string chatMessage, ChatReportFlag flag) { - Context.PlayerReports.Add(new PlayerReport { - ReporterCharacterId = reporterCharacterId, - ReporterName = reporterName, - CharacterId = characterId, - PlayerName = playerName, - Reason = reason, - Category = ReportCategory.Chat, - CreateTime = DateTime.Now, - ReportInfo = new ChatReportInfo(flag.ToString(), chatMessage), - }); - Context.TrySaveChanges(); - } - - public void ReportPoster(long characterId, string playerName, long reporterCharacterId, string reporterName, string reason, int posterId, string templateId, PosterReportFlag flag) { - Context.PlayerReports.Add(new PlayerReport { - ReporterCharacterId = reporterCharacterId, - ReporterName = reporterName, - CharacterId = characterId, - PlayerName = playerName, - Reason = reason, - Category = ReportCategory.Poster, - CreateTime = DateTime.Now, - ReportInfo = new PosterReportInfo(flag.ToString(), posterId, templateId), - }); - Context.TrySaveChanges(); - } - - public void ReportDesignItem(long characterId, string playerName, long reporterCharacterId, string reporterName, string reason, long listingId, DesignItemReportFlag flag) { - Context.PlayerReports.Add(new PlayerReport { - ReporterCharacterId = reporterCharacterId, - ReporterName = reporterName, - CharacterId = characterId, - PlayerName = playerName, - Reason = reason, - Category = ReportCategory.ItemDesign, - CreateTime = DateTime.Now, - ReportInfo = new ItemReportInfo(flag.ToString(), listingId), - }); - Context.TrySaveChanges(); - } - - public void ReportHome(long characterId, string playerName, long reporterCharacterId, string reporterName, string reason, long homeId, int mapId, int plotId, HomeReportFlag flag) { - Context.PlayerReports.Add(new PlayerReport { - ReporterCharacterId = reporterCharacterId, - ReporterName = reporterName, - CharacterId = characterId, - PlayerName = playerName, - Reason = reason, - Category = ReportCategory.Home, - CreateTime = DateTime.Now, - ReportInfo = new HomeReportInfo(flag.ToString(), homeId, mapId, plotId), - }); - Context.TrySaveChanges(); - } - - public void ReportPet(long characterId, string playerName, long reporterCharacterId, string reporterName, string reason, string petName, PetReportFlag flag) { - Context.PlayerReports.Add(new PlayerReport { - ReporterCharacterId = reporterCharacterId, - ReporterName = reporterName, - CharacterId = characterId, - PlayerName = playerName, - Reason = reason, - Category = ReportCategory.Pet, - CreateTime = DateTime.Now, - ReportInfo = new PetReportInfo(flag.ToString(), petName), - }); - Context.TrySaveChanges(); - } - } -} +using Maple2.Database.Extensions; +using Maple2.Database.Model; +using Maple2.Model.Enum; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public void ReportPlayer(long characterId, string playerName, long reporterCharacterId, string reporterName, string reason, PlayerReportFlag flag) { + Context.PlayerReports.Add(new PlayerReport { + ReporterCharacterId = reporterCharacterId, + ReporterName = reporterName, + CharacterId = characterId, + PlayerName = playerName, + Reason = reason, + Category = ReportCategory.Player, + CreateTime = DateTime.Now, + ReportInfo = new PlayerReportInfo(flag.ToString()), + }); + Context.TrySaveChanges(); + } + + public void ReportChat(long characterId, string playerName, long reporterCharacterId, string reporterName, string reason, string chatMessage, ChatReportFlag flag) { + Context.PlayerReports.Add(new PlayerReport { + ReporterCharacterId = reporterCharacterId, + ReporterName = reporterName, + CharacterId = characterId, + PlayerName = playerName, + Reason = reason, + Category = ReportCategory.Chat, + CreateTime = DateTime.Now, + ReportInfo = new ChatReportInfo(flag.ToString(), chatMessage), + }); + Context.TrySaveChanges(); + } + + public void ReportPoster(long characterId, string playerName, long reporterCharacterId, string reporterName, string reason, int posterId, string templateId, PosterReportFlag flag) { + Context.PlayerReports.Add(new PlayerReport { + ReporterCharacterId = reporterCharacterId, + ReporterName = reporterName, + CharacterId = characterId, + PlayerName = playerName, + Reason = reason, + Category = ReportCategory.Poster, + CreateTime = DateTime.Now, + ReportInfo = new PosterReportInfo(flag.ToString(), posterId, templateId), + }); + Context.TrySaveChanges(); + } + + public void ReportDesignItem(long characterId, string playerName, long reporterCharacterId, string reporterName, string reason, long listingId, DesignItemReportFlag flag) { + Context.PlayerReports.Add(new PlayerReport { + ReporterCharacterId = reporterCharacterId, + ReporterName = reporterName, + CharacterId = characterId, + PlayerName = playerName, + Reason = reason, + Category = ReportCategory.ItemDesign, + CreateTime = DateTime.Now, + ReportInfo = new ItemReportInfo(flag.ToString(), listingId), + }); + Context.TrySaveChanges(); + } + + public void ReportHome(long characterId, string playerName, long reporterCharacterId, string reporterName, string reason, long homeId, int mapId, int plotId, HomeReportFlag flag) { + Context.PlayerReports.Add(new PlayerReport { + ReporterCharacterId = reporterCharacterId, + ReporterName = reporterName, + CharacterId = characterId, + PlayerName = playerName, + Reason = reason, + Category = ReportCategory.Home, + CreateTime = DateTime.Now, + ReportInfo = new HomeReportInfo(flag.ToString(), homeId, mapId, plotId), + }); + Context.TrySaveChanges(); + } + + public void ReportPet(long characterId, string playerName, long reporterCharacterId, string reporterName, string reason, string petName, PetReportFlag flag) { + Context.PlayerReports.Add(new PlayerReport { + ReporterCharacterId = reporterCharacterId, + ReporterName = reporterName, + CharacterId = characterId, + PlayerName = playerName, + Reason = reason, + Category = ReportCategory.Pet, + CreateTime = DateTime.Now, + ReportInfo = new PetReportInfo(flag.ToString(), petName), + }); + Context.TrySaveChanges(); + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.ServerInfo.cs b/Maple2.Database/Storage/Game/GameStorage.ServerInfo.cs index ff8f30635..c5c25591b 100644 --- a/Maple2.Database/Storage/Game/GameStorage.ServerInfo.cs +++ b/Maple2.Database/Storage/Game/GameStorage.ServerInfo.cs @@ -1,41 +1,41 @@ -using Maple2.Database.Model; -using Microsoft.EntityFrameworkCore; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public DateTime GetLastDailyReset() { - ServerInfo? dailyReset = Context.ServerInfo.Find("DailyReset"); - return dailyReset?.LastModified ?? CreateDailyReset(); - } - - private DateTime CreateDailyReset() { - var model = new ServerInfo { - Key = "DailyReset", - }; - Context.ServerInfo.Add(model); - Context.SaveChanges(); // Exception if failed. - - return model.LastModified; - } - - public void DailyReset() { - lock (Context) { - ServerInfo serverInfo = Context.ServerInfo.Find("DailyReset")!; - serverInfo.LastModified = DateTime.Now; - Context.Update(serverInfo); - Context.SaveChanges(); - - Context.Database.ExecuteSqlRaw("UPDATE `account` SET `PrestigeExp` = `PrestigeCurrentExp`"); - Context.Database.ExecuteSqlRaw("UPDATE `account` SET `PrestigeLevelsGained` = DEFAULT"); - Context.Database.ExecuteSqlRaw("UPDATE `account` SET `PremiumRewardsClaimed` = DEFAULT"); - Context.Database.ExecuteSqlRaw("UPDATE `character-config` SET `GatheringCounts` = DEFAULT"); - Context.Database.ExecuteSqlRaw("UPDATE `character-config` SET `InstantRevivalCount` = 0"); - Context.Database.ExecuteSqlRaw("UPDATE `nurturing` SET `PlayedBy` = '[]'"); - Context.Database.ExecuteSqlRaw("UPDATE `home` SET `DecorationRewardTimestamp` = 0"); - // TODO: Death counter - } - } - } -} +using Maple2.Database.Model; +using Microsoft.EntityFrameworkCore; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public DateTime GetLastDailyReset() { + ServerInfo? dailyReset = Context.ServerInfo.Find("DailyReset"); + return dailyReset?.LastModified ?? CreateDailyReset(); + } + + private DateTime CreateDailyReset() { + var model = new ServerInfo { + Key = "DailyReset", + }; + Context.ServerInfo.Add(model); + Context.SaveChanges(); // Exception if failed. + + return model.LastModified; + } + + public void DailyReset() { + lock (Context) { + ServerInfo serverInfo = Context.ServerInfo.Find("DailyReset")!; + serverInfo.LastModified = DateTime.Now; + Context.Update(serverInfo); + Context.SaveChanges(); + + Context.Database.ExecuteSqlRaw("UPDATE `account` SET `PrestigeExp` = `PrestigeCurrentExp`"); + Context.Database.ExecuteSqlRaw("UPDATE `account` SET `PrestigeLevelsGained` = DEFAULT"); + Context.Database.ExecuteSqlRaw("UPDATE `account` SET `PremiumRewardsClaimed` = DEFAULT"); + Context.Database.ExecuteSqlRaw("UPDATE `character-config` SET `GatheringCounts` = DEFAULT"); + Context.Database.ExecuteSqlRaw("UPDATE `character-config` SET `InstantRevivalCount` = 0"); + Context.Database.ExecuteSqlRaw("UPDATE `nurturing` SET `PlayedBy` = '[]'"); + Context.Database.ExecuteSqlRaw("UPDATE `home` SET `DecorationRewardTimestamp` = 0"); + // TODO: Death counter + } + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Shop.cs b/Maple2.Database/Storage/Game/GameStorage.Shop.cs index f5e6d1b87..19d709d19 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Shop.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Shop.cs @@ -1,103 +1,103 @@ -using Maple2.Database.Extensions; -using Maple2.Model.Game; -using Maple2.Model.Game.Shop; -using Maple2.Model.Metadata; -using Microsoft.EntityFrameworkCore; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public CharacterShopData? CreateCharacterShopData(long ownerId, CharacterShopData shop) { - Model.Shop.CharacterShopData model = shop; - model.OwnerId = ownerId; - Context.CharacterShopData.Add(model); - - return SaveChanges() ? model : null; - } - - public IDictionary GetCharacterShopData(long ownerId) { - return Context.CharacterShopData.Where(data => data.OwnerId == ownerId) - .Select(data => data) - .ToDictionary(data => data.ShopId, data => data); - } - - public CharacterShopData? GetCharacterShopData(long ownerId, int shopId) { - return Context.CharacterShopData.Find(shopId, ownerId); - } - - public bool SaveCharacterShopData(long ownerId, ICollection shopDatas) { - foreach (CharacterShopData data in shopDatas) { - Model.Shop.CharacterShopData model = data; - model.OwnerId = ownerId; - - Context.CharacterShopData.Update(model); - } - - return Context.TrySaveChanges(); - } - - public bool DeleteCharacterShopData(long ownerId, int shopId) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - Model.Shop.CharacterShopData? data = Context.CharacterShopData.Find(shopId, ownerId); - if (data == null) { - return false; - } - - Context.CharacterShopData.Remove(data); - return SaveChanges(); - } - - public CharacterShopItemData? CreateCharacterShopItemData(long ownerId, CharacterShopItemData item) { - Model.Shop.CharacterShopItemData model = item; - model.OwnerId = ownerId; - Context.CharacterShopItemData.Add(model); - - return SaveChanges() ? ToShopItemData(model) : null; - } - - public ICollection GetCharacterShopItemData(long ownerId) { - return Context.CharacterShopItemData.Where(data => data.OwnerId == ownerId) - .AsEnumerable() - .Select(ToShopItemData) - .ToList()!; - } - - public bool SaveCharacterShopItemData(long ownerId, ICollection itemDatas) { - foreach (CharacterShopItemData data in itemDatas) { - Model.Shop.CharacterShopItemData model = data; - model.OwnerId = ownerId; - - Context.CharacterShopItemData.Update(model); - } - - return Context.TrySaveChanges(); - } - - public bool DeleteCharacterShopItemData(long ownerId, int shopId, int shopItemId) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - Model.Shop.CharacterShopItemData? data = Context.CharacterShopItemData.Find(shopItemId, shopId, ownerId); - if (data == null) { - return false; - } - - Context.CharacterShopItemData.Remove(data); - return SaveChanges(); - } - - private CharacterShopItemData? ToShopItemData(Model.Shop.CharacterShopItemData? model) { - if (model == null) { - return null; - } - if (!game.itemMetadata.TryGet(model.Item.ItemId, out ItemMetadata? metadata)) { - return null; - } - Item item = model.Item.Convert(metadata); - CharacterShopItemData data = model; - data.Item = item; - return data; - } - } -} +using Maple2.Database.Extensions; +using Maple2.Model.Game; +using Maple2.Model.Game.Shop; +using Maple2.Model.Metadata; +using Microsoft.EntityFrameworkCore; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public CharacterShopData? CreateCharacterShopData(long ownerId, CharacterShopData shop) { + Model.Shop.CharacterShopData model = shop; + model.OwnerId = ownerId; + Context.CharacterShopData.Add(model); + + return SaveChanges() ? model : null; + } + + public IDictionary GetCharacterShopData(long ownerId) { + return Context.CharacterShopData.Where(data => data.OwnerId == ownerId) + .Select(data => data) + .ToDictionary(data => data.ShopId, data => data); + } + + public CharacterShopData? GetCharacterShopData(long ownerId, int shopId) { + return Context.CharacterShopData.Find(shopId, ownerId); + } + + public bool SaveCharacterShopData(long ownerId, ICollection shopDatas) { + foreach (CharacterShopData data in shopDatas) { + Model.Shop.CharacterShopData model = data; + model.OwnerId = ownerId; + + Context.CharacterShopData.Update(model); + } + + return Context.TrySaveChanges(); + } + + public bool DeleteCharacterShopData(long ownerId, int shopId) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + Model.Shop.CharacterShopData? data = Context.CharacterShopData.Find(shopId, ownerId); + if (data == null) { + return false; + } + + Context.CharacterShopData.Remove(data); + return SaveChanges(); + } + + public CharacterShopItemData? CreateCharacterShopItemData(long ownerId, CharacterShopItemData item) { + Model.Shop.CharacterShopItemData model = item; + model.OwnerId = ownerId; + Context.CharacterShopItemData.Add(model); + + return SaveChanges() ? ToShopItemData(model) : null; + } + + public ICollection GetCharacterShopItemData(long ownerId) { + return Context.CharacterShopItemData.Where(data => data.OwnerId == ownerId) + .AsEnumerable() + .Select(ToShopItemData) + .ToList()!; + } + + public bool SaveCharacterShopItemData(long ownerId, ICollection itemDatas) { + foreach (CharacterShopItemData data in itemDatas) { + Model.Shop.CharacterShopItemData model = data; + model.OwnerId = ownerId; + + Context.CharacterShopItemData.Update(model); + } + + return Context.TrySaveChanges(); + } + + public bool DeleteCharacterShopItemData(long ownerId, int shopId, int shopItemId) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + Model.Shop.CharacterShopItemData? data = Context.CharacterShopItemData.Find(shopItemId, shopId, ownerId); + if (data == null) { + return false; + } + + Context.CharacterShopItemData.Remove(data); + return SaveChanges(); + } + + private CharacterShopItemData? ToShopItemData(Model.Shop.CharacterShopItemData? model) { + if (model == null) { + return null; + } + if (!game.itemMetadata.TryGet(model.Item.ItemId, out ItemMetadata? metadata)) { + return null; + } + Item item = model.Item.Convert(metadata); + CharacterShopItemData data = model; + data.Item = item; + return data; + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.SystemBanner.cs b/Maple2.Database/Storage/Game/GameStorage.SystemBanner.cs index 761d18d9f..65fb05c6e 100644 --- a/Maple2.Database/Storage/Game/GameStorage.SystemBanner.cs +++ b/Maple2.Database/Storage/Game/GameStorage.SystemBanner.cs @@ -1,14 +1,14 @@ -using Maple2.Model.Game; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public IList GetBanners() { - return Context.SystemBanner - .Where(banner => banner.EndTime > DateTime.Now) - .Select(banner => banner) - .ToList(); - } - } -} +using Maple2.Model.Game; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public IList GetBanners() { + return Context.SystemBanner + .Where(banner => banner.EndTime > DateTime.Now) + .Select(banner => banner) + .ToList(); + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.UgcBannerSlots.cs b/Maple2.Database/Storage/Game/GameStorage.UgcBannerSlots.cs index 792422923..239a2bb43 100644 --- a/Maple2.Database/Storage/Game/GameStorage.UgcBannerSlots.cs +++ b/Maple2.Database/Storage/Game/GameStorage.UgcBannerSlots.cs @@ -1,42 +1,42 @@ -using BannerSlot = Maple2.Model.Game.Ugc.BannerSlot; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public List FindBannerSlotsByBannerId(long bannerId) { - return Context.BannerSlots - .Where(slot => slot.BannerId == bannerId) - .Select(slot => new BannerSlot(slot.Id, slot.ActivateTime, slot.BannerId, slot.Template)) - .ToList(); - } - - public BannerSlot AddBannerSlot(BannerSlot slot) { - Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry result = Context.BannerSlots.Add(new Model.BannerSlot { - ActivateTime = slot.ActivateTime, - BannerId = slot.BannerId, - Template = slot.Template, - }); - - Context.SaveChanges(); - - slot.Id = result.Entity.Id; - - return slot; - } - - public void UpdateBannerSlot(BannerSlot slot) { - Context.BannerSlots.Update(slot); - - Context.SaveChanges(); - } - - public void RemoveBannerSlots(IEnumerable slots) { - foreach (BannerSlot slot in slots) { - Context.BannerSlots.Remove(slot); - } - - Context.SaveChanges(); - } - } -} +using BannerSlot = Maple2.Model.Game.Ugc.BannerSlot; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public List FindBannerSlotsByBannerId(long bannerId) { + return Context.BannerSlots + .Where(slot => slot.BannerId == bannerId) + .Select(slot => new BannerSlot(slot.Id, slot.ActivateTime, slot.BannerId, slot.Template)) + .ToList(); + } + + public BannerSlot AddBannerSlot(BannerSlot slot) { + Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry result = Context.BannerSlots.Add(new Model.BannerSlot { + ActivateTime = slot.ActivateTime, + BannerId = slot.BannerId, + Template = slot.Template, + }); + + Context.SaveChanges(); + + slot.Id = result.Entity.Id; + + return slot; + } + + public void UpdateBannerSlot(BannerSlot slot) { + Context.BannerSlots.Update(slot); + + Context.SaveChanges(); + } + + public void RemoveBannerSlots(IEnumerable slots) { + foreach (BannerSlot slot in slots) { + Context.BannerSlots.Remove(slot); + } + + Context.SaveChanges(); + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.User.cs b/Maple2.Database/Storage/Game/GameStorage.User.cs index 5e145e4ae..2ab587add 100644 --- a/Maple2.Database/Storage/Game/GameStorage.User.cs +++ b/Maple2.Database/Storage/Game/GameStorage.User.cs @@ -1,526 +1,526 @@ -using Maple2.Database.Extensions; -using Maple2.Database.Model; -using Maple2.Model.Enum; -using Maple2.Model.Game; -using Maple2.Model.Metadata; -using Maple2.Server.Game.Manager.Config; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using Account = Maple2.Model.Game.Account; -using Character = Maple2.Model.Game.Character; -using SkillMacro = Maple2.Model.Game.SkillMacro; -using SkillBook = Maple2.Model.Game.SkillBook; -using SkillTab = Maple2.Model.Game.SkillTab; -using SkillPoint = Maple2.Model.Game.SkillPoint; -using Wardrobe = Maple2.Model.Game.Wardrobe; -using GameEventUserValue = Maple2.Model.Game.GameEventUserValue; -using Home = Maple2.Model.Game.Home; -using HomeLayout = Maple2.Database.Model.HomeLayout; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request : IPlayerInfoProvider { - public Account? GetAccount(long accountId) { - return Context.Account.Find(accountId); - } - - public Account? GetAccount(string username) { - return Context.Account - .FirstOrDefault(account => account.Username == username); - } - - public bool VerifyPassword(long accountId, string password) { - Model.Account? account = Context.Account.Find(accountId); -#if DEBUG - if (string.IsNullOrEmpty(account?.Password)) { - return true; - } -#endif - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - return account != null && BCrypt.Net.BCrypt.Verify(password, account.Password); - } - - public bool UpdateMachineId(long accountId, Guid machineId) { - Model.Account? account = Context.Account.Find(accountId); - if (account == null) { - return false; - } - account.MachineId = machineId; - Context.Account.Update(account); - - return Context.TrySaveChanges(); - } - - public (Account?, IList?) ListCharacters(long accountId) { - Model.Account? model = Context.Account - .Include(account => account.Characters) - .FirstOrDefault(account => account.Id == accountId); - if (model == null) { - return (null, null); - } - - IList? characters = model.Characters?.Select(c => c).ToList(); - if (characters != null) { - foreach (Character character in characters) { - character.AchievementInfo = GetAchievementInfo(accountId, character.Id); - } - } - - return (model, characters); - } - - public void SetAllCharacterToOffline() { - Context.Database.ExecuteSqlRaw("UPDATE `character` SET Channel = -1"); - } - - // If accountId is specified, only characters for the account will be returned. - public Character? GetCharacter(long characterId, long accountId = -1) { - if (accountId < 0) { - Character? characterFind = Context.Character.Find(characterId); - if (characterFind != null) { - characterFind.AchievementInfo = GetAchievementInfo(accountId, characterId); - characterFind.MarriageInfo = GetMarriageInfo(characterId); - } - return characterFind; - } - - // Limit character fetching to those owned by account. - Character? character = Context.Character.FirstOrDefault(character => - character.Id == characterId && character.AccountId == accountId); - if (character != null) { - character.AchievementInfo = GetAchievementInfo(accountId, characterId); - character.MarriageInfo = GetMarriageInfo(characterId); - Account? accountFind = Context.Account.Find(accountId); - character.PremiumTime = accountFind?.PremiumTime ?? 0; - } - return character; - } - - public long GetCharacterId(string name) { - return Context.Character.Where(character => character.Name.ToLower() == name.ToLower()) - .Select(character => character.Id) - .FirstOrDefault(); - } - - public PlayerInfo? GetPlayerInfo(long characterId) { - var result = (from character in Context.Character where character.Id == characterId - join account in Context.Account on character.AccountId equals account.Id - join indoor in Context.UgcMap on - new { - OwnerId = character.AccountId, - Indoor = true, - } equals new { - indoor.OwnerId, - indoor.Indoor, - } - join outdoor in Context.UgcMap on - new { - OwnerId = character.AccountId, - Indoor = false, - } equals new { - outdoor.OwnerId, - outdoor.Indoor, - } into plot - from outdoor in plot.DefaultIfEmpty() - select new { - character, - indoor, - outdoor, - account.PremiumTime, - }) - .FirstOrDefault(); - if (result == null) { - return null; - } - - Tuple guild = Context.GuildMember - .Where(member => member.CharacterId == characterId) - .Join(Context.Guild, member => member.GuildId, guild => guild.Id, - (member, guild) => new Tuple(guild.Id, guild.Name)) - .FirstOrDefault() ?? new Tuple(0, string.Empty); - - AchievementInfo achievementInfo = GetAchievementInfo(result.character.AccountId, result.character.Id); - IList clubs = ListClubs(result.character.Id); - return BuildPlayerInfo(result.character, result.indoor, result.outdoor, achievementInfo, guild.Item1, guild.Item2, result.PremiumTime, clubs); - } - - public Home? GetHome(long ownerId) { - Model.Home? model = Context.Home.Find(ownerId); - if (model == null) { - return null; - } - - Home home = model; - UgcMap[] ugcMaps = Context.UgcMap - .Where(map => map.OwnerId == ownerId) - .ToArray(); - PlotInfo? indoor = ToPlotInfo(ugcMaps.FirstOrDefault(map => map.Indoor)); - if (indoor == null) { - Logger.LogError("Home does not have a indoor entry: {OwnerId}", ownerId); - return null; - } - - foreach (long layoutUid in model.Layouts) { - HomeLayout? layout = GetHomeLayout(layoutUid); - if (layout is null) { - Logger.LogError("Home layout not found: {LayoutUid}", layoutUid); - continue; - } - - Maple2.Model.Game.HomeLayout? homeLayoutModel = ToHomeLayout(layout); - if (homeLayoutModel == null) { - Logger.LogError("Failed to convert HomeLayout: {LayoutUid}", layoutUid); - continue; - } - - home.Layouts.Add(homeLayoutModel); - } - - foreach (long layoutUid in model.Blueprints) { - HomeLayout? layout = GetHomeLayout(layoutUid); - if (layout is null) { - Logger.LogError("Home layout not found: {LayoutUid}", layoutUid); - continue; - } - - Maple2.Model.Game.HomeLayout? homeLayoutModel = ToHomeLayout(layout); - if (homeLayoutModel == null) { - Logger.LogError("Failed to convert HomeLayout: {LayoutUid}", layoutUid); - continue; - } - - home.Blueprints.Add(homeLayoutModel); - } - - home.Indoor = indoor; - home.Outdoor = ToPlotInfo(ugcMaps.FirstOrDefault(map => !map.Indoor)); - return home; - } - - // We pass in objectId only for Player initialization. - public Player? LoadPlayer(long accountId, long characterId, int objectId, short channel) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - Model.Account? account = Context.Account.Find(accountId); - if (account == null) { - return null; - } - - Model.Character? character = Context.Character.FirstOrDefault(character => - character.Id == characterId && character.AccountId == accountId); - if (character == null) { - return null; - } - - account.Online = true; - character.Channel = channel; - - Context.Account.Update(account); - Context.Character.Update(character); - Context.SaveChanges(); - - Tuple guild = Context.GuildMember - .Where(member => member.CharacterId == characterId) - .Join(Context.Guild, member => member.GuildId, guild => guild.Id, - (member, guild) => new Tuple(guild.Id, guild.Name)) - .FirstOrDefault() ?? new Tuple(0, string.Empty); - - List> clubs = Context.ClubMember - .Where(member => member.CharacterId == characterId) - .Join(Context.Club, member => member.ClubId, club => club.Id, - (member, club) => new Tuple(club.Id, club.Name)) - .ToList(); - - Home? home = GetHome(accountId); - if (home == null) { - return null; - } - - var player = new Player(account, character, objectId) { - Currency = new Currency { - Meret = account.Currency.Meret, - GameMeret = account.Currency.GameMeret, - Meso = character.Currency.Meso, - EventMeret = character.Currency.EventMeret, - ValorToken = character.Currency.ValorToken, - Treva = character.Currency.Treva, - Rue = character.Currency.Rue, - HaviFruit = character.Currency.HaviFruit, - ReverseCoin = character.Currency.ReverseCoin, - MentorToken = character.Currency.MentorToken, - MenteeToken = character.Currency.MenteeToken, - StarPoint = character.Currency.StarPoint, - MesoToken = account.Currency.MesoToken, - }, - Unlock = Context.CharacterUnlock.Find(characterId), - Home = home, - Character = { - GuildId = guild.Item1, - GuildName = guild.Item2, - ClubIds = clubs.Select(club => club.Item1).ToList(), - AchievementInfo = GetAchievementInfo(accountId, characterId), - MarriageInfo = GetMarriageInfo(characterId), - PremiumTime = account.PremiumTime, - }, - }; - - return player; - } - - public bool SavePlayer(Player player) { - Console.WriteLine($"> Begin Save... {Context.ContextId}"); - - Model.Account account = player.Account; - account.Currency = new AccountCurrency { - Meret = player.Currency.Meret, - GameMeret = player.Currency.GameMeret, - MesoToken = player.Currency.MesoToken, - }; - - Model.Character character = player.Character; - character.Currency = new CharacterCurrency { - Meso = player.Currency.Meso, - EventMeret = player.Currency.EventMeret, - ValorToken = player.Currency.ValorToken, - Treva = player.Currency.Treva, - Rue = player.Currency.Rue, - HaviFruit = player.Currency.HaviFruit, - ReverseCoin = player.Currency.ReverseCoin, - MentorToken = player.Currency.MentorToken, - MenteeToken = player.Currency.MenteeToken, - StarPoint = player.Currency.StarPoint, - }; - - Model.Account? dbAccount = Context.Account.Find(account.Id); - if (dbAccount == null) { - return false; - } - account.Password = dbAccount.Password; - - Context.Update(account); - Context.Update(character); - - CharacterUnlock unlock = player.Unlock; - unlock.CharacterId = character.Id; - Context.Update(unlock); - - Context.ChangeTracker.Entries().DisplayStates(); - return Context.TrySaveChanges(); - } - - public bool SaveCharacter(Character character) { - Context.Character.Update(character); - return Context.TrySaveChanges(); - } - - public (IList? KeyBinds, IList? HotBars, List?, List?, List? FavoriteStickers, List? FavoriteDesigners, - IDictionary? Lapenshards, int InstantRevivalCount, int ExplorationProgress, IDictionary?, - IDictionary?, SkillPoint? SkillPoint, IDictionary? GatheringCounts, IDictionary? GuideRecords, SkillBook?) LoadCharacterConfig(long characterId) { - CharacterConfig? config = Context.CharacterConfig.Find(characterId); - if (config == null) { - return (null, null, null, null, null, null, null, 0, 0, null, null, null, null, null, null); - } - - SkillBook? skillBook = config.SkillBook == null ? null : new SkillBook { - MaxSkillTabs = config.SkillBook.MaxSkillTabs, - ActiveSkillTabId = config.SkillBook.ActiveSkillTabId, - SkillTabs = Context.SkillTab.Where(tab => tab.CharacterId == characterId) - .Select(tab => tab) - .ToList(), - }; - - Dictionary eventValues = Context.GameEventUserValue.Where(value => value.CharacterId == characterId) - .Select(value => value) - .ToDictionary(value => value.Type, value => value); - - var skillPoint = new SkillPoint(); - if (config.SkillPoint != null) { - foreach (Model.SkillPoint point in config.SkillPoint) { - skillPoint[point.Source][point.Rank] = point.Points; - } - } - - return ( - config.KeyBinds, - config.HotBars, - config.SkillMacros?.Select(macro => macro).ToList(), - config.Wardrobes?.Select(wardrobe => wardrobe).ToList(), - config.FavoriteStickers?.Select(stickers => stickers).ToList(), - config.FavoriteDesigners?.Select(designer => designer).ToList(), - config.Lapenshards, - config.InstantRevivalCount, - config.ExplorationProgress, - config.StatPoints, - config.StatAllocation, - skillPoint, - config.GatheringCounts, - config.GuideRecords, - skillBook - ); - } - - public bool SaveCharacterConfig( - long characterId, - IList keyBinds, - IList hotBars, - IEnumerable skillMacros, - IEnumerable wardrobes, - IList favoriteStickers, - IList favoriteDesigners, - IDictionary lapenshards, - int instantRevivalCount, - int explorationProgress, - StatAttributes.PointAllocation allocation, - StatAttributes.PointSources statSources, - SkillPoint skillPoint, - IDictionary gatheringCounts, - IDictionary guideRecords, - SkillBook skillBook) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - CharacterConfig? config = Context.CharacterConfig.Find(characterId); - if (config == null) { - return false; - } - - config.KeyBinds = keyBinds; - config.HotBars = hotBars; - config.SkillMacros = skillMacros.Select(macro => macro).ToList(); - config.Wardrobes = wardrobes.Select(wardrobe => wardrobe).ToList(); - config.FavoriteStickers = favoriteStickers; - config.FavoriteDesigners = favoriteDesigners; - config.Lapenshards = lapenshards; - config.InstantRevivalCount = instantRevivalCount; - config.ExplorationProgress = explorationProgress; - config.StatAllocation = allocation.Attributes.ToDictionary( - attribute => attribute, - attribute => allocation[attribute]); - config.StatPoints = statSources.Points; - config.SkillPoint = skillPoint.Points.SelectMany(point => point.Value.Ranks.Select(rankPoint => new Model.SkillPoint { - Source = point.Key, - Rank = rankPoint.Key, - Points = rankPoint.Value, - })) - .ToList(); - config.GatheringCounts = gatheringCounts; - config.GuideRecords = guideRecords; - config.SkillBook = new Model.SkillBook { - MaxSkillTabs = skillBook.MaxSkillTabs, - ActiveSkillTabId = skillBook.ActiveSkillTabId, - }; - Context.CharacterConfig.Update(config); - - foreach (SkillTab skillTab in skillBook.SkillTabs) { - Model.SkillTab model = skillTab; - model.CharacterId = characterId; - Context.SkillTab.Update(model); - } - - return Context.TrySaveChanges(); - } - - #region Create - public Account CreateAccount(Account account, string password) { - Model.Account model = account; - model.Id = 0; - model.Password = BCrypt.Net.BCrypt.HashPassword(password, 13); -#if DEBUG - model.Currency = new AccountCurrency { - Meret = 9_999_999, - }; - model.Permissions = AdminPermissions.Admin.ToString(); -#endif - Context.Account.Add(model); - Context.SaveChanges(); // Exception if failed. - - Context.Home.Add(new Home { - AccountId = model.Id, - }); - Context.UgcMap.Add(new UgcMap { - OwnerId = model.Id, - MapId = Constant.DefaultHomeMapId, - Indoor = true, - Number = Constant.DefaultHomeNumber, - }); - Context.SaveChanges(); // Exception if failed. - - return model; - } - - public Character? CreateCharacter(Character character) { - Model.Character model = character; - model.Id = 0; - model.Channel = -1; -#if DEBUG - model.Currency = new CharacterCurrency { - Meso = 999999999, - }; -#endif - Context.Character.Add(model); - return Context.TrySaveChanges() ? model : null; - } - - public bool InitNewCharacter(long characterId, Unlock unlock) { - CharacterUnlock model = unlock; - model.CharacterId = characterId; - Context.CharacterUnlock.Add(model); - - SkillTab? defaultTab = CreateSkillTab(characterId, new SkillTab("Build 1") { - Id = characterId, - }); - if (defaultTab == null) { - return false; - } - - var config = new CharacterConfig { - CharacterId = characterId, - SkillBook = new Model.SkillBook { - MaxSkillTabs = 1, - ActiveSkillTabId = defaultTab.Id, - }, - }; - Context.CharacterConfig.Add(config); - - return Context.TrySaveChanges(); - } - - public SkillTab? CreateSkillTab(long characterId, SkillTab skillTab) { - Model.SkillTab model = skillTab; - model.CharacterId = characterId; - Context.SkillTab.Add(model); - return Context.TrySaveChanges() ? model : null; - } - #endregion - - #region Delete - public bool UpdateDelete(long accountId, long characterId, long time) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - Model.Character? model = Context.Character.FirstOrDefault(character => - character.Id == characterId && character.AccountId == accountId); - if (model == null) { - return false; - } - - model.DeleteTime = time.FromEpochSeconds(); - Context.Update(model); - return Context.TrySaveChanges(); - } - - public bool DeleteCharacter(long accountId, long characterId) { - Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; - - Model.Character? character = Context.Character.FirstOrDefault(character => - character.Id == characterId && character.AccountId == accountId); - if (character == null) { - return false; - } - - Context.Remove(character); - return Context.TrySaveChanges(); - } - #endregion - } - -} +using Maple2.Database.Extensions; +using Maple2.Database.Model; +using Maple2.Model.Enum; +using Maple2.Model.Game; +using Maple2.Model.Metadata; +using Maple2.Server.Game.Manager.Config; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Account = Maple2.Model.Game.Account; +using Character = Maple2.Model.Game.Character; +using SkillMacro = Maple2.Model.Game.SkillMacro; +using SkillBook = Maple2.Model.Game.SkillBook; +using SkillTab = Maple2.Model.Game.SkillTab; +using SkillPoint = Maple2.Model.Game.SkillPoint; +using Wardrobe = Maple2.Model.Game.Wardrobe; +using GameEventUserValue = Maple2.Model.Game.GameEventUserValue; +using Home = Maple2.Model.Game.Home; +using HomeLayout = Maple2.Database.Model.HomeLayout; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request : IPlayerInfoProvider { + public Account? GetAccount(long accountId) { + return Context.Account.Find(accountId); + } + + public Account? GetAccount(string username) { + return Context.Account + .FirstOrDefault(account => account.Username == username); + } + + public bool VerifyPassword(long accountId, string password) { + Model.Account? account = Context.Account.Find(accountId); +#if DEBUG + if (string.IsNullOrEmpty(account?.Password)) { + return true; + } +#endif + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + return account != null && BCrypt.Net.BCrypt.Verify(password, account.Password); + } + + public bool UpdateMachineId(long accountId, Guid machineId) { + Model.Account? account = Context.Account.Find(accountId); + if (account == null) { + return false; + } + account.MachineId = machineId; + Context.Account.Update(account); + + return Context.TrySaveChanges(); + } + + public (Account?, IList?) ListCharacters(long accountId) { + Model.Account? model = Context.Account + .Include(account => account.Characters) + .FirstOrDefault(account => account.Id == accountId); + if (model == null) { + return (null, null); + } + + IList? characters = model.Characters?.Select(c => c).ToList(); + if (characters != null) { + foreach (Character character in characters) { + character.AchievementInfo = GetAchievementInfo(accountId, character.Id); + } + } + + return (model, characters); + } + + public void SetAllCharacterToOffline() { + Context.Database.ExecuteSqlRaw("UPDATE `character` SET Channel = -1"); + } + + // If accountId is specified, only characters for the account will be returned. + public Character? GetCharacter(long characterId, long accountId = -1) { + if (accountId < 0) { + Character? characterFind = Context.Character.Find(characterId); + if (characterFind != null) { + characterFind.AchievementInfo = GetAchievementInfo(accountId, characterId); + characterFind.MarriageInfo = GetMarriageInfo(characterId); + } + return characterFind; + } + + // Limit character fetching to those owned by account. + Character? character = Context.Character.FirstOrDefault(character => + character.Id == characterId && character.AccountId == accountId); + if (character != null) { + character.AchievementInfo = GetAchievementInfo(accountId, characterId); + character.MarriageInfo = GetMarriageInfo(characterId); + Account? accountFind = Context.Account.Find(accountId); + character.PremiumTime = accountFind?.PremiumTime ?? 0; + } + return character; + } + + public long GetCharacterId(string name) { + return Context.Character.Where(character => character.Name.ToLower() == name.ToLower()) + .Select(character => character.Id) + .FirstOrDefault(); + } + + public PlayerInfo? GetPlayerInfo(long characterId) { + var result = (from character in Context.Character where character.Id == characterId + join account in Context.Account on character.AccountId equals account.Id + join indoor in Context.UgcMap on + new { + OwnerId = character.AccountId, + Indoor = true, + } equals new { + indoor.OwnerId, + indoor.Indoor, + } + join outdoor in Context.UgcMap on + new { + OwnerId = character.AccountId, + Indoor = false, + } equals new { + outdoor.OwnerId, + outdoor.Indoor, + } into plot + from outdoor in plot.DefaultIfEmpty() + select new { + character, + indoor, + outdoor, + account.PremiumTime, + }) + .FirstOrDefault(); + if (result == null) { + return null; + } + + Tuple guild = Context.GuildMember + .Where(member => member.CharacterId == characterId) + .Join(Context.Guild, member => member.GuildId, guild => guild.Id, + (member, guild) => new Tuple(guild.Id, guild.Name)) + .FirstOrDefault() ?? new Tuple(0, string.Empty); + + AchievementInfo achievementInfo = GetAchievementInfo(result.character.AccountId, result.character.Id); + IList clubs = ListClubs(result.character.Id); + return BuildPlayerInfo(result.character, result.indoor, result.outdoor, achievementInfo, guild.Item1, guild.Item2, result.PremiumTime, clubs); + } + + public Home? GetHome(long ownerId) { + Model.Home? model = Context.Home.Find(ownerId); + if (model == null) { + return null; + } + + Home home = model; + UgcMap[] ugcMaps = Context.UgcMap + .Where(map => map.OwnerId == ownerId) + .ToArray(); + PlotInfo? indoor = ToPlotInfo(ugcMaps.FirstOrDefault(map => map.Indoor)); + if (indoor == null) { + Logger.LogError("Home does not have a indoor entry: {OwnerId}", ownerId); + return null; + } + + foreach (long layoutUid in model.Layouts) { + HomeLayout? layout = GetHomeLayout(layoutUid); + if (layout is null) { + Logger.LogError("Home layout not found: {LayoutUid}", layoutUid); + continue; + } + + Maple2.Model.Game.HomeLayout? homeLayoutModel = ToHomeLayout(layout); + if (homeLayoutModel == null) { + Logger.LogError("Failed to convert HomeLayout: {LayoutUid}", layoutUid); + continue; + } + + home.Layouts.Add(homeLayoutModel); + } + + foreach (long layoutUid in model.Blueprints) { + HomeLayout? layout = GetHomeLayout(layoutUid); + if (layout is null) { + Logger.LogError("Home layout not found: {LayoutUid}", layoutUid); + continue; + } + + Maple2.Model.Game.HomeLayout? homeLayoutModel = ToHomeLayout(layout); + if (homeLayoutModel == null) { + Logger.LogError("Failed to convert HomeLayout: {LayoutUid}", layoutUid); + continue; + } + + home.Blueprints.Add(homeLayoutModel); + } + + home.Indoor = indoor; + home.Outdoor = ToPlotInfo(ugcMaps.FirstOrDefault(map => !map.Indoor)); + return home; + } + + // We pass in objectId only for Player initialization. + public Player? LoadPlayer(long accountId, long characterId, int objectId, short channel) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + Model.Account? account = Context.Account.Find(accountId); + if (account == null) { + return null; + } + + Model.Character? character = Context.Character.FirstOrDefault(character => + character.Id == characterId && character.AccountId == accountId); + if (character == null) { + return null; + } + + account.Online = true; + character.Channel = channel; + + Context.Account.Update(account); + Context.Character.Update(character); + Context.SaveChanges(); + + Tuple guild = Context.GuildMember + .Where(member => member.CharacterId == characterId) + .Join(Context.Guild, member => member.GuildId, guild => guild.Id, + (member, guild) => new Tuple(guild.Id, guild.Name)) + .FirstOrDefault() ?? new Tuple(0, string.Empty); + + List> clubs = Context.ClubMember + .Where(member => member.CharacterId == characterId) + .Join(Context.Club, member => member.ClubId, club => club.Id, + (member, club) => new Tuple(club.Id, club.Name)) + .ToList(); + + Home? home = GetHome(accountId); + if (home == null) { + return null; + } + + var player = new Player(account, character, objectId) { + Currency = new Currency { + Meret = account.Currency.Meret, + GameMeret = account.Currency.GameMeret, + Meso = character.Currency.Meso, + EventMeret = character.Currency.EventMeret, + ValorToken = character.Currency.ValorToken, + Treva = character.Currency.Treva, + Rue = character.Currency.Rue, + HaviFruit = character.Currency.HaviFruit, + ReverseCoin = character.Currency.ReverseCoin, + MentorToken = character.Currency.MentorToken, + MenteeToken = character.Currency.MenteeToken, + StarPoint = character.Currency.StarPoint, + MesoToken = account.Currency.MesoToken, + }, + Unlock = Context.CharacterUnlock.Find(characterId), + Home = home, + Character = { + GuildId = guild.Item1, + GuildName = guild.Item2, + ClubIds = clubs.Select(club => club.Item1).ToList(), + AchievementInfo = GetAchievementInfo(accountId, characterId), + MarriageInfo = GetMarriageInfo(characterId), + PremiumTime = account.PremiumTime, + }, + }; + + return player; + } + + public bool SavePlayer(Player player) { + Console.WriteLine($"> Begin Save... {Context.ContextId}"); + + Model.Account account = player.Account; + account.Currency = new AccountCurrency { + Meret = player.Currency.Meret, + GameMeret = player.Currency.GameMeret, + MesoToken = player.Currency.MesoToken, + }; + + Model.Character character = player.Character; + character.Currency = new CharacterCurrency { + Meso = player.Currency.Meso, + EventMeret = player.Currency.EventMeret, + ValorToken = player.Currency.ValorToken, + Treva = player.Currency.Treva, + Rue = player.Currency.Rue, + HaviFruit = player.Currency.HaviFruit, + ReverseCoin = player.Currency.ReverseCoin, + MentorToken = player.Currency.MentorToken, + MenteeToken = player.Currency.MenteeToken, + StarPoint = player.Currency.StarPoint, + }; + + Model.Account? dbAccount = Context.Account.Find(account.Id); + if (dbAccount == null) { + return false; + } + account.Password = dbAccount.Password; + + Context.Update(account); + Context.Update(character); + + CharacterUnlock unlock = player.Unlock; + unlock.CharacterId = character.Id; + Context.Update(unlock); + + Context.ChangeTracker.Entries().DisplayStates(); + return Context.TrySaveChanges(); + } + + public bool SaveCharacter(Character character) { + Context.Character.Update(character); + return Context.TrySaveChanges(); + } + + public (IList? KeyBinds, IList? HotBars, List?, List?, List? FavoriteStickers, List? FavoriteDesigners, + IDictionary? Lapenshards, int InstantRevivalCount, int ExplorationProgress, IDictionary?, + IDictionary?, SkillPoint? SkillPoint, IDictionary? GatheringCounts, IDictionary? GuideRecords, SkillBook?) LoadCharacterConfig(long characterId) { + CharacterConfig? config = Context.CharacterConfig.Find(characterId); + if (config == null) { + return (null, null, null, null, null, null, null, 0, 0, null, null, null, null, null, null); + } + + SkillBook? skillBook = config.SkillBook == null ? null : new SkillBook { + MaxSkillTabs = config.SkillBook.MaxSkillTabs, + ActiveSkillTabId = config.SkillBook.ActiveSkillTabId, + SkillTabs = Context.SkillTab.Where(tab => tab.CharacterId == characterId) + .Select(tab => tab) + .ToList(), + }; + + Dictionary eventValues = Context.GameEventUserValue.Where(value => value.CharacterId == characterId) + .Select(value => value) + .ToDictionary(value => value.Type, value => value); + + var skillPoint = new SkillPoint(); + if (config.SkillPoint != null) { + foreach (Model.SkillPoint point in config.SkillPoint) { + skillPoint[point.Source][point.Rank] = point.Points; + } + } + + return ( + config.KeyBinds, + config.HotBars, + config.SkillMacros?.Select(macro => macro).ToList(), + config.Wardrobes?.Select(wardrobe => wardrobe).ToList(), + config.FavoriteStickers?.Select(stickers => stickers).ToList(), + config.FavoriteDesigners?.Select(designer => designer).ToList(), + config.Lapenshards, + config.InstantRevivalCount, + config.ExplorationProgress, + config.StatPoints, + config.StatAllocation, + skillPoint, + config.GatheringCounts, + config.GuideRecords, + skillBook + ); + } + + public bool SaveCharacterConfig( + long characterId, + IList keyBinds, + IList hotBars, + IEnumerable skillMacros, + IEnumerable wardrobes, + IList favoriteStickers, + IList favoriteDesigners, + IDictionary lapenshards, + int instantRevivalCount, + int explorationProgress, + StatAttributes.PointAllocation allocation, + StatAttributes.PointSources statSources, + SkillPoint skillPoint, + IDictionary gatheringCounts, + IDictionary guideRecords, + SkillBook skillBook) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + CharacterConfig? config = Context.CharacterConfig.Find(characterId); + if (config == null) { + return false; + } + + config.KeyBinds = keyBinds; + config.HotBars = hotBars; + config.SkillMacros = skillMacros.Select(macro => macro).ToList(); + config.Wardrobes = wardrobes.Select(wardrobe => wardrobe).ToList(); + config.FavoriteStickers = favoriteStickers; + config.FavoriteDesigners = favoriteDesigners; + config.Lapenshards = lapenshards; + config.InstantRevivalCount = instantRevivalCount; + config.ExplorationProgress = explorationProgress; + config.StatAllocation = allocation.Attributes.ToDictionary( + attribute => attribute, + attribute => allocation[attribute]); + config.StatPoints = statSources.Points; + config.SkillPoint = skillPoint.Points.SelectMany(point => point.Value.Ranks.Select(rankPoint => new Model.SkillPoint { + Source = point.Key, + Rank = rankPoint.Key, + Points = rankPoint.Value, + })) + .ToList(); + config.GatheringCounts = gatheringCounts; + config.GuideRecords = guideRecords; + config.SkillBook = new Model.SkillBook { + MaxSkillTabs = skillBook.MaxSkillTabs, + ActiveSkillTabId = skillBook.ActiveSkillTabId, + }; + Context.CharacterConfig.Update(config); + + foreach (SkillTab skillTab in skillBook.SkillTabs) { + Model.SkillTab model = skillTab; + model.CharacterId = characterId; + Context.SkillTab.Update(model); + } + + return Context.TrySaveChanges(); + } + + #region Create + public Account CreateAccount(Account account, string password) { + Model.Account model = account; + model.Id = 0; + model.Password = BCrypt.Net.BCrypt.HashPassword(password, 13); +#if DEBUG + model.Currency = new AccountCurrency { + Meret = 9_999_999, + }; + model.Permissions = AdminPermissions.Admin.ToString(); +#endif + Context.Account.Add(model); + Context.SaveChanges(); // Exception if failed. + + Context.Home.Add(new Home { + AccountId = model.Id, + }); + Context.UgcMap.Add(new UgcMap { + OwnerId = model.Id, + MapId = Constant.DefaultHomeMapId, + Indoor = true, + Number = Constant.DefaultHomeNumber, + }); + Context.SaveChanges(); // Exception if failed. + + return model; + } + + public Character? CreateCharacter(Character character) { + Model.Character model = character; + model.Id = 0; + model.Channel = -1; +#if DEBUG + model.Currency = new CharacterCurrency { + Meso = 999999999, + }; +#endif + Context.Character.Add(model); + return Context.TrySaveChanges() ? model : null; + } + + public bool InitNewCharacter(long characterId, Unlock unlock) { + CharacterUnlock model = unlock; + model.CharacterId = characterId; + Context.CharacterUnlock.Add(model); + + SkillTab? defaultTab = CreateSkillTab(characterId, new SkillTab("Build 1") { + Id = characterId, + }); + if (defaultTab == null) { + return false; + } + + var config = new CharacterConfig { + CharacterId = characterId, + SkillBook = new Model.SkillBook { + MaxSkillTabs = 1, + ActiveSkillTabId = defaultTab.Id, + }, + }; + Context.CharacterConfig.Add(config); + + return Context.TrySaveChanges(); + } + + public SkillTab? CreateSkillTab(long characterId, SkillTab skillTab) { + Model.SkillTab model = skillTab; + model.CharacterId = characterId; + Context.SkillTab.Add(model); + return Context.TrySaveChanges() ? model : null; + } + #endregion + + #region Delete + public bool UpdateDelete(long accountId, long characterId, long time) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + Model.Character? model = Context.Character.FirstOrDefault(character => + character.Id == characterId && character.AccountId == accountId); + if (model == null) { + return false; + } + + model.DeleteTime = time.FromEpochSeconds(); + Context.Update(model); + return Context.TrySaveChanges(); + } + + public bool DeleteCharacter(long accountId, long characterId) { + Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll; + + Model.Character? character = Context.Character.FirstOrDefault(character => + character.Id == characterId && character.AccountId == accountId); + if (character == null) { + return false; + } + + Context.Remove(character); + return Context.TrySaveChanges(); + } + #endregion + } + +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Web.cs b/Maple2.Database/Storage/Game/GameStorage.Web.cs index 8bc8d3957..25d2a22d5 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Web.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Web.cs @@ -1,124 +1,124 @@ -using Maple2.Database.Model.Ranking; -using Maple2.Model.Game; -using Character = Maple2.Database.Model.Character; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - - #region Ranking - // This is not an efficient way to fetch rankings. This is only done for proof of concept. - // Ideally, we would store the rank records in db and fetch that. any current season data should be refreshed on a daily basis. - public TrophyRankInfo? GetTrophyRankInfo(long characterId) { - Character? character = Context.Character.Find(characterId); - if (character == null) { - return null; - } - AchievementInfo achievementInfo = GetAchievementInfo(character.AccountId, character.Id); - - // Get all characters with their account IDs - var allCharacters = Context.Character - .Select(c => new { - CharacterId = c.Id, - AccountId = c.AccountId, - }) - .ToList(); - - // Calculate total trophies for each character - var characterTrophies = new List<(long CharacterId, int TotalTrophies)>(); - foreach (var characterEntry in allCharacters) { - AchievementInfo info = GetAchievementInfo(characterEntry.AccountId, characterEntry.CharacterId); - characterTrophies.Add((characterEntry.CharacterId, info.Total)); - } - - // Sort by trophy count (descending) and find our character's position - characterTrophies = characterTrophies.OrderByDescending(ct => ct.TotalTrophies).ToList(); - int rank = characterTrophies.FindIndex(ct => ct.CharacterId == character.Id) + 1; - - // If character not found (shouldn't happen), default to last place - if (rank == 0) { - rank = characterTrophies.Count + 1; - } - return new TrophyRankInfo( - Rank: rank, - CharacterId: character.Id, - Name: character.Name, - Profile: character.Profile?.Picture ?? string.Empty, - Trophy: achievementInfo); - } - - public TrophyRankInfo? GetTrophyRankInfo(string name) { - long characterId = GetCharacterId(name); - if (characterId == default) { - return null; - } - - return GetTrophyRankInfo(characterId); - } - - public IList GetTrophyRankings() { - // Get all characters with their account IDs - var characters = Context.Character - .Select(c => new { - CharacterId = c.Id, - AccountId = c.AccountId, - Name = c.Name, - Profile = c.Profile.Picture, - }) - .ToList(); - - // Calculate total trophies for each character (including account-wide trophies) - var characterRankings = new List<(int Rank, long CharacterId, string Name, string Profile, AchievementInfo Trophy)>(); - - foreach (var character in characters) { - // Get achievement info for this character (combines account and character trophies) - AchievementInfo achievementInfo = GetAchievementInfo(character.AccountId, character.CharacterId); - if (achievementInfo.Total <= 0) { - continue; - } - // Add to our list - characterRankings.Add((0, character.CharacterId, character.Name, character.Profile ?? string.Empty, achievementInfo)); - } - - // Sort by total trophy count and assign ranks - characterRankings = characterRankings - .OrderByDescending(r => r.Trophy.Total) - .Select((r, index) => (index + 1, r.CharacterId, r.Name, r.Profile, r.Trophy)) - .Take(200) - .ToList(); - - // Convert to TrophyRankInfo objects - return characterRankings - .Select(r => new TrophyRankInfo( - Rank: r.Rank, - CharacterId: r.CharacterId, - Name: r.Name, - Profile: r.Profile, - Trophy: r.Trophy)) - .ToList(); - } - #endregion - - public IList GetMentorList(long accountId, long characterId) { - // Get only characters that have been modified in the last 30 days (including time) - DateTime thirtyDaysAgo = DateTime.Now.AddDays(-30); - - // Get filtered character IDs, excluding the current account and character - // Group by AccountId to ensure we only get one character per account - List filteredCharacterIds = Context.Character - .Where(c => c.LastModified >= thirtyDaysAgo && - c.AccountId != accountId && - c.Id != characterId) - .GroupBy(c => c.AccountId) // Group by AccountId - .Select(g => g.OrderByDescending(c => c.LastModified).First().Id) // Take the most recently modified character from each account - .ToList(); // Materialize the query here - - // Randomize the order and take up to 50 - return filteredCharacterIds - .OrderBy(_ => Random.Shared.Next()) // Randomize order - .Take(50) - .ToList(); - } - } -} +using Maple2.Database.Model.Ranking; +using Maple2.Model.Game; +using Character = Maple2.Database.Model.Character; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + + #region Ranking + // This is not an efficient way to fetch rankings. This is only done for proof of concept. + // Ideally, we would store the rank records in db and fetch that. any current season data should be refreshed on a daily basis. + public TrophyRankInfo? GetTrophyRankInfo(long characterId) { + Character? character = Context.Character.Find(characterId); + if (character == null) { + return null; + } + AchievementInfo achievementInfo = GetAchievementInfo(character.AccountId, character.Id); + + // Get all characters with their account IDs + var allCharacters = Context.Character + .Select(c => new { + CharacterId = c.Id, + AccountId = c.AccountId, + }) + .ToList(); + + // Calculate total trophies for each character + var characterTrophies = new List<(long CharacterId, int TotalTrophies)>(); + foreach (var characterEntry in allCharacters) { + AchievementInfo info = GetAchievementInfo(characterEntry.AccountId, characterEntry.CharacterId); + characterTrophies.Add((characterEntry.CharacterId, info.Total)); + } + + // Sort by trophy count (descending) and find our character's position + characterTrophies = characterTrophies.OrderByDescending(ct => ct.TotalTrophies).ToList(); + int rank = characterTrophies.FindIndex(ct => ct.CharacterId == character.Id) + 1; + + // If character not found (shouldn't happen), default to last place + if (rank == 0) { + rank = characterTrophies.Count + 1; + } + return new TrophyRankInfo( + Rank: rank, + CharacterId: character.Id, + Name: character.Name, + Profile: character.Profile?.Picture ?? string.Empty, + Trophy: achievementInfo); + } + + public TrophyRankInfo? GetTrophyRankInfo(string name) { + long characterId = GetCharacterId(name); + if (characterId == default) { + return null; + } + + return GetTrophyRankInfo(characterId); + } + + public IList GetTrophyRankings() { + // Get all characters with their account IDs + var characters = Context.Character + .Select(c => new { + CharacterId = c.Id, + AccountId = c.AccountId, + Name = c.Name, + Profile = c.Profile.Picture, + }) + .ToList(); + + // Calculate total trophies for each character (including account-wide trophies) + var characterRankings = new List<(int Rank, long CharacterId, string Name, string Profile, AchievementInfo Trophy)>(); + + foreach (var character in characters) { + // Get achievement info for this character (combines account and character trophies) + AchievementInfo achievementInfo = GetAchievementInfo(character.AccountId, character.CharacterId); + if (achievementInfo.Total <= 0) { + continue; + } + // Add to our list + characterRankings.Add((0, character.CharacterId, character.Name, character.Profile ?? string.Empty, achievementInfo)); + } + + // Sort by total trophy count and assign ranks + characterRankings = characterRankings + .OrderByDescending(r => r.Trophy.Total) + .Select((r, index) => (index + 1, r.CharacterId, r.Name, r.Profile, r.Trophy)) + .Take(200) + .ToList(); + + // Convert to TrophyRankInfo objects + return characterRankings + .Select(r => new TrophyRankInfo( + Rank: r.Rank, + CharacterId: r.CharacterId, + Name: r.Name, + Profile: r.Profile, + Trophy: r.Trophy)) + .ToList(); + } + #endregion + + public IList GetMentorList(long accountId, long characterId) { + // Get only characters that have been modified in the last 30 days (including time) + DateTime thirtyDaysAgo = DateTime.Now.AddDays(-30); + + // Get filtered character IDs, excluding the current account and character + // Group by AccountId to ensure we only get one character per account + List filteredCharacterIds = Context.Character + .Where(c => c.LastModified >= thirtyDaysAgo && + c.AccountId != accountId && + c.Id != characterId) + .GroupBy(c => c.AccountId) // Group by AccountId + .Select(g => g.OrderByDescending(c => c.LastModified).First().Id) // Take the most recently modified character from each account + .ToList(); // Materialize the query here + + // Randomize the order and take up to 50 + return filteredCharacterIds + .OrderBy(_ => Random.Shared.Next()) // Randomize order + .Take(50) + .ToList(); + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.Wedding.cs b/Maple2.Database/Storage/Game/GameStorage.Wedding.cs index 3c5ed2cf7..81342d18b 100644 --- a/Maple2.Database/Storage/Game/GameStorage.Wedding.cs +++ b/Maple2.Database/Storage/Game/GameStorage.Wedding.cs @@ -1,161 +1,161 @@ -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Maple2.Model.Game; -using Marriage = Maple2.Model.Game.Marriage; -using MarriageExp = Maple2.Model.Game.MarriageExp; -using WeddingHall = Maple2.Model.Game.WeddingHall; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - public partial class Request { - public Marriage? CreateMarriage(long partner1Id, long partner2Id) { - BeginTransaction(); - - Model.Marriage marriage = new Model.Marriage { - Partner1Id = partner1Id, - Partner2Id = partner2Id, - Status = MaritalStatus.Engaged, - Profile = string.Empty, - Partner1Message = string.Empty, - Partner2Message = string.Empty, - CreationTime = DateTime.Now, - }; - Context.Marriage.Add(marriage); - if (!SaveChanges()) { - return null; - } - - return Commit() ? GetMarriage(partner1Id) : null; - } - - public bool SaveMarriage(Marriage marriage) { - // Don't save marriage if it was disbanded. - if (!Context.Marriage.Any(model => model.Id == marriage.Id)) { - return false; - } - - Context.Marriage.Update(marriage); - - return Context.TrySaveChanges(); - } - - public bool DeleteMarriage(long marriageId) { - Model.Marriage? marriage = Context.Marriage.Find(marriageId); - if (marriage == null) { - return false; - } - - Context.Marriage.Remove(marriage); - return Context.TrySaveChanges(); - } - - public Marriage? GetMarriage(long characterId = 0, long weddingId = 0) { - Model.Marriage? marriage = weddingId > 0 ? Context.Marriage.Find(weddingId) : - Context.Marriage.FirstOrDefault(marriage => marriage.Partner1Id == characterId || marriage.Partner2Id == characterId); - if (marriage == null) { - return null; - } - - PlayerInfo? partner1 = GetPlayerInfo(marriage.Partner1Id); - PlayerInfo? partner2 = GetPlayerInfo(marriage.Partner2Id); - if (partner1 == null || partner2 == null) { - return null; - } - - return new Marriage { - Id = marriage.Id, - CreationTime = marriage.CreationTime.ToEpochSeconds(), - ExpHistory = marriage.ExpHistory.Select(exp => exp).ToList(), - Partner1 = new MarriagePartner { - Info = partner1.CharacterId == characterId ? partner2 : partner1, - Message = partner1.CharacterId == characterId ? marriage.Partner2Message : marriage.Partner1Message, - }, - Partner2 = new MarriagePartner { - Info = partner1.CharacterId == characterId ? partner1 : partner2, - Message = partner1.CharacterId == characterId ? marriage.Partner1Message : marriage.Partner2Message, - }, - Status = marriage.Status, - }; - } - - public MarriageInfo GetMarriageInfo(long characterId) { - Model.Marriage? model = Context.Marriage.FirstOrDefault(member => member.Partner1Id == characterId || member.Partner2Id == characterId); - if (model == null) { - return new MarriageInfo(); - } - - PlayerInfo? owner = GetPlayerInfo(model.Partner1Id); - PlayerInfo? partner = GetPlayerInfo(model.Partner2Id); - - return owner == null || partner == null ? new MarriageInfo() : new MarriageInfo { - Status = model.Status, - CreationTime = model.CreationTime.ToEpochSeconds(), - Partner1Name = owner.Name, - Partner2Name = partner.Name, - }; - } - - public WeddingHall? CreateWeddingHall(WeddingHall hall, Marriage marriage) { - BeginTransaction(); - - Model.WeddingHall model = hall; - model.MarriageId = marriage.Id; - model.OwnerId = marriage.Partner1.CharacterId; - model.CreationTime = DateTime.Now; - Context.WeddingHall.Add(model); - if (!SaveChanges()) { - return null; - } - - return Commit() ? GetWeddingHall(marriage) : null; - } - - public WeddingHall? GetWeddingHall(Marriage marriage) { - Model.WeddingHall? model = Context.WeddingHall.FirstOrDefault(hall => hall.MarriageId == marriage.Id); - return model?.Convert(marriage); - } - - public WeddingHall? GetWeddingHall(long hallId = 0, long marriageId = 0) { - Model.WeddingHall? model = hallId > 0 ? Context.WeddingHall.Find(hallId) : - Context.WeddingHall.FirstOrDefault(hall => hall.MarriageId == marriageId); - - if (model == null) { - return null; - } - - Marriage? marriage = GetMarriage(weddingId: marriageId); - return marriage == null ? null : model.Convert(marriage); - } - - public bool DeleteWeddingHall(long hallId) { - Model.WeddingHall? hall = Context.WeddingHall.Find(hallId); - if (hall == null) { - return false; - } - - Context.WeddingHall.Remove(hall); - return Context.TrySaveChanges(); - } - - public bool WeddingHallTimeIsAvailable(long ceremonyTime) { - DateTime ceremonyDateTime = ceremonyTime.FromEpochSeconds(); - return Context.WeddingHall.FirstOrDefault(hall => hall.CeremonyTime == ceremonyDateTime) == null; - } - - public IEnumerable GetWeddingHalls() { - List entries = Context.WeddingHall - .Where(hall => hall.CeremonyTime > DateTime.Now) - .AsEnumerable() - .ToList(); - - foreach (Model.WeddingHall entry in entries) { - Marriage? marriage = GetMarriage(weddingId: entry.MarriageId); - if (marriage == null) { - continue; - } - yield return entry.Convert(marriage); - } - } - } -} +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Maple2.Model.Game; +using Marriage = Maple2.Model.Game.Marriage; +using MarriageExp = Maple2.Model.Game.MarriageExp; +using WeddingHall = Maple2.Model.Game.WeddingHall; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + public partial class Request { + public Marriage? CreateMarriage(long partner1Id, long partner2Id) { + BeginTransaction(); + + Model.Marriage marriage = new Model.Marriage { + Partner1Id = partner1Id, + Partner2Id = partner2Id, + Status = MaritalStatus.Engaged, + Profile = string.Empty, + Partner1Message = string.Empty, + Partner2Message = string.Empty, + CreationTime = DateTime.Now, + }; + Context.Marriage.Add(marriage); + if (!SaveChanges()) { + return null; + } + + return Commit() ? GetMarriage(partner1Id) : null; + } + + public bool SaveMarriage(Marriage marriage) { + // Don't save marriage if it was disbanded. + if (!Context.Marriage.Any(model => model.Id == marriage.Id)) { + return false; + } + + Context.Marriage.Update(marriage); + + return Context.TrySaveChanges(); + } + + public bool DeleteMarriage(long marriageId) { + Model.Marriage? marriage = Context.Marriage.Find(marriageId); + if (marriage == null) { + return false; + } + + Context.Marriage.Remove(marriage); + return Context.TrySaveChanges(); + } + + public Marriage? GetMarriage(long characterId = 0, long weddingId = 0) { + Model.Marriage? marriage = weddingId > 0 ? Context.Marriage.Find(weddingId) : + Context.Marriage.FirstOrDefault(marriage => marriage.Partner1Id == characterId || marriage.Partner2Id == characterId); + if (marriage == null) { + return null; + } + + PlayerInfo? partner1 = GetPlayerInfo(marriage.Partner1Id); + PlayerInfo? partner2 = GetPlayerInfo(marriage.Partner2Id); + if (partner1 == null || partner2 == null) { + return null; + } + + return new Marriage { + Id = marriage.Id, + CreationTime = marriage.CreationTime.ToEpochSeconds(), + ExpHistory = marriage.ExpHistory.Select(exp => exp).ToList(), + Partner1 = new MarriagePartner { + Info = partner1.CharacterId == characterId ? partner2 : partner1, + Message = partner1.CharacterId == characterId ? marriage.Partner2Message : marriage.Partner1Message, + }, + Partner2 = new MarriagePartner { + Info = partner1.CharacterId == characterId ? partner1 : partner2, + Message = partner1.CharacterId == characterId ? marriage.Partner1Message : marriage.Partner2Message, + }, + Status = marriage.Status, + }; + } + + public MarriageInfo GetMarriageInfo(long characterId) { + Model.Marriage? model = Context.Marriage.FirstOrDefault(member => member.Partner1Id == characterId || member.Partner2Id == characterId); + if (model == null) { + return new MarriageInfo(); + } + + PlayerInfo? owner = GetPlayerInfo(model.Partner1Id); + PlayerInfo? partner = GetPlayerInfo(model.Partner2Id); + + return owner == null || partner == null ? new MarriageInfo() : new MarriageInfo { + Status = model.Status, + CreationTime = model.CreationTime.ToEpochSeconds(), + Partner1Name = owner.Name, + Partner2Name = partner.Name, + }; + } + + public WeddingHall? CreateWeddingHall(WeddingHall hall, Marriage marriage) { + BeginTransaction(); + + Model.WeddingHall model = hall; + model.MarriageId = marriage.Id; + model.OwnerId = marriage.Partner1.CharacterId; + model.CreationTime = DateTime.Now; + Context.WeddingHall.Add(model); + if (!SaveChanges()) { + return null; + } + + return Commit() ? GetWeddingHall(marriage) : null; + } + + public WeddingHall? GetWeddingHall(Marriage marriage) { + Model.WeddingHall? model = Context.WeddingHall.FirstOrDefault(hall => hall.MarriageId == marriage.Id); + return model?.Convert(marriage); + } + + public WeddingHall? GetWeddingHall(long hallId = 0, long marriageId = 0) { + Model.WeddingHall? model = hallId > 0 ? Context.WeddingHall.Find(hallId) : + Context.WeddingHall.FirstOrDefault(hall => hall.MarriageId == marriageId); + + if (model == null) { + return null; + } + + Marriage? marriage = GetMarriage(weddingId: marriageId); + return marriage == null ? null : model.Convert(marriage); + } + + public bool DeleteWeddingHall(long hallId) { + Model.WeddingHall? hall = Context.WeddingHall.Find(hallId); + if (hall == null) { + return false; + } + + Context.WeddingHall.Remove(hall); + return Context.TrySaveChanges(); + } + + public bool WeddingHallTimeIsAvailable(long ceremonyTime) { + DateTime ceremonyDateTime = ceremonyTime.FromEpochSeconds(); + return Context.WeddingHall.FirstOrDefault(hall => hall.CeremonyTime == ceremonyDateTime) == null; + } + + public IEnumerable GetWeddingHalls() { + List entries = Context.WeddingHall + .Where(hall => hall.CeremonyTime > DateTime.Now) + .AsEnumerable() + .ToList(); + + foreach (Model.WeddingHall entry in entries) { + Marriage? marriage = GetMarriage(weddingId: entry.MarriageId); + if (marriage == null) { + continue; + } + yield return entry.Convert(marriage); + } + } + } +} diff --git a/Maple2.Database/Storage/Game/GameStorage.cs b/Maple2.Database/Storage/Game/GameStorage.cs index cb9ff1471..a3b4d5f02 100644 --- a/Maple2.Database/Storage/Game/GameStorage.cs +++ b/Maple2.Database/Storage/Game/GameStorage.cs @@ -1,78 +1,78 @@ -using Maple2.Database.Context; -using Maple2.Database.Extensions; -using Maple2.Database.Model; -using Maple2.Model.Game; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; - -namespace Maple2.Database.Storage; - -public partial class GameStorage { - private readonly ItemMetadataStorage itemMetadata; - private readonly MapMetadataStorage mapMetadata; - private readonly AchievementMetadataStorage achievementMetadata; - private readonly QuestMetadataStorage questMetadata; - private readonly ServerTableMetadataStorage serverTableMetadata; - private readonly TableMetadataStorage tableMetadata; - private readonly FunctionCubeMetadataStorage functionCubeMetadata; - private readonly ILogger logger; - private readonly DbContextOptions options; - - public GameStorage(DbContextOptions options, ItemMetadataStorage itemMetadata, MapMetadataStorage mapMetadata, AchievementMetadataStorage achievementMetadata, - QuestMetadataStorage questMetadata, TableMetadataStorage tableMetadata, ServerTableMetadataStorage serverTableMetadata, ILogger logger, FunctionCubeMetadataStorage functionCubeMetadata) { - this.options = options; - this.itemMetadata = itemMetadata; - this.mapMetadata = mapMetadata; - this.achievementMetadata = achievementMetadata; - this.questMetadata = questMetadata; - this.logger = logger; - this.functionCubeMetadata = functionCubeMetadata; - this.tableMetadata = tableMetadata; - this.serverTableMetadata = serverTableMetadata; - - var context = new MetadataContext(options); - - // check Ingest has been run - if (!context.Database.CanConnect()) { - throw new Exception("Game database not found, did you run Maple2.File.Ingest?"); - } - } - - public Request Context() { - // We use NoTracking by default since most requests are Read or Overwrite. - // If we need tracking for modifying data, we can set it individually as needed. - var context = new Ms2Context(options); - context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; - - return new Request(this, context, logger); - } - - public partial class Request : DatabaseRequest { - private readonly GameStorage game; - public Request(GameStorage game, Ms2Context context, ILogger logger) : base(context, logger) { - this.game = game; - } - - private static PlayerInfo BuildPlayerInfo(Model.Character character, UgcMap indoor, UgcMap? outdoor, AchievementInfo achievementInfo, long guildId, string guildName, long premiumTime, IList clubs) { - if (outdoor == null) { - return new PlayerInfo(character, indoor.Name, achievementInfo, clubs) { - PremiumTime = premiumTime, - LastOnlineTime = character.LastModified.ToEpochSeconds(), - GuildId = guildId, - GuildName = guildName, - }; - } - - return new PlayerInfo(character, outdoor.Name, achievementInfo, clubs) { - PlotMapId = outdoor.MapId, - PlotNumber = outdoor.Number, - PremiumTime = premiumTime, - ApartmentNumber = outdoor.ApartmentNumber, - PlotExpiryTime = outdoor.ExpiryTime.ToUnixTimeSeconds(), - LastOnlineTime = character.LastModified.ToEpochSeconds(), - GuildId = guildId, - GuildName = guildName, - }; - } - } -} +using Maple2.Database.Context; +using Maple2.Database.Extensions; +using Maple2.Database.Model; +using Maple2.Model.Game; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +namespace Maple2.Database.Storage; + +public partial class GameStorage { + private readonly ItemMetadataStorage itemMetadata; + private readonly MapMetadataStorage mapMetadata; + private readonly AchievementMetadataStorage achievementMetadata; + private readonly QuestMetadataStorage questMetadata; + private readonly ServerTableMetadataStorage serverTableMetadata; + private readonly TableMetadataStorage tableMetadata; + private readonly FunctionCubeMetadataStorage functionCubeMetadata; + private readonly ILogger logger; + private readonly DbContextOptions options; + + public GameStorage(DbContextOptions options, ItemMetadataStorage itemMetadata, MapMetadataStorage mapMetadata, AchievementMetadataStorage achievementMetadata, + QuestMetadataStorage questMetadata, TableMetadataStorage tableMetadata, ServerTableMetadataStorage serverTableMetadata, ILogger logger, FunctionCubeMetadataStorage functionCubeMetadata) { + this.options = options; + this.itemMetadata = itemMetadata; + this.mapMetadata = mapMetadata; + this.achievementMetadata = achievementMetadata; + this.questMetadata = questMetadata; + this.logger = logger; + this.functionCubeMetadata = functionCubeMetadata; + this.tableMetadata = tableMetadata; + this.serverTableMetadata = serverTableMetadata; + + var context = new MetadataContext(options); + + // check Ingest has been run + if (!context.Database.CanConnect()) { + throw new Exception("Game database not found, did you run Maple2.File.Ingest?"); + } + } + + public Request Context() { + // We use NoTracking by default since most requests are Read or Overwrite. + // If we need tracking for modifying data, we can set it individually as needed. + var context = new Ms2Context(options); + context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; + + return new Request(this, context, logger); + } + + public partial class Request : DatabaseRequest { + private readonly GameStorage game; + public Request(GameStorage game, Ms2Context context, ILogger logger) : base(context, logger) { + this.game = game; + } + + private static PlayerInfo BuildPlayerInfo(Model.Character character, UgcMap indoor, UgcMap? outdoor, AchievementInfo achievementInfo, long guildId, string guildName, long premiumTime, IList clubs) { + if (outdoor == null) { + return new PlayerInfo(character, indoor.Name, achievementInfo, clubs) { + PremiumTime = premiumTime, + LastOnlineTime = character.LastModified.ToEpochSeconds(), + GuildId = guildId, + GuildName = guildName, + }; + } + + return new PlayerInfo(character, outdoor.Name, achievementInfo, clubs) { + PlotMapId = outdoor.MapId, + PlotNumber = outdoor.Number, + PremiumTime = premiumTime, + ApartmentNumber = outdoor.ApartmentNumber, + PlotExpiryTime = outdoor.ExpiryTime.ToUnixTimeSeconds(), + LastOnlineTime = character.LastModified.ToEpochSeconds(), + GuildId = guildId, + GuildName = guildName, + }; + } + } +} diff --git a/Maple2.Database/Storage/IPlayerInfoProvider.cs b/Maple2.Database/Storage/IPlayerInfoProvider.cs index 4b70fa1fb..03cb57060 100644 --- a/Maple2.Database/Storage/IPlayerInfoProvider.cs +++ b/Maple2.Database/Storage/IPlayerInfoProvider.cs @@ -1,7 +1,7 @@ -using Maple2.Model.Game; - -namespace Maple2.Database.Storage; - -public interface IPlayerInfoProvider { - public PlayerInfo? GetPlayerInfo(long id); -} +using Maple2.Model.Game; + +namespace Maple2.Database.Storage; + +public interface IPlayerInfoProvider { + public PlayerInfo? GetPlayerInfo(long id); +} diff --git a/Maple2.Database/Storage/Metadata/AchievementMetadataStorage.cs b/Maple2.Database/Storage/Metadata/AchievementMetadataStorage.cs index 768e33d26..ab171aa6d 100644 --- a/Maple2.Database/Storage/Metadata/AchievementMetadataStorage.cs +++ b/Maple2.Database/Storage/Metadata/AchievementMetadataStorage.cs @@ -1,75 +1,75 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Context; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Microsoft.EntityFrameworkCore; - -namespace Maple2.Database.Storage; - -public class AchievementMetadataStorage(MetadataContext context) : MetadataStorage(context, CACHE_SIZE), ISearchable { - private const int CACHE_SIZE = 2500; // ~2.2k total trophies - - private readonly HashSet cachedTypes = []; - - public bool TryGet(int id, [NotNullWhen(true)] out AchievementMetadata? achievement) { - if (Cache.TryGet(id, out achievement)) { - return true; - } - - lock (Context) { - // Double-checked locking - if (Cache.TryGet(id, out achievement)) { - return true; - } - - achievement = Context.AchievementMetadata.Find(id); - - if (achievement == null) { - return false; - } - - Cache.AddReplace(id, achievement); - } - - return true; - } - - public ICollection GetType(ConditionType type) { - lock (Context) { - // If we've already loaded this type, use cached items - if (cachedTypes.Contains(type)) { - return Cache.All().Values - .Where(achievement => achievement.Grades.Values.Any(grade => grade.Condition.Type == type)) - .ToList(); - } - - // Otherwise, query from database and cache all results - List achievements = Context.AchievementMetadata - .AsEnumerable() - .Where(achievement => achievement.Grades.Values.Any(grade => grade.Condition.Type == type)) - .ToList(); - - foreach (AchievementMetadata achievement in achievements) { - Cache.AddReplace(achievement.Id, achievement); - } - - cachedTypes.Add(type); - - return achievements; - } - } - - public ICollection GetAll() { - lock (Context) { - return Context.AchievementMetadata.ToList(); - } - } - - public List Search(string name) { - lock (Context) { - return Context.AchievementMetadata - .Where(achievement => EF.Functions.Like(achievement.Name!, $"%{name}%")) - .ToList(); - } - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Context; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Microsoft.EntityFrameworkCore; + +namespace Maple2.Database.Storage; + +public class AchievementMetadataStorage(MetadataContext context) : MetadataStorage(context, CACHE_SIZE), ISearchable { + private const int CACHE_SIZE = 2500; // ~2.2k total trophies + + private readonly HashSet cachedTypes = []; + + public bool TryGet(int id, [NotNullWhen(true)] out AchievementMetadata? achievement) { + if (Cache.TryGet(id, out achievement)) { + return true; + } + + lock (Context) { + // Double-checked locking + if (Cache.TryGet(id, out achievement)) { + return true; + } + + achievement = Context.AchievementMetadata.Find(id); + + if (achievement == null) { + return false; + } + + Cache.AddReplace(id, achievement); + } + + return true; + } + + public ICollection GetType(ConditionType type) { + lock (Context) { + // If we've already loaded this type, use cached items + if (cachedTypes.Contains(type)) { + return Cache.All().Values + .Where(achievement => achievement.Grades.Values.Any(grade => grade.Condition.Type == type)) + .ToList(); + } + + // Otherwise, query from database and cache all results + List achievements = Context.AchievementMetadata + .AsEnumerable() + .Where(achievement => achievement.Grades.Values.Any(grade => grade.Condition.Type == type)) + .ToList(); + + foreach (AchievementMetadata achievement in achievements) { + Cache.AddReplace(achievement.Id, achievement); + } + + cachedTypes.Add(type); + + return achievements; + } + } + + public ICollection GetAll() { + lock (Context) { + return Context.AchievementMetadata.ToList(); + } + } + + public List Search(string name) { + lock (Context) { + return Context.AchievementMetadata + .Where(achievement => EF.Functions.Like(achievement.Name!, $"%{name}%")) + .ToList(); + } + } +} diff --git a/Maple2.Database/Storage/Metadata/AiMetadataStorage.cs b/Maple2.Database/Storage/Metadata/AiMetadataStorage.cs index f5f4a87ad..e4877c19a 100644 --- a/Maple2.Database/Storage/Metadata/AiMetadataStorage.cs +++ b/Maple2.Database/Storage/Metadata/AiMetadataStorage.cs @@ -1,38 +1,38 @@ -using Maple2.Database.Context; -using Maple2.Model.Metadata; -using System.Diagnostics.CodeAnalysis; - -namespace Maple2.Database.Storage; - -public class AiMetadataStorage(MetadataContext context) : MetadataStorage(context, CACHE_SIZE) { - private const int CACHE_SIZE = 2500; // ~2.2k total items - - public bool TryGet(string name, [NotNullWhen(true)] out AiMetadata? npcAi) { - if (Cache.TryGet(name, out npcAi)) { - return true; - } - - lock (Context) { - // Double-checked locking - if (Cache.TryGet(name, out npcAi)) { - return true; - } - - npcAi = Context.AiMetadata.Find(name); - - if (npcAi == null) { - return false; - } - - Cache.AddReplace(name, npcAi); - } - - return true; - } - - public IEnumerable GetAis() { - lock (Context) { - return Context.AiMetadata.ToList(); - } - } -} +using Maple2.Database.Context; +using Maple2.Model.Metadata; +using System.Diagnostics.CodeAnalysis; + +namespace Maple2.Database.Storage; + +public class AiMetadataStorage(MetadataContext context) : MetadataStorage(context, CACHE_SIZE) { + private const int CACHE_SIZE = 2500; // ~2.2k total items + + public bool TryGet(string name, [NotNullWhen(true)] out AiMetadata? npcAi) { + if (Cache.TryGet(name, out npcAi)) { + return true; + } + + lock (Context) { + // Double-checked locking + if (Cache.TryGet(name, out npcAi)) { + return true; + } + + npcAi = Context.AiMetadata.Find(name); + + if (npcAi == null) { + return false; + } + + Cache.AddReplace(name, npcAi); + } + + return true; + } + + public IEnumerable GetAis() { + lock (Context) { + return Context.AiMetadata.ToList(); + } + } +} diff --git a/Maple2.Database/Storage/Metadata/FunctionCubeMetadataStorage.cs b/Maple2.Database/Storage/Metadata/FunctionCubeMetadataStorage.cs index df7d1e7cb..b4432a8df 100644 --- a/Maple2.Database/Storage/Metadata/FunctionCubeMetadataStorage.cs +++ b/Maple2.Database/Storage/Metadata/FunctionCubeMetadataStorage.cs @@ -1,21 +1,21 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Context; -using Maple2.Model.Metadata; - -namespace Maple2.Database.Storage; - -public class FunctionCubeMetadataStorage : MetadataStorage { - protected readonly Dictionary FunctionCubeMetadata; - - public FunctionCubeMetadataStorage(MetadataContext context) : base(context, capacity: 500) { - FunctionCubeMetadata = new Dictionary(); - - foreach (FunctionCubeMetadata functionCube in context.FunctionCubeMetadata) { - FunctionCubeMetadata.Add(functionCube.Id, functionCube); - } - } - - public bool TryGet(int id, [NotNullWhen(true)] out FunctionCubeMetadata? functionCube) { - return FunctionCubeMetadata.TryGetValue(id, out functionCube); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Context; +using Maple2.Model.Metadata; + +namespace Maple2.Database.Storage; + +public class FunctionCubeMetadataStorage : MetadataStorage { + protected readonly Dictionary FunctionCubeMetadata; + + public FunctionCubeMetadataStorage(MetadataContext context) : base(context, capacity: 500) { + FunctionCubeMetadata = new Dictionary(); + + foreach (FunctionCubeMetadata functionCube in context.FunctionCubeMetadata) { + FunctionCubeMetadata.Add(functionCube.Id, functionCube); + } + } + + public bool TryGet(int id, [NotNullWhen(true)] out FunctionCubeMetadata? functionCube) { + return FunctionCubeMetadata.TryGetValue(id, out functionCube); + } +} diff --git a/Maple2.Database/Storage/Metadata/ItemMetadataStorage.cs b/Maple2.Database/Storage/Metadata/ItemMetadataStorage.cs index b11188d68..b29907bc8 100644 --- a/Maple2.Database/Storage/Metadata/ItemMetadataStorage.cs +++ b/Maple2.Database/Storage/Metadata/ItemMetadataStorage.cs @@ -1,86 +1,86 @@ -using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Context; -using Maple2.Model.Metadata; -using Maple2.Tools; -using Microsoft.EntityFrameworkCore; - -namespace Maple2.Database.Storage; - -public class ItemMetadataStorage : MetadataStorage, ISearchable { - private const int CACHE_SIZE = 40000; // ~34k total items - - private readonly ConcurrentDictionary petToItem = new(); - private readonly ConcurrentMultiDictionary petLookup = new(); - - public ItemMetadataStorage(MetadataContext context) : base(context, CACHE_SIZE) { - IndexPets(); - - foreach (PetMetadata metadata in Context.PetMetadata) { - petLookup.TryAdd(metadata.Id, metadata.NpcId, metadata); - } - } - - public bool TryGet(int id, [NotNullWhen(true)] out ItemMetadata? item) { - if (Cache.TryGet(id, out item)) { - return true; - } - - lock (Context) { - // Double-checked locking - if (Cache.TryGet(id, out item)) { - return true; - } - - item = Context.ItemMetadata.Find(id); - - if (item == null) { - return false; - } - - Cache.AddReplace(id, item); - } - - return true; - } - - public bool TryGetPet(int petId, [NotNullWhen(true)] out ItemMetadata? item) { - return petToItem.TryGetValue(petId, out item); - } - - public bool TryGetPet(int petId, [NotNullWhen(true)] out PetMetadata? pet) { - return petLookup.TryGetKey1(petId, out pet); - } - - public bool TryGetPetByNpcId(int npcId, [NotNullWhen(true)] out PetMetadata? pet) { - return petLookup.TryGetKey2(npcId, out pet); - } - - public override void InvalidateCache() { - base.InvalidateCache(); - IndexPets(); - } - - public List Search(string name) { - lock (Context) { - return Context.ItemMetadata - .Where(item => EF.Functions.Like(item.Name!, $"%{name}%")) - .ToList(); - } - } - - private void IndexPets() { - petToItem.Clear(); - - lock (Context) { - List result = Context.ItemMetadata - .FromSql($"SELECT * FROM `item` WHERE JSON_EXTRACT(Property, '$.PetId') > 0") - .ToList(); - - foreach (ItemMetadata item in result) { - Cache.AddReplace(item.Id, item); - petToItem[item.Property.PetId] = item; - } - } - } -} +using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Context; +using Maple2.Model.Metadata; +using Maple2.Tools; +using Microsoft.EntityFrameworkCore; + +namespace Maple2.Database.Storage; + +public class ItemMetadataStorage : MetadataStorage, ISearchable { + private const int CACHE_SIZE = 40000; // ~34k total items + + private readonly ConcurrentDictionary petToItem = new(); + private readonly ConcurrentMultiDictionary petLookup = new(); + + public ItemMetadataStorage(MetadataContext context) : base(context, CACHE_SIZE) { + IndexPets(); + + foreach (PetMetadata metadata in Context.PetMetadata) { + petLookup.TryAdd(metadata.Id, metadata.NpcId, metadata); + } + } + + public bool TryGet(int id, [NotNullWhen(true)] out ItemMetadata? item) { + if (Cache.TryGet(id, out item)) { + return true; + } + + lock (Context) { + // Double-checked locking + if (Cache.TryGet(id, out item)) { + return true; + } + + item = Context.ItemMetadata.Find(id); + + if (item == null) { + return false; + } + + Cache.AddReplace(id, item); + } + + return true; + } + + public bool TryGetPet(int petId, [NotNullWhen(true)] out ItemMetadata? item) { + return petToItem.TryGetValue(petId, out item); + } + + public bool TryGetPet(int petId, [NotNullWhen(true)] out PetMetadata? pet) { + return petLookup.TryGetKey1(petId, out pet); + } + + public bool TryGetPetByNpcId(int npcId, [NotNullWhen(true)] out PetMetadata? pet) { + return petLookup.TryGetKey2(npcId, out pet); + } + + public override void InvalidateCache() { + base.InvalidateCache(); + IndexPets(); + } + + public List Search(string name) { + lock (Context) { + return Context.ItemMetadata + .Where(item => EF.Functions.Like(item.Name!, $"%{name}%")) + .ToList(); + } + } + + private void IndexPets() { + petToItem.Clear(); + + lock (Context) { + List result = Context.ItemMetadata + .FromSql($"SELECT * FROM `item` WHERE JSON_EXTRACT(Property, '$.PetId') > 0") + .ToList(); + + foreach (ItemMetadata item in result) { + Cache.AddReplace(item.Id, item); + petToItem[item.Property.PetId] = item; + } + } + } +} diff --git a/Maple2.Database/Storage/Metadata/MapDataStorage.cs b/Maple2.Database/Storage/Metadata/MapDataStorage.cs index 65f225097..6161ff679 100644 --- a/Maple2.Database/Storage/Metadata/MapDataStorage.cs +++ b/Maple2.Database/Storage/Metadata/MapDataStorage.cs @@ -1,47 +1,47 @@ -using System.Diagnostics.CodeAnalysis; -using System.IO.Compression; -using Maple2.Database.Context; -using Maple2.Model.Game.Field; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Database.Storage; - -public class MapDataStorage(MetadataContext context) : MetadataStorage(context, CACHE_SIZE) { - private const int CACHE_SIZE = 1500; // ~1.1k total Maps - - public bool TryGet(string xblock, [NotNullWhen(true)] out FieldAccelerationStructure? mapData) { - if (Cache.TryGet(xblock, out mapData)) { - return true; - } - - lock (Context) { - // Double-checked locking - if (Cache.TryGet(xblock, out mapData)) { - return true; - } - - MapDataMetadata? data = Context.MapDataMetadata.Find(xblock); - - if (data == null) { - return false; - } - - MemoryStream input = new MemoryStream(data.Data); - MemoryStream output = new MemoryStream(); - - using (DeflateStream dstream = new DeflateStream(input, CompressionMode.Decompress)) { - dstream.CopyTo(output); - } - - ByteReader reader = new ByteReader(output.ToArray()); - - mapData = reader.ReadClassWithNew(); - - Cache.AddReplace(xblock, mapData); - } - - return true; - } -} +using System.Diagnostics.CodeAnalysis; +using System.IO.Compression; +using Maple2.Database.Context; +using Maple2.Model.Game.Field; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Database.Storage; + +public class MapDataStorage(MetadataContext context) : MetadataStorage(context, CACHE_SIZE) { + private const int CACHE_SIZE = 1500; // ~1.1k total Maps + + public bool TryGet(string xblock, [NotNullWhen(true)] out FieldAccelerationStructure? mapData) { + if (Cache.TryGet(xblock, out mapData)) { + return true; + } + + lock (Context) { + // Double-checked locking + if (Cache.TryGet(xblock, out mapData)) { + return true; + } + + MapDataMetadata? data = Context.MapDataMetadata.Find(xblock); + + if (data == null) { + return false; + } + + MemoryStream input = new MemoryStream(data.Data); + MemoryStream output = new MemoryStream(); + + using (DeflateStream dstream = new DeflateStream(input, CompressionMode.Decompress)) { + dstream.CopyTo(output); + } + + ByteReader reader = new ByteReader(output.ToArray()); + + mapData = reader.ReadClassWithNew(); + + Cache.AddReplace(xblock, mapData); + } + + return true; + } +} diff --git a/Maple2.Database/Storage/Metadata/MapEntityStorage.cs b/Maple2.Database/Storage/Metadata/MapEntityStorage.cs index 19ff31cc9..2290c0481 100644 --- a/Maple2.Database/Storage/Metadata/MapEntityStorage.cs +++ b/Maple2.Database/Storage/Metadata/MapEntityStorage.cs @@ -1,145 +1,145 @@ -using System.Diagnostics; -using System.Numerics; -using Maple2.Database.Context; -using Maple2.Model.Common; -using Maple2.Model.Metadata; -using Maple2.Tools.Collision; - -namespace Maple2.Database.Storage; - -public class MapEntityStorage(MetadataContext context) : MetadataStorage(context, CACHE_SIZE) { - private const int CACHE_SIZE = 1500; // ~1.1k total Maps - - private const float MAP_LIMIT = sbyte.MaxValue * 150f; - private static readonly Prism LargeBoundingBox = new(new BoundingBox( - new Vector2(-MAP_LIMIT, -MAP_LIMIT), - new Vector2(MAP_LIMIT, MAP_LIMIT)), - -MAP_LIMIT, MAP_LIMIT * 2); - - public MapEntityMetadata? Get(string xblock) { - if (Cache.TryGet(xblock, out MapEntityMetadata mapEntity)) { - return mapEntity; - } - - lock (Context) { - // Double-checked locking - if (Cache.TryGet(xblock, out mapEntity)) { - return mapEntity; - } - - var breakables = new Dictionary(); - var liftables = new Dictionary(); - var liftableTargetBoxes = new Dictionary(); - var objectWeapons = new Dictionary(); - var portals = new Dictionary(); - var playerSpawns = new Dictionary(); - var npcSpawns = new List(); - var regionSpawns = new Dictionary(); - var regionSkills = new List(); - var eventNpcSpawns = new Dictionary(); - var eventItemSpawns = new Dictionary(); - TaxiStation? taxi = null; - Prism? bounding = null; - var breakableActors = new Dictionary(); - var interacts = new Dictionary(); - var triggerModels = new Dictionary(); - var triggers = new List(); - var patrols = new List(); - foreach (MapEntity entity in Context.MapEntity.Where(entity => entity.XBlock == xblock)) { - switch (entity.Block) { - case Breakable breakable: - breakables[entity.Guid] = breakable; - break; - case BreakableActor breakableActor: - breakableActors[entity.Guid] = breakableActor; - break; - case Liftable liftable: - liftables[entity.Guid] = liftable; - break; - case LiftableTargetBox liftableTargetBox: - liftableTargetBoxes[liftableTargetBox.Position] = liftableTargetBox; - break; - case ObjectWeapon objectWeapon: - objectWeapons[objectWeapon.Position] = objectWeapon; - break; - case Portal portal: - portals[portal.Id] = portal; - break; - case Ms2RegionSpawn regionSpawn: - regionSpawns[regionSpawn.Id] = regionSpawn; - break; - case Ms2RegionSkill regionSkill: - regionSkills.Add(regionSkill); - break; - case SpawnPointPC playerSpawn: - playerSpawns[playerSpawn.SpawnPointId] = playerSpawn; - break; - case SpawnPointNPC npcSpawn: - if (npcSpawn is EventSpawnPointNPC eventNpcSpawn) { - eventNpcSpawns.Add(eventNpcSpawn.SpawnPointId, eventNpcSpawn); - } else { - npcSpawns.Add(npcSpawn); - } - break; - case EventSpawnPointItem eventItemSpawn: - eventItemSpawns.Add(eventItemSpawn.SpawnPointId, eventItemSpawn); - break; - case TaxiStation taxiStation: - Debug.Assert(taxi == null, $"Multiple taxi stations found in xblock:{xblock}"); - taxi = taxiStation; - break; - case TriggerModel triggerModel: - triggerModels.Add(triggerModel.Id, triggerModel); - break; - case Ms2InteractActor or Ms2InteractDisplay or Ms2InteractMesh or Ms2SimpleUiObject or Ms2Telescope: - interacts.Add(entity.Guid, (InteractObject) entity.Block); - break; - case Ms2TriggerActor or Ms2TriggerAgent or Ms2TriggerBox or Ms2TriggerCamera or Ms2TriggerCube or Ms2TriggerEffect or - Ms2TriggerLadder or Ms2TriggerMesh or Ms2TriggerRope or Ms2TriggerSkill or Ms2TriggerSound: - triggers.Add((Ms2Trigger) entity.Block); - break; - case Ms2Bounding mapBounding: - var box = new BoundingBox( - new Vector2(mapBounding.Position1.X, mapBounding.Position1.Y), - new Vector2(mapBounding.Position2.X, mapBounding.Position2.Y) - ); - float baseHeight = Math.Min(mapBounding.Position1.Z, mapBounding.Position2.Z); - float height = Math.Abs(mapBounding.Position2.Z - mapBounding.Position1.Z); - bounding = new Prism(box, baseHeight, height); - break; - case MS2PatrolData patrol: - patrols.Add(patrol); - break; - } - } - - mapEntity = new MapEntityMetadata { - Breakables = breakables, - Liftables = liftables, - LiftableTargetBoxes = liftableTargetBoxes, - ObjectWeapons = objectWeapons, - Portals = portals, - PlayerSpawns = playerSpawns, - NpcSpawns = npcSpawns, - EventNpcSpawns = eventNpcSpawns, - EventItemSpawns = eventItemSpawns, - RegionSpawns = regionSpawns, - RegionSkills = regionSkills, - Taxi = taxi, - BoundingBox = bounding ?? LargeBoundingBox, - BreakableActors = breakableActors, - Interacts = interacts, - TriggerModels = triggerModels, - Trigger = new TriggerStorage(triggers), - Patrols = patrols, - }; - Cache.AddReplace(xblock, mapEntity); - } - - return mapEntity; - } - - public bool Contains(string xblock) { - return Get(xblock) != null; - } -} +using System.Diagnostics; +using System.Numerics; +using Maple2.Database.Context; +using Maple2.Model.Common; +using Maple2.Model.Metadata; +using Maple2.Tools.Collision; + +namespace Maple2.Database.Storage; + +public class MapEntityStorage(MetadataContext context) : MetadataStorage(context, CACHE_SIZE) { + private const int CACHE_SIZE = 1500; // ~1.1k total Maps + + private const float MAP_LIMIT = sbyte.MaxValue * 150f; + private static readonly Prism LargeBoundingBox = new(new BoundingBox( + new Vector2(-MAP_LIMIT, -MAP_LIMIT), + new Vector2(MAP_LIMIT, MAP_LIMIT)), + -MAP_LIMIT, MAP_LIMIT * 2); + + public MapEntityMetadata? Get(string xblock) { + if (Cache.TryGet(xblock, out MapEntityMetadata mapEntity)) { + return mapEntity; + } + + lock (Context) { + // Double-checked locking + if (Cache.TryGet(xblock, out mapEntity)) { + return mapEntity; + } + + var breakables = new Dictionary(); + var liftables = new Dictionary(); + var liftableTargetBoxes = new Dictionary(); + var objectWeapons = new Dictionary(); + var portals = new Dictionary(); + var playerSpawns = new Dictionary(); + var npcSpawns = new List(); + var regionSpawns = new Dictionary(); + var regionSkills = new List(); + var eventNpcSpawns = new Dictionary(); + var eventItemSpawns = new Dictionary(); + TaxiStation? taxi = null; + Prism? bounding = null; + var breakableActors = new Dictionary(); + var interacts = new Dictionary(); + var triggerModels = new Dictionary(); + var triggers = new List(); + var patrols = new List(); + foreach (MapEntity entity in Context.MapEntity.Where(entity => entity.XBlock == xblock)) { + switch (entity.Block) { + case Breakable breakable: + breakables[entity.Guid] = breakable; + break; + case BreakableActor breakableActor: + breakableActors[entity.Guid] = breakableActor; + break; + case Liftable liftable: + liftables[entity.Guid] = liftable; + break; + case LiftableTargetBox liftableTargetBox: + liftableTargetBoxes[liftableTargetBox.Position] = liftableTargetBox; + break; + case ObjectWeapon objectWeapon: + objectWeapons[objectWeapon.Position] = objectWeapon; + break; + case Portal portal: + portals[portal.Id] = portal; + break; + case Ms2RegionSpawn regionSpawn: + regionSpawns[regionSpawn.Id] = regionSpawn; + break; + case Ms2RegionSkill regionSkill: + regionSkills.Add(regionSkill); + break; + case SpawnPointPC playerSpawn: + playerSpawns[playerSpawn.SpawnPointId] = playerSpawn; + break; + case SpawnPointNPC npcSpawn: + if (npcSpawn is EventSpawnPointNPC eventNpcSpawn) { + eventNpcSpawns.Add(eventNpcSpawn.SpawnPointId, eventNpcSpawn); + } else { + npcSpawns.Add(npcSpawn); + } + break; + case EventSpawnPointItem eventItemSpawn: + eventItemSpawns.Add(eventItemSpawn.SpawnPointId, eventItemSpawn); + break; + case TaxiStation taxiStation: + Debug.Assert(taxi == null, $"Multiple taxi stations found in xblock:{xblock}"); + taxi = taxiStation; + break; + case TriggerModel triggerModel: + triggerModels.Add(triggerModel.Id, triggerModel); + break; + case Ms2InteractActor or Ms2InteractDisplay or Ms2InteractMesh or Ms2SimpleUiObject or Ms2Telescope: + interacts.Add(entity.Guid, (InteractObject) entity.Block); + break; + case Ms2TriggerActor or Ms2TriggerAgent or Ms2TriggerBox or Ms2TriggerCamera or Ms2TriggerCube or Ms2TriggerEffect or + Ms2TriggerLadder or Ms2TriggerMesh or Ms2TriggerRope or Ms2TriggerSkill or Ms2TriggerSound: + triggers.Add((Ms2Trigger) entity.Block); + break; + case Ms2Bounding mapBounding: + var box = new BoundingBox( + new Vector2(mapBounding.Position1.X, mapBounding.Position1.Y), + new Vector2(mapBounding.Position2.X, mapBounding.Position2.Y) + ); + float baseHeight = Math.Min(mapBounding.Position1.Z, mapBounding.Position2.Z); + float height = Math.Abs(mapBounding.Position2.Z - mapBounding.Position1.Z); + bounding = new Prism(box, baseHeight, height); + break; + case MS2PatrolData patrol: + patrols.Add(patrol); + break; + } + } + + mapEntity = new MapEntityMetadata { + Breakables = breakables, + Liftables = liftables, + LiftableTargetBoxes = liftableTargetBoxes, + ObjectWeapons = objectWeapons, + Portals = portals, + PlayerSpawns = playerSpawns, + NpcSpawns = npcSpawns, + EventNpcSpawns = eventNpcSpawns, + EventItemSpawns = eventItemSpawns, + RegionSpawns = regionSpawns, + RegionSkills = regionSkills, + Taxi = taxi, + BoundingBox = bounding ?? LargeBoundingBox, + BreakableActors = breakableActors, + Interacts = interacts, + TriggerModels = triggerModels, + Trigger = new TriggerStorage(triggers), + Patrols = patrols, + }; + Cache.AddReplace(xblock, mapEntity); + } + + return mapEntity; + } + + public bool Contains(string xblock) { + return Get(xblock) != null; + } +} diff --git a/Maple2.Database/Storage/Metadata/MapMetadataStorage.cs b/Maple2.Database/Storage/Metadata/MapMetadataStorage.cs index 4a5d3575b..4b3547f55 100644 --- a/Maple2.Database/Storage/Metadata/MapMetadataStorage.cs +++ b/Maple2.Database/Storage/Metadata/MapMetadataStorage.cs @@ -1,93 +1,93 @@ -using System.Diagnostics.CodeAnalysis; -using Caching; -using Maple2.Database.Context; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Microsoft.EntityFrameworkCore; - -namespace Maple2.Database.Storage; - -public class MapMetadataStorage : MetadataStorage, ISearchable { - private const int CACHE_SIZE = 1500; // ~1.1k total Maps - private const int UGC_CACHE_SIZE = 200; - - protected readonly LRUCache UgcCache; - protected readonly Dictionary ExportedUgcCache; // only 17 entries - - public MapMetadataStorage(MetadataContext context) : base(context, CACHE_SIZE) { - UgcCache = new LRUCache(UGC_CACHE_SIZE, (int) (UGC_CACHE_SIZE * 0.05)); - ExportedUgcCache = []; - - foreach (ExportedUgcMapMetadata exportedUgcMap in context.ExportedUgcMapMetadata) { - ExportedUgcCache.Add(exportedUgcMap.Id, exportedUgcMap); - } - } - - public bool TryGet(int id, [NotNullWhen(true)] out MapMetadata? map) { - if (Cache.TryGet(id, out map)) { - return true; - } - - lock (Context) { - // Double-checked locking - if (Cache.TryGet(id, out map)) { - return true; - } - - map = Context.MapMetadata.Find(id); - - if (map == null) { - return false; - } - - Cache.AddReplace(id, map); - } - - return true; - } - - public bool TryGetUgc(int id, [NotNullWhen(true)] out UgcMapMetadata? map) { - if (UgcCache.TryGet(id, out map)) { - return true; - } - - lock (Context) { - map = Context.UgcMapMetadata.Find(id); - } - - if (map == null) { - return false; - } - - UgcCache.AddReplace(id, map); - return true; - } - - public IList GetMapsByType(Continent continent, MapType mapType) { - lock (Context) { - return Context.MapMetadata.FromSqlRaw($"SELECT * FROM `map` WHERE JSON_EXTRACT(Property, '$.Type')={(int) mapType} AND JSON_EXTRACT(Property, '$.Continent')={(int) continent}") - .ToList(); - } - } - - public IEnumerable GetAllUgc() { - lock (Context) { - foreach (UgcMapMetadata map in Context.UgcMapMetadata) { - UgcCache.AddReplace(map.Id, map); - yield return map; - } - } - } - - public List Search(string name) { - lock (Context) { - return Context.MapMetadata - .Where(map => EF.Functions.Like(map.Name!, $"%{name}%")) - .ToList(); - } - } - - public bool TryGetExportedUgc(string id, [NotNullWhen(true)] out ExportedUgcMapMetadata? map) { - return ExportedUgcCache.TryGetValue(id, out map); - } -} +using System.Diagnostics.CodeAnalysis; +using Caching; +using Maple2.Database.Context; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Microsoft.EntityFrameworkCore; + +namespace Maple2.Database.Storage; + +public class MapMetadataStorage : MetadataStorage, ISearchable { + private const int CACHE_SIZE = 1500; // ~1.1k total Maps + private const int UGC_CACHE_SIZE = 200; + + protected readonly LRUCache UgcCache; + protected readonly Dictionary ExportedUgcCache; // only 17 entries + + public MapMetadataStorage(MetadataContext context) : base(context, CACHE_SIZE) { + UgcCache = new LRUCache(UGC_CACHE_SIZE, (int) (UGC_CACHE_SIZE * 0.05)); + ExportedUgcCache = []; + + foreach (ExportedUgcMapMetadata exportedUgcMap in context.ExportedUgcMapMetadata) { + ExportedUgcCache.Add(exportedUgcMap.Id, exportedUgcMap); + } + } + + public bool TryGet(int id, [NotNullWhen(true)] out MapMetadata? map) { + if (Cache.TryGet(id, out map)) { + return true; + } + + lock (Context) { + // Double-checked locking + if (Cache.TryGet(id, out map)) { + return true; + } + + map = Context.MapMetadata.Find(id); + + if (map == null) { + return false; + } + + Cache.AddReplace(id, map); + } + + return true; + } + + public bool TryGetUgc(int id, [NotNullWhen(true)] out UgcMapMetadata? map) { + if (UgcCache.TryGet(id, out map)) { + return true; + } + + lock (Context) { + map = Context.UgcMapMetadata.Find(id); + } + + if (map == null) { + return false; + } + + UgcCache.AddReplace(id, map); + return true; + } + + public IList GetMapsByType(Continent continent, MapType mapType) { + lock (Context) { + return Context.MapMetadata.FromSqlRaw($"SELECT * FROM `map` WHERE JSON_EXTRACT(Property, '$.Type')={(int) mapType} AND JSON_EXTRACT(Property, '$.Continent')={(int) continent}") + .ToList(); + } + } + + public IEnumerable GetAllUgc() { + lock (Context) { + foreach (UgcMapMetadata map in Context.UgcMapMetadata) { + UgcCache.AddReplace(map.Id, map); + yield return map; + } + } + } + + public List Search(string name) { + lock (Context) { + return Context.MapMetadata + .Where(map => EF.Functions.Like(map.Name!, $"%{name}%")) + .ToList(); + } + } + + public bool TryGetExportedUgc(string id, [NotNullWhen(true)] out ExportedUgcMapMetadata? map) { + return ExportedUgcCache.TryGetValue(id, out map); + } +} diff --git a/Maple2.Database/Storage/Metadata/MetadataStorage.cs b/Maple2.Database/Storage/Metadata/MetadataStorage.cs index dd7bd00b7..1e4b0b9da 100644 --- a/Maple2.Database/Storage/Metadata/MetadataStorage.cs +++ b/Maple2.Database/Storage/Metadata/MetadataStorage.cs @@ -1,23 +1,23 @@ -using Caching; -using Maple2.Database.Context; -using Maple2.Model.Metadata; - -namespace Maple2.Database.Storage; - -public abstract class MetadataStorage { - protected readonly MetadataContext Context; - protected readonly LRUCache Cache; - - protected MetadataStorage(MetadataContext context, int capacity) { - Context = context; - Cache = new LRUCache(capacity, (int) (capacity * 0.05)); - } - - public virtual void InvalidateCache() { - Cache.Clear(); - } -} - -public interface ISearchable where T : ISearchResult { - public List Search(string name); -} +using Caching; +using Maple2.Database.Context; +using Maple2.Model.Metadata; + +namespace Maple2.Database.Storage; + +public abstract class MetadataStorage { + protected readonly MetadataContext Context; + protected readonly LRUCache Cache; + + protected MetadataStorage(MetadataContext context, int capacity) { + Context = context; + Cache = new LRUCache(capacity, (int) (capacity * 0.05)); + } + + public virtual void InvalidateCache() { + Cache.Clear(); + } +} + +public interface ISearchable where T : ISearchResult { + public List Search(string name); +} diff --git a/Maple2.Database/Storage/Metadata/NpcMetadataStorage.cs b/Maple2.Database/Storage/Metadata/NpcMetadataStorage.cs index 24006d640..b4a6fc833 100644 --- a/Maple2.Database/Storage/Metadata/NpcMetadataStorage.cs +++ b/Maple2.Database/Storage/Metadata/NpcMetadataStorage.cs @@ -1,88 +1,88 @@ -using System.Diagnostics.CodeAnalysis; -using Caching; -using Maple2.Database.Context; -using Maple2.Model.Metadata; -using Microsoft.EntityFrameworkCore; - -namespace Maple2.Database.Storage; - -public class NpcMetadataStorage : MetadataStorage, ISearchable { - private const int CACHE_SIZE = 7500; // ~7.4k total npcs - private const int ANI_CACHE_SIZE = 2500; - private const int MOB_BASE_ID = 20000000; // Starting id for mobs - - private readonly Dictionary> tagLookup; - protected readonly LRUCache AniCache; - - public NpcMetadataStorage(MetadataContext context) : base(context, CACHE_SIZE) { - tagLookup = new Dictionary>(); - AniCache = new LRUCache(ANI_CACHE_SIZE, (int) (ANI_CACHE_SIZE * 0.05)); - - foreach (NpcMetadata npc in Context.NpcMetadata.Where(npc => npc.Id > MOB_BASE_ID)) { - Cache.AddReplace(npc.Id, npc); - foreach (string tag in npc.Basic.MainTags) { - if (!tagLookup.ContainsKey(tag)) { - tagLookup[tag] = []; - } - - tagLookup[tag].Add(npc.Id); - } - } - } - - public bool TryGet(int id, [NotNullWhen(true)] out NpcMetadata? npc) { - if (Cache.TryGet(id, out npc)) { - return true; - } - - lock (Context) { - // Double-checked locking - if (Cache.TryGet(id, out npc)) { - return true; - } - - npc = Context.NpcMetadata.Find(id); - - if (npc == null) { - return false; - } - - Cache.AddReplace(id, npc); - } - - return true; - } - - public bool TryLookupTag(string tag, [NotNullWhen(true)] out IReadOnlyCollection? npcIds) { - bool result = tagLookup.TryGetValue(tag, out HashSet? set); - npcIds = set; - - return result; - } - - public List Search(string name) { - lock (Context) { - return Context.NpcMetadata - .Where(npc => EF.Functions.Like(npc.Name!, $"%{name}%")) - .ToList(); - } - } - - public AnimationMetadata? GetAnimation(string model) { - if (AniCache.TryGet(model, out AnimationMetadata? animation)) { - return animation; - } - - lock (Context) { - animation = Context.AnimationMetadata.Find(model); - } - - if (animation == null) { - return null; - } - - AniCache.AddReplace(model, animation); - - return animation; - } -} +using System.Diagnostics.CodeAnalysis; +using Caching; +using Maple2.Database.Context; +using Maple2.Model.Metadata; +using Microsoft.EntityFrameworkCore; + +namespace Maple2.Database.Storage; + +public class NpcMetadataStorage : MetadataStorage, ISearchable { + private const int CACHE_SIZE = 7500; // ~7.4k total npcs + private const int ANI_CACHE_SIZE = 2500; + private const int MOB_BASE_ID = 20000000; // Starting id for mobs + + private readonly Dictionary> tagLookup; + protected readonly LRUCache AniCache; + + public NpcMetadataStorage(MetadataContext context) : base(context, CACHE_SIZE) { + tagLookup = new Dictionary>(); + AniCache = new LRUCache(ANI_CACHE_SIZE, (int) (ANI_CACHE_SIZE * 0.05)); + + foreach (NpcMetadata npc in Context.NpcMetadata.Where(npc => npc.Id > MOB_BASE_ID)) { + Cache.AddReplace(npc.Id, npc); + foreach (string tag in npc.Basic.MainTags) { + if (!tagLookup.ContainsKey(tag)) { + tagLookup[tag] = []; + } + + tagLookup[tag].Add(npc.Id); + } + } + } + + public bool TryGet(int id, [NotNullWhen(true)] out NpcMetadata? npc) { + if (Cache.TryGet(id, out npc)) { + return true; + } + + lock (Context) { + // Double-checked locking + if (Cache.TryGet(id, out npc)) { + return true; + } + + npc = Context.NpcMetadata.Find(id); + + if (npc == null) { + return false; + } + + Cache.AddReplace(id, npc); + } + + return true; + } + + public bool TryLookupTag(string tag, [NotNullWhen(true)] out IReadOnlyCollection? npcIds) { + bool result = tagLookup.TryGetValue(tag, out HashSet? set); + npcIds = set; + + return result; + } + + public List Search(string name) { + lock (Context) { + return Context.NpcMetadata + .Where(npc => EF.Functions.Like(npc.Name!, $"%{name}%")) + .ToList(); + } + } + + public AnimationMetadata? GetAnimation(string model) { + if (AniCache.TryGet(model, out AnimationMetadata? animation)) { + return animation; + } + + lock (Context) { + animation = Context.AnimationMetadata.Find(model); + } + + if (animation == null) { + return null; + } + + AniCache.AddReplace(model, animation); + + return animation; + } +} diff --git a/Maple2.Database/Storage/Metadata/QuestMetadataStorage.cs b/Maple2.Database/Storage/Metadata/QuestMetadataStorage.cs index 37a3c58cc..ab056608f 100644 --- a/Maple2.Database/Storage/Metadata/QuestMetadataStorage.cs +++ b/Maple2.Database/Storage/Metadata/QuestMetadataStorage.cs @@ -1,71 +1,71 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Context; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; - -namespace Maple2.Database.Storage; - -public class QuestMetadataStorage(MetadataContext context) : MetadataStorage(context, CACHE_SIZE), ISearchable { - private const int CACHE_SIZE = 8000; // 4.903 quests are in the database with NA feature, 7.2k quests in xmls - private bool isInitialized; - - public bool TryGet(int id, [NotNullWhen(true)] out QuestMetadata? quest) { - if (Cache.TryGet(id, out quest)) { - return true; - } - - lock (Context) { - // Double-checked locking - if (Cache.TryGet(id, out quest)) { - return true; - } - - quest = Context.QuestMetadata.Find(id); - - if (quest is null) { - return false; - } - - Cache.AddReplace(id, quest); - } - - return true; - } - - public IEnumerable GetQuests() { - if (isInitialized) { - return Cache.All().Values; - } - - lock (Context) { - List allQuestsList = Context.QuestMetadata.ToList(); - if (allQuestsList.Count > CACHE_SIZE) { - // leaving this exception in case more quests are added in the future - throw new Exception("Cache size exceeded the limit."); - } - - foreach (QuestMetadata quest in allQuestsList) { - Cache.AddReplace(quest.Id, quest); - } - - isInitialized = true; - return allQuestsList; - } - } - - public IEnumerable GetQuestsByNpc(int npcId) { - return GetQuests().Where(x => x.Basic.StartNpc == npcId).ToList(); - } - - public IEnumerable GetQuestsByType(QuestType type) { - return GetQuests().Where(x => x.Basic.Type == type); - } - - public IEnumerable GetQuestsByChapter(int chapterId) { - return GetQuests().Where(x => x.Basic.ChapterId == chapterId); - } - - public List Search(string name) { - return GetQuests().Where(x => x.Name != null && x.Name.Contains(name, StringComparison.OrdinalIgnoreCase)).ToList(); - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Context; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; + +namespace Maple2.Database.Storage; + +public class QuestMetadataStorage(MetadataContext context) : MetadataStorage(context, CACHE_SIZE), ISearchable { + private const int CACHE_SIZE = 8000; // 4.903 quests are in the database with NA feature, 7.2k quests in xmls + private bool isInitialized; + + public bool TryGet(int id, [NotNullWhen(true)] out QuestMetadata? quest) { + if (Cache.TryGet(id, out quest)) { + return true; + } + + lock (Context) { + // Double-checked locking + if (Cache.TryGet(id, out quest)) { + return true; + } + + quest = Context.QuestMetadata.Find(id); + + if (quest is null) { + return false; + } + + Cache.AddReplace(id, quest); + } + + return true; + } + + public IEnumerable GetQuests() { + if (isInitialized) { + return Cache.All().Values; + } + + lock (Context) { + List allQuestsList = Context.QuestMetadata.ToList(); + if (allQuestsList.Count > CACHE_SIZE) { + // leaving this exception in case more quests are added in the future + throw new Exception("Cache size exceeded the limit."); + } + + foreach (QuestMetadata quest in allQuestsList) { + Cache.AddReplace(quest.Id, quest); + } + + isInitialized = true; + return allQuestsList; + } + } + + public IEnumerable GetQuestsByNpc(int npcId) { + return GetQuests().Where(x => x.Basic.StartNpc == npcId).ToList(); + } + + public IEnumerable GetQuestsByType(QuestType type) { + return GetQuests().Where(x => x.Basic.Type == type); + } + + public IEnumerable GetQuestsByChapter(int chapterId) { + return GetQuests().Where(x => x.Basic.ChapterId == chapterId); + } + + public List Search(string name) { + return GetQuests().Where(x => x.Name != null && x.Name.Contains(name, StringComparison.OrdinalIgnoreCase)).ToList(); + } +} diff --git a/Maple2.Database/Storage/Metadata/RideMetadataStorage.cs b/Maple2.Database/Storage/Metadata/RideMetadataStorage.cs index 481986302..c0ee4b0be 100644 --- a/Maple2.Database/Storage/Metadata/RideMetadataStorage.cs +++ b/Maple2.Database/Storage/Metadata/RideMetadataStorage.cs @@ -1,32 +1,32 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Context; -using Maple2.Model.Metadata; - -namespace Maple2.Database.Storage; - -public class RideMetadataStorage(MetadataContext context) : MetadataStorage(context, CACHE_SIZE) { - private const int CACHE_SIZE = 500; // ~500 total items - - public bool TryGet(int id, [NotNullWhen(true)] out RideMetadata? ride) { - if (Cache.TryGet(id, out ride)) { - return true; - } - - lock (Context) { - // Double-checked locking - if (Cache.TryGet(id, out ride)) { - return true; - } - - ride = Context.RideMetadata.Find(id); - - if (ride == null) { - return false; - } - - Cache.AddReplace(id, ride); - } - - return true; - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Context; +using Maple2.Model.Metadata; + +namespace Maple2.Database.Storage; + +public class RideMetadataStorage(MetadataContext context) : MetadataStorage(context, CACHE_SIZE) { + private const int CACHE_SIZE = 500; // ~500 total items + + public bool TryGet(int id, [NotNullWhen(true)] out RideMetadata? ride) { + if (Cache.TryGet(id, out ride)) { + return true; + } + + lock (Context) { + // Double-checked locking + if (Cache.TryGet(id, out ride)) { + return true; + } + + ride = Context.RideMetadata.Find(id); + + if (ride == null) { + return false; + } + + Cache.AddReplace(id, ride); + } + + return true; + } +} diff --git a/Maple2.Database/Storage/Metadata/ScriptMetadataStorage.cs b/Maple2.Database/Storage/Metadata/ScriptMetadataStorage.cs index c93a6c5ba..78cf4f939 100644 --- a/Maple2.Database/Storage/Metadata/ScriptMetadataStorage.cs +++ b/Maple2.Database/Storage/Metadata/ScriptMetadataStorage.cs @@ -1,32 +1,32 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Context; -using Maple2.Model.Metadata; - -namespace Maple2.Database.Storage; - -public class ScriptMetadataStorage(MetadataContext context) : MetadataStorage(context, CACHE_SIZE) { - private const int CACHE_SIZE = 7000; // ~6.5k total items - - public bool TryGet(int id, [NotNullWhen(true)] out ScriptMetadata? script) { - if (Cache.TryGet(id, out script)) { - return true; - } - - lock (Context) { - // Double-checked locking - if (Cache.TryGet(id, out script)) { - return true; - } - - script = Context.ScriptMetadata.Find(id); - - if (script == null) { - return false; - } - - Cache.AddReplace(id, script); - } - - return true; - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Context; +using Maple2.Model.Metadata; + +namespace Maple2.Database.Storage; + +public class ScriptMetadataStorage(MetadataContext context) : MetadataStorage(context, CACHE_SIZE) { + private const int CACHE_SIZE = 7000; // ~6.5k total items + + public bool TryGet(int id, [NotNullWhen(true)] out ScriptMetadata? script) { + if (Cache.TryGet(id, out script)) { + return true; + } + + lock (Context) { + // Double-checked locking + if (Cache.TryGet(id, out script)) { + return true; + } + + script = Context.ScriptMetadata.Find(id); + + if (script == null) { + return false; + } + + Cache.AddReplace(id, script); + } + + return true; + } +} diff --git a/Maple2.Database/Storage/Metadata/ServerTableMetadataStorage.cs b/Maple2.Database/Storage/Metadata/ServerTableMetadataStorage.cs index 9a3c1d591..995d7493d 100644 --- a/Maple2.Database/Storage/Metadata/ServerTableMetadataStorage.cs +++ b/Maple2.Database/Storage/Metadata/ServerTableMetadataStorage.cs @@ -1,111 +1,111 @@ -using Maple2.Database.Context; -using Maple2.Model.Common; -using Maple2.Model.Game.Event; -using Maple2.Model.Metadata; - -namespace Maple2.Database.Storage; - -public class ServerTableMetadataStorage { - private readonly Lazy instanceFieldTable; - private readonly Lazy scriptConditionTable; - private readonly Lazy scriptFunctionTable; - private readonly Lazy scriptEventConditionTable; - private readonly Lazy jobConditionTable; - private readonly Lazy bonusGameTable; - private readonly Lazy globalDropItemBoxTable; - private readonly Lazy userStatTable; - private readonly Lazy individualDropItemTable; - private readonly Lazy prestigeExpTable; - private readonly Lazy prestigeIdExpTable; - private readonly Lazy timeEventTable; - private readonly Lazy gameEventTable; - private readonly Lazy oxQuizTable; - private readonly Lazy itemMergeTable; - private readonly Lazy shopTable; - private readonly Lazy shopItemTable; - private readonly Lazy beautyShopTable; - private readonly Lazy meretMarketTable; - private readonly Lazy fishTable; - private readonly Lazy combineSpawnTable; - private readonly Lazy enchantOptionTable; - private readonly Lazy unlimitedEnchantOptionTable; - - public InstanceFieldTable InstanceFieldTable => instanceFieldTable.Value; - public ScriptConditionTable ScriptConditionTable => scriptConditionTable.Value; - public ScriptFunctionTable ScriptFunctionTable => scriptFunctionTable.Value; - public ScriptEventConditionTable ScriptEventConditionTable => scriptEventConditionTable.Value; - public JobConditionTable JobConditionTable => jobConditionTable.Value; - public BonusGameTable BonusGameTable => bonusGameTable.Value; - public GlobalDropItemBoxTable GlobalDropItemBoxTable => globalDropItemBoxTable.Value; - public UserStatTable UserStatTable => userStatTable.Value; - public IndividualDropItemTable IndividualDropItemTable => individualDropItemTable.Value; - public PrestigeExpTable PrestigeExpTable => prestigeExpTable.Value; - public PrestigeIdExpTable PrestigeIdExpTable => prestigeIdExpTable.Value; - public TimeEventTable TimeEventTable => timeEventTable.Value; - public GameEventTable GameEventTable => gameEventTable.Value; - public OxQuizTable OxQuizTable => oxQuizTable.Value; - public ItemMergeTable ItemMergeTable => itemMergeTable.Value; - public ShopTable ShopTable => shopTable.Value; - public ShopItemTable ShopItemTable => shopItemTable.Value; - public BeautyShopTable BeautyShopTable => beautyShopTable.Value; - public MeretMarketTable MeretMarketTable => meretMarketTable.Value; - public FishTable FishTable => fishTable.Value; - public CombineSpawnTable CombineSpawnTable => combineSpawnTable.Value; - public EnchantOptionTable EnchantOptionTable => enchantOptionTable.Value; - public UnlimitedEnchantOptionTable UnlimitedEnchantOptionTable => unlimitedEnchantOptionTable.Value; - - public ServerTableMetadataStorage(MetadataContext context) { - instanceFieldTable = Retrieve(context, ServerTableNames.INSTANCE_FIELD); - scriptConditionTable = Retrieve(context, ServerTableNames.SCRIPT_CONDITION); - scriptFunctionTable = Retrieve(context, ServerTableNames.SCRIPT_FUNCTION); - scriptEventConditionTable = Retrieve(context, ServerTableNames.SCRIPT_EVENT); - jobConditionTable = Retrieve(context, ServerTableNames.JOB_CONDITION); - bonusGameTable = Retrieve(context, ServerTableNames.BONUS_GAME); - globalDropItemBoxTable = Retrieve(context, ServerTableNames.GLOBAL_DROP_ITEM_BOX); - userStatTable = Retrieve(context, ServerTableNames.USER_STAT); - individualDropItemTable = Retrieve(context, ServerTableNames.INDIVIDUAL_DROP_ITEM); - prestigeExpTable = Retrieve(context, ServerTableNames.PRESTIGE_EXP); - prestigeIdExpTable = Retrieve(context, ServerTableNames.PRESTIGE_ID_EXP); - timeEventTable = Retrieve(context, ServerTableNames.TIME_EVENT); - gameEventTable = Retrieve(context, ServerTableNames.GAME_EVENT); - oxQuizTable = Retrieve(context, ServerTableNames.OX_QUIZ); - itemMergeTable = Retrieve(context, ServerTableNames.ITEM_MERGE); - shopTable = Retrieve(context, ServerTableNames.SHOP); - shopItemTable = Retrieve(context, ServerTableNames.SHOP_ITEM); - beautyShopTable = Retrieve(context, ServerTableNames.BEAUTY_SHOP); - meretMarketTable = Retrieve(context, ServerTableNames.MERET_MARKET); - fishTable = Retrieve(context, ServerTableNames.FISH); - combineSpawnTable = Retrieve(context, ServerTableNames.COMBINE_SPAWN); - enchantOptionTable = Retrieve(context, ServerTableNames.ENCHANT_OPTION); - unlimitedEnchantOptionTable = Retrieve(context, ServerTableNames.UNLIMITED_ENCHANT_OPTION); - } - - public IEnumerable GetGameEvents() { - foreach ((int id, GameEventMetadata gameEvent) in GameEventTable.Entries) { - if (gameEvent.EndTime < DateTime.Now) { - continue; - } - - yield return new GameEvent(gameEvent); - } - } - - private static Lazy Retrieve(MetadataContext context, string key) where T : ServerTable { - var result = new Lazy(() => { - lock (context) { - ServerTableMetadata? row = context.ServerTableMetadata.Find(key); - if (row?.Table is not T result) { - throw new InvalidOperationException($"Row does not exist: {key}"); - } - - return result; - } - }); - -#if !DEBUG - // No lazy loading for RELEASE build. - _ = result.Value; -#endif - return result; - } -} +using Maple2.Database.Context; +using Maple2.Model.Common; +using Maple2.Model.Game.Event; +using Maple2.Model.Metadata; + +namespace Maple2.Database.Storage; + +public class ServerTableMetadataStorage { + private readonly Lazy instanceFieldTable; + private readonly Lazy scriptConditionTable; + private readonly Lazy scriptFunctionTable; + private readonly Lazy scriptEventConditionTable; + private readonly Lazy jobConditionTable; + private readonly Lazy bonusGameTable; + private readonly Lazy globalDropItemBoxTable; + private readonly Lazy userStatTable; + private readonly Lazy individualDropItemTable; + private readonly Lazy prestigeExpTable; + private readonly Lazy prestigeIdExpTable; + private readonly Lazy timeEventTable; + private readonly Lazy gameEventTable; + private readonly Lazy oxQuizTable; + private readonly Lazy itemMergeTable; + private readonly Lazy shopTable; + private readonly Lazy shopItemTable; + private readonly Lazy beautyShopTable; + private readonly Lazy meretMarketTable; + private readonly Lazy fishTable; + private readonly Lazy combineSpawnTable; + private readonly Lazy enchantOptionTable; + private readonly Lazy unlimitedEnchantOptionTable; + + public InstanceFieldTable InstanceFieldTable => instanceFieldTable.Value; + public ScriptConditionTable ScriptConditionTable => scriptConditionTable.Value; + public ScriptFunctionTable ScriptFunctionTable => scriptFunctionTable.Value; + public ScriptEventConditionTable ScriptEventConditionTable => scriptEventConditionTable.Value; + public JobConditionTable JobConditionTable => jobConditionTable.Value; + public BonusGameTable BonusGameTable => bonusGameTable.Value; + public GlobalDropItemBoxTable GlobalDropItemBoxTable => globalDropItemBoxTable.Value; + public UserStatTable UserStatTable => userStatTable.Value; + public IndividualDropItemTable IndividualDropItemTable => individualDropItemTable.Value; + public PrestigeExpTable PrestigeExpTable => prestigeExpTable.Value; + public PrestigeIdExpTable PrestigeIdExpTable => prestigeIdExpTable.Value; + public TimeEventTable TimeEventTable => timeEventTable.Value; + public GameEventTable GameEventTable => gameEventTable.Value; + public OxQuizTable OxQuizTable => oxQuizTable.Value; + public ItemMergeTable ItemMergeTable => itemMergeTable.Value; + public ShopTable ShopTable => shopTable.Value; + public ShopItemTable ShopItemTable => shopItemTable.Value; + public BeautyShopTable BeautyShopTable => beautyShopTable.Value; + public MeretMarketTable MeretMarketTable => meretMarketTable.Value; + public FishTable FishTable => fishTable.Value; + public CombineSpawnTable CombineSpawnTable => combineSpawnTable.Value; + public EnchantOptionTable EnchantOptionTable => enchantOptionTable.Value; + public UnlimitedEnchantOptionTable UnlimitedEnchantOptionTable => unlimitedEnchantOptionTable.Value; + + public ServerTableMetadataStorage(MetadataContext context) { + instanceFieldTable = Retrieve(context, ServerTableNames.INSTANCE_FIELD); + scriptConditionTable = Retrieve(context, ServerTableNames.SCRIPT_CONDITION); + scriptFunctionTable = Retrieve(context, ServerTableNames.SCRIPT_FUNCTION); + scriptEventConditionTable = Retrieve(context, ServerTableNames.SCRIPT_EVENT); + jobConditionTable = Retrieve(context, ServerTableNames.JOB_CONDITION); + bonusGameTable = Retrieve(context, ServerTableNames.BONUS_GAME); + globalDropItemBoxTable = Retrieve(context, ServerTableNames.GLOBAL_DROP_ITEM_BOX); + userStatTable = Retrieve(context, ServerTableNames.USER_STAT); + individualDropItemTable = Retrieve(context, ServerTableNames.INDIVIDUAL_DROP_ITEM); + prestigeExpTable = Retrieve(context, ServerTableNames.PRESTIGE_EXP); + prestigeIdExpTable = Retrieve(context, ServerTableNames.PRESTIGE_ID_EXP); + timeEventTable = Retrieve(context, ServerTableNames.TIME_EVENT); + gameEventTable = Retrieve(context, ServerTableNames.GAME_EVENT); + oxQuizTable = Retrieve(context, ServerTableNames.OX_QUIZ); + itemMergeTable = Retrieve(context, ServerTableNames.ITEM_MERGE); + shopTable = Retrieve(context, ServerTableNames.SHOP); + shopItemTable = Retrieve(context, ServerTableNames.SHOP_ITEM); + beautyShopTable = Retrieve(context, ServerTableNames.BEAUTY_SHOP); + meretMarketTable = Retrieve(context, ServerTableNames.MERET_MARKET); + fishTable = Retrieve(context, ServerTableNames.FISH); + combineSpawnTable = Retrieve(context, ServerTableNames.COMBINE_SPAWN); + enchantOptionTable = Retrieve(context, ServerTableNames.ENCHANT_OPTION); + unlimitedEnchantOptionTable = Retrieve(context, ServerTableNames.UNLIMITED_ENCHANT_OPTION); + } + + public IEnumerable GetGameEvents() { + foreach ((int id, GameEventMetadata gameEvent) in GameEventTable.Entries) { + if (gameEvent.EndTime < DateTime.Now) { + continue; + } + + yield return new GameEvent(gameEvent); + } + } + + private static Lazy Retrieve(MetadataContext context, string key) where T : ServerTable { + var result = new Lazy(() => { + lock (context) { + ServerTableMetadata? row = context.ServerTableMetadata.Find(key); + if (row?.Table is not T result) { + throw new InvalidOperationException($"Row does not exist: {key}"); + } + + return result; + } + }); + +#if !DEBUG + // No lazy loading for RELEASE build. + _ = result.Value; +#endif + return result; + } +} diff --git a/Maple2.Database/Storage/Metadata/SkillMetadataStorage.cs b/Maple2.Database/Storage/Metadata/SkillMetadataStorage.cs index 11bb0b7a8..fa5218ebf 100644 --- a/Maple2.Database/Storage/Metadata/SkillMetadataStorage.cs +++ b/Maple2.Database/Storage/Metadata/SkillMetadataStorage.cs @@ -1,79 +1,79 @@ -using System.Diagnostics.CodeAnalysis; -using Caching; -using Maple2.Database.Context; -using Maple2.Model.Metadata; -using Microsoft.EntityFrameworkCore; - -namespace Maple2.Database.Storage; - -public class SkillMetadataStorage : MetadataStorage<(int, short), SkillMetadata>, ISearchable { - private const int CACHE_SIZE = 23000; // ~22k total skill levels - private const int EFFECT_CACHE_SIZE = 15000; // ~14.5k total additional effect levels - - protected readonly LRUCache<(int Id, short Level), AdditionalEffectMetadata> EffectCache; - - public SkillMetadataStorage(MetadataContext context) : base(context, CACHE_SIZE) { - EffectCache = new(EFFECT_CACHE_SIZE, (int) (EFFECT_CACHE_SIZE * 0.05)); - } - - public bool TryGet(int id, short level, [NotNullWhen(true)] out SkillMetadata? skill) { - if (Cache.TryGet((id, level), out skill)) { - return true; - } - - lock (Context) { - // Double-checked locking - if (Cache.TryGet((id, level), out skill)) { - return true; - } - - StoredSkillMetadata? storedSkill = Context.SkillMetadata.Find(id); - - if (storedSkill == null) { - return false; - } - - foreach ((short dataLevel, SkillMetadataLevel data) in storedSkill.Levels) { - var metadata = new SkillMetadata(id, dataLevel, storedSkill.Name, storedSkill.Property, storedSkill.State, data); - Cache.AddReplace((id, dataLevel), metadata); - - if (dataLevel == level) { - skill = metadata; - } - } - } - - return skill != null; - } - - public bool TryGetEffect(int id, short level, [NotNullWhen(true)] out AdditionalEffectMetadata? effect) { - if (EffectCache.TryGet((id, level), out effect)) { - return true; - } - - lock (Context) { - // Double-checked locking - if (EffectCache.TryGet((id, level), out effect)) { - return true; - } - - effect = Context.AdditionalEffectMetadata.Find(id, level); - - if (effect == null) { - return false; - } - - EffectCache.AddReplace((id, level), effect); - } - - return true; - } - - public List Search(string name) { - lock (Context) { - return Context.SkillMetadata - .Where(skill => EF.Functions.Like(skill.Name!, $"%{name}%")) - .ToList(); - } - } -} +using System.Diagnostics.CodeAnalysis; +using Caching; +using Maple2.Database.Context; +using Maple2.Model.Metadata; +using Microsoft.EntityFrameworkCore; + +namespace Maple2.Database.Storage; + +public class SkillMetadataStorage : MetadataStorage<(int, short), SkillMetadata>, ISearchable { + private const int CACHE_SIZE = 23000; // ~22k total skill levels + private const int EFFECT_CACHE_SIZE = 15000; // ~14.5k total additional effect levels + + protected readonly LRUCache<(int Id, short Level), AdditionalEffectMetadata> EffectCache; + + public SkillMetadataStorage(MetadataContext context) : base(context, CACHE_SIZE) { + EffectCache = new(EFFECT_CACHE_SIZE, (int) (EFFECT_CACHE_SIZE * 0.05)); + } + + public bool TryGet(int id, short level, [NotNullWhen(true)] out SkillMetadata? skill) { + if (Cache.TryGet((id, level), out skill)) { + return true; + } + + lock (Context) { + // Double-checked locking + if (Cache.TryGet((id, level), out skill)) { + return true; + } + + StoredSkillMetadata? storedSkill = Context.SkillMetadata.Find(id); + + if (storedSkill == null) { + return false; + } + + foreach ((short dataLevel, SkillMetadataLevel data) in storedSkill.Levels) { + var metadata = new SkillMetadata(id, dataLevel, storedSkill.Name, storedSkill.Property, storedSkill.State, data); + Cache.AddReplace((id, dataLevel), metadata); + + if (dataLevel == level) { + skill = metadata; + } + } + } + + return skill != null; + } + + public bool TryGetEffect(int id, short level, [NotNullWhen(true)] out AdditionalEffectMetadata? effect) { + if (EffectCache.TryGet((id, level), out effect)) { + return true; + } + + lock (Context) { + // Double-checked locking + if (EffectCache.TryGet((id, level), out effect)) { + return true; + } + + effect = Context.AdditionalEffectMetadata.Find(id, level); + + if (effect == null) { + return false; + } + + EffectCache.AddReplace((id, level), effect); + } + + return true; + } + + public List Search(string name) { + lock (Context) { + return Context.SkillMetadata + .Where(skill => EF.Functions.Like(skill.Name!, $"%{name}%")) + .ToList(); + } + } +} diff --git a/Maple2.Database/Storage/Metadata/TableMetadataStorage.cs b/Maple2.Database/Storage/Metadata/TableMetadataStorage.cs index 8caf0f403..aff06081c 100644 --- a/Maple2.Database/Storage/Metadata/TableMetadataStorage.cs +++ b/Maple2.Database/Storage/Metadata/TableMetadataStorage.cs @@ -1,224 +1,224 @@ -using Maple2.Database.Context; -using Maple2.Model.Common; -using Maple2.Model.Metadata; - -namespace Maple2.Database.Storage; - -public class TableMetadataStorage { - private readonly Lazy chatStickerTable; - private readonly Lazy defaultItemsTable; - private readonly Lazy itemBreakTable; - private readonly Lazy itemExtractionTable; - private readonly Lazy gemstoneUpgradeTable; - private readonly Lazy jobTable; - private readonly Lazy magicPathTable; - private readonly Lazy masteryRecipeTable; - private readonly Lazy masteryRewardTable; - private readonly Lazy fishingRodTable; - private readonly Lazy instrumentTable; - private readonly Lazy interactObjectTable; - private readonly Lazy lapenshardUpgradeTable; - private readonly Lazy itemSocketTable; - private readonly Lazy guildTable; - private readonly Lazy premiumClubTable; - private readonly Lazy individualItemDropTable; - private readonly Lazy colorPaletteTable; - private readonly Lazy meretMarketCategoryTable; - private readonly Lazy shopBeautyCouponTable; - private readonly Lazy furnishingShopTable; - private readonly Lazy gachaInfoTable; - private readonly Lazy insigniaTable; - private readonly Lazy expTable; - private readonly Lazy commonExpTable; - private readonly Lazy ugcDesignTable; - private readonly Lazy masteryUgcHousingTable; - private readonly Lazy ugcHousingPointRewardTable; - private readonly Lazy learningQuestTable; - private readonly Lazy prestigeLevelAbilityTable; - private readonly Lazy prestigeLevelRewardTable; - private readonly Lazy prestigeMissionTable; - private readonly Lazy blackMarketTable; - private readonly Lazy changeJobTable; - private readonly Lazy chapterBookTable; - private readonly Lazy fieldMissionTable; - private readonly Lazy worldMapTable; - private readonly Lazy survivalSkinInfoTable; - private readonly Lazy bannerTable; - private readonly Lazy weddingTable; - private readonly Lazy rewardContentTable; - private readonly Lazy seasonDataTable; - private readonly Lazy smartPushTable; - private readonly Lazy autoActionTable; - - private readonly Lazy enchantScrollTable; - private readonly Lazy itemRemakeScrollTable; - private readonly Lazy itemRepackingScrollTable; - private readonly Lazy itemSocketScrollTable; - private readonly Lazy itemExchangeScrollTable; - - private readonly Lazy itemOptionConstantTable; - private readonly Lazy itemOptionRandomTable; - private readonly Lazy itemOptionStaticTable; - private readonly Lazy itemOptionPickTable; - private readonly Lazy itemVariationTable; - private readonly Lazy accVariationTable; - private readonly Lazy armorVariationTable; - private readonly Lazy petVariationTable; - private readonly Lazy weaponVariationTable; - - private readonly Lazy dungeonRoomTable; - private readonly Lazy dungeonRankRewardTable; - private readonly Lazy dungeonConfigTable; - private readonly Lazy dungeonMissionTable; - - public ChatStickerTable ChatStickerTable => chatStickerTable.Value; - public DefaultItemsTable DefaultItemsTable => defaultItemsTable.Value; - public ItemBreakTable ItemBreakTable => itemBreakTable.Value; - public ItemExtractionTable ItemExtractionTable => itemExtractionTable.Value; - public GemstoneUpgradeTable GemstoneUpgradeTable => gemstoneUpgradeTable.Value; - public JobTable JobTable => jobTable.Value; - public MagicPathTable MagicPathTable => magicPathTable.Value; - public MasteryRecipeTable MasteryRecipeTable => masteryRecipeTable.Value; - public MasteryRewardTable MasteryRewardTable => masteryRewardTable.Value; - public FishingRodTable FishingRodTable => fishingRodTable.Value; - public InstrumentTable InstrumentTable => instrumentTable.Value; - public InteractObjectTable InteractObjectTable => interactObjectTable.Value; - public LapenshardUpgradeTable LapenshardUpgradeTable => lapenshardUpgradeTable.Value; - public ItemSocketTable ItemSocketTable => itemSocketTable.Value; - public GuildTable GuildTable => guildTable.Value; - public PremiumClubTable PremiumClubTable => premiumClubTable.Value; - public IndividualItemDropTable IndividualItemDropTable => individualItemDropTable.Value; - public ColorPaletteTable ColorPaletteTable => colorPaletteTable.Value; - public MeretMarketCategoryTable MeretMarketCategoryTable => meretMarketCategoryTable.Value; - public ShopBeautyCouponTable ShopBeautyCouponTable => shopBeautyCouponTable.Value; - public FurnishingShopTable FurnishingShopTable => furnishingShopTable.Value; - public GachaInfoTable GachaInfoTable => gachaInfoTable.Value; - public InsigniaTable InsigniaTable => insigniaTable.Value; - public ExpTable ExpTable => expTable.Value; - public CommonExpTable CommonExpTable => commonExpTable.Value; - public UgcDesignTable UgcDesignTable => ugcDesignTable.Value; - public MasteryUgcHousingTable MasteryUgcHousingTable => masteryUgcHousingTable.Value; - public UgcHousingPointRewardTable UgcHousingPointRewardTable => ugcHousingPointRewardTable.Value; - public LearningQuestTable LearningQuestTable => learningQuestTable.Value; - public PrestigeLevelAbilityTable PrestigeLevelAbilityTable => prestigeLevelAbilityTable.Value; - public PrestigeLevelRewardTable PrestigeLevelRewardTable => prestigeLevelRewardTable.Value; - public PrestigeMissionTable PrestigeMissionTable => prestigeMissionTable.Value; - public BlackMarketTable BlackMarketTable => blackMarketTable.Value; - public ChangeJobTable ChangeJobTable => changeJobTable.Value; - public ChapterBookTable ChapterBookTable => chapterBookTable.Value; - public FieldMissionTable FieldMissionTable => fieldMissionTable.Value; - public WorldMapTable WorldMapTable => worldMapTable.Value; - public SurvivalSkinInfoTable SurvivalSkinInfoTable => survivalSkinInfoTable.Value; - public BannerTable BannerTable => bannerTable.Value; - public WeddingTable WeddingTable => weddingTable.Value; - public RewardContentTable RewardContentTable => rewardContentTable.Value; - public SeasonDataTable SeasonDataTable => seasonDataTable.Value; - public SmartPushTable SmartPushTable => smartPushTable.Value; - public AutoActionTable AutoActionTable => autoActionTable.Value; - - public EnchantScrollTable EnchantScrollTable => enchantScrollTable.Value; - public ItemRemakeScrollTable ItemRemakeScrollTable => itemRemakeScrollTable.Value; - public ItemRepackingScrollTable ItemRepackingScrollTable => itemRepackingScrollTable.Value; - public ItemSocketScrollTable ItemSocketScrollTable => itemSocketScrollTable.Value; - public ItemExchangeScrollTable ItemExchangeScrollTable => itemExchangeScrollTable.Value; - - public ItemOptionConstantTable ItemOptionConstantTable => itemOptionConstantTable.Value; - public ItemOptionRandomTable ItemOptionRandomTable => itemOptionRandomTable.Value; - public ItemOptionStaticTable ItemOptionStaticTable => itemOptionStaticTable.Value; - public ItemOptionPickTable ItemOptionPickTable => itemOptionPickTable.Value; - public ItemVariationTable ItemVariationTable => itemVariationTable.Value; - public ItemEquipVariationTable AccessoryVariationTable => accVariationTable.Value; - public ItemEquipVariationTable ArmorVariationTable => armorVariationTable.Value; - public ItemEquipVariationTable PetVariationTable => petVariationTable.Value; - public ItemEquipVariationTable WeaponVariationTable => weaponVariationTable.Value; - - public DungeonRoomTable DungeonRoomTable => dungeonRoomTable.Value; - public DungeonRankRewardTable DungeonRankRewardTable => dungeonRankRewardTable.Value; - public DungeonConfigTable DungeonConfigTable => dungeonConfigTable.Value; - public DungeonMissionTable DungeonMissionTable => dungeonMissionTable.Value; - - public TableMetadataStorage(MetadataContext context) { - chatStickerTable = Retrieve(context, TableNames.CHAT_EMOTICON); - defaultItemsTable = Retrieve(context, TableNames.DEFAULT_ITEMS); - itemBreakTable = Retrieve(context, TableNames.ITEM_BREAK_INGREDIENT); - itemExtractionTable = Retrieve(context, TableNames.ITEM_EXTRACTION); - gemstoneUpgradeTable = Retrieve(context, TableNames.ITEM_GEMSTONE_UPGRADE); - jobTable = Retrieve(context, TableNames.JOB); - magicPathTable = Retrieve(context, TableNames.MAGIC_PATH); - masteryRecipeTable = Retrieve(context, TableNames.MASTERY_RECIPE); - masteryRewardTable = Retrieve(context, TableNames.MASTERY); - fishingRodTable = Retrieve(context, TableNames.FISHING_ROD); - instrumentTable = Retrieve(context, TableNames.INSTRUMENT_CATEGORY_INFO); - interactObjectTable = Retrieve(context, TableNames.INTERACT_OBJECT); - lapenshardUpgradeTable = Retrieve(context, TableNames.ITEM_LAPENSHARD_UPGRADE); - itemSocketTable = Retrieve(context, TableNames.ITEM_SOCKET); - guildTable = Retrieve(context, TableNames.GUILD); - premiumClubTable = Retrieve(context, TableNames.VIP); - individualItemDropTable = Retrieve(context, TableNames.INDIVIDUAL_ITEM_DROP); - colorPaletteTable = Retrieve(context, TableNames.COLOR_PALETTE); - meretMarketCategoryTable = Retrieve(context, TableNames.MERET_MARKET_CATEGORY); - shopBeautyCouponTable = Retrieve(context, TableNames.SHOP_BEAUTY_COUPON); - furnishingShopTable = Retrieve(context, TableNames.SHOP_FURNISHING); - gachaInfoTable = Retrieve(context, TableNames.GACHA_INFO); - insigniaTable = Retrieve(context, TableNames.NAME_TAG_SYMBOL); - expTable = Retrieve(context, TableNames.EXP); - commonExpTable = Retrieve(context, TableNames.COMMON_EXP); - ugcDesignTable = Retrieve(context, TableNames.UGC_DESIGN); - masteryUgcHousingTable = Retrieve(context, TableNames.MASTERY_UGC_HOUSING); - ugcHousingPointRewardTable = Retrieve(context, TableNames.UGC_HOUSING_POINT_REWARD); - learningQuestTable = Retrieve(context, TableNames.LEARNING_QUEST); - prestigeLevelAbilityTable = Retrieve(context, TableNames.PRESTIGE_LEVEL_ABILITY); - prestigeLevelRewardTable = Retrieve(context, TableNames.PRESTIGE_LEVEL_REWARD); - prestigeMissionTable = Retrieve(context, TableNames.PRESTIGE_MISSION); - blackMarketTable = Retrieve(context, TableNames.BLACK_MARKET_TABLE); - changeJobTable = Retrieve(context, TableNames.CHANGE_JOB); - chapterBookTable = Retrieve(context, TableNames.CHAPTER_BOOK); - fieldMissionTable = Retrieve(context, TableNames.FIELD_MISSION); - worldMapTable = Retrieve(context, TableNames.WORLD_MAP); - survivalSkinInfoTable = Retrieve(context, TableNames.SURVIVAL_SKIN_INFO); - bannerTable = Retrieve(context, TableNames.BANNER); - weddingTable = Retrieve(context, TableNames.WEDDING); - rewardContentTable = Retrieve(context, TableNames.REWARD_CONTENT); - enchantScrollTable = Retrieve(context, TableNames.ENCHANT_SCROLL); - itemRemakeScrollTable = Retrieve(context, TableNames.ITEM_REMAKE_SCROLL); - itemRepackingScrollTable = Retrieve(context, TableNames.ITEM_REPACKING_SCROLL); - itemSocketScrollTable = Retrieve(context, TableNames.ITEM_SOCKET_SCROLL); - itemExchangeScrollTable = Retrieve(context, TableNames.ITEM_EXCHANGE_SCROLL); - itemOptionConstantTable = Retrieve(context, TableNames.ITEM_OPTION_CONSTANT); - itemOptionRandomTable = Retrieve(context, TableNames.ITEM_OPTION_RANDOM); - itemOptionStaticTable = Retrieve(context, TableNames.ITEM_OPTION_STATIC); - itemOptionPickTable = Retrieve(context, TableNames.ITEM_OPTION_PICK); - itemVariationTable = Retrieve(context, TableNames.ITEM_OPTION_VARIATION); - accVariationTable = Retrieve(context, TableNames.ITEM_OPTION_VARIATION_ACC); - armorVariationTable = Retrieve(context, TableNames.ITEM_OPTION_VARIATION_ARMOR); - petVariationTable = Retrieve(context, TableNames.ITEM_OPTION_VARIATION_PET); - weaponVariationTable = Retrieve(context, TableNames.ITEM_OPTION_VARIATION_WEAPON); - dungeonRoomTable = Retrieve(context, TableNames.DUNGEON_ROOM); - dungeonRankRewardTable = Retrieve(context, TableNames.DUNGEON_RANK_REWARD); - dungeonConfigTable = Retrieve(context, TableNames.DUNGEON_CONFIG); - dungeonMissionTable = Retrieve(context, TableNames.DUNGEON_MISSION); - seasonDataTable = Retrieve(context, TableNames.SEASON_DATA); - smartPushTable = Retrieve(context, TableNames.SMART_PUSH); - autoActionTable = Retrieve(context, TableNames.AUTO_ACTION); - - } - - private static Lazy Retrieve(MetadataContext context, string key) where T : Table { - var result = new Lazy(() => { - lock (context) { - TableMetadata? row = context.TableMetadata.Find(key); - if (row?.Table is not T result) { - throw new InvalidOperationException($"Row does not exist: {key}"); - } - - return result; - } - }); - -#if !DEBUG - // No lazy loading for RELEASE build. - _ = result.Value; -#endif - return result; - } -} +using Maple2.Database.Context; +using Maple2.Model.Common; +using Maple2.Model.Metadata; + +namespace Maple2.Database.Storage; + +public class TableMetadataStorage { + private readonly Lazy chatStickerTable; + private readonly Lazy defaultItemsTable; + private readonly Lazy itemBreakTable; + private readonly Lazy itemExtractionTable; + private readonly Lazy gemstoneUpgradeTable; + private readonly Lazy jobTable; + private readonly Lazy magicPathTable; + private readonly Lazy masteryRecipeTable; + private readonly Lazy masteryRewardTable; + private readonly Lazy fishingRodTable; + private readonly Lazy instrumentTable; + private readonly Lazy interactObjectTable; + private readonly Lazy lapenshardUpgradeTable; + private readonly Lazy itemSocketTable; + private readonly Lazy guildTable; + private readonly Lazy premiumClubTable; + private readonly Lazy individualItemDropTable; + private readonly Lazy colorPaletteTable; + private readonly Lazy meretMarketCategoryTable; + private readonly Lazy shopBeautyCouponTable; + private readonly Lazy furnishingShopTable; + private readonly Lazy gachaInfoTable; + private readonly Lazy insigniaTable; + private readonly Lazy expTable; + private readonly Lazy commonExpTable; + private readonly Lazy ugcDesignTable; + private readonly Lazy masteryUgcHousingTable; + private readonly Lazy ugcHousingPointRewardTable; + private readonly Lazy learningQuestTable; + private readonly Lazy prestigeLevelAbilityTable; + private readonly Lazy prestigeLevelRewardTable; + private readonly Lazy prestigeMissionTable; + private readonly Lazy blackMarketTable; + private readonly Lazy changeJobTable; + private readonly Lazy chapterBookTable; + private readonly Lazy fieldMissionTable; + private readonly Lazy worldMapTable; + private readonly Lazy survivalSkinInfoTable; + private readonly Lazy bannerTable; + private readonly Lazy weddingTable; + private readonly Lazy rewardContentTable; + private readonly Lazy seasonDataTable; + private readonly Lazy smartPushTable; + private readonly Lazy autoActionTable; + + private readonly Lazy enchantScrollTable; + private readonly Lazy itemRemakeScrollTable; + private readonly Lazy itemRepackingScrollTable; + private readonly Lazy itemSocketScrollTable; + private readonly Lazy itemExchangeScrollTable; + + private readonly Lazy itemOptionConstantTable; + private readonly Lazy itemOptionRandomTable; + private readonly Lazy itemOptionStaticTable; + private readonly Lazy itemOptionPickTable; + private readonly Lazy itemVariationTable; + private readonly Lazy accVariationTable; + private readonly Lazy armorVariationTable; + private readonly Lazy petVariationTable; + private readonly Lazy weaponVariationTable; + + private readonly Lazy dungeonRoomTable; + private readonly Lazy dungeonRankRewardTable; + private readonly Lazy dungeonConfigTable; + private readonly Lazy dungeonMissionTable; + + public ChatStickerTable ChatStickerTable => chatStickerTable.Value; + public DefaultItemsTable DefaultItemsTable => defaultItemsTable.Value; + public ItemBreakTable ItemBreakTable => itemBreakTable.Value; + public ItemExtractionTable ItemExtractionTable => itemExtractionTable.Value; + public GemstoneUpgradeTable GemstoneUpgradeTable => gemstoneUpgradeTable.Value; + public JobTable JobTable => jobTable.Value; + public MagicPathTable MagicPathTable => magicPathTable.Value; + public MasteryRecipeTable MasteryRecipeTable => masteryRecipeTable.Value; + public MasteryRewardTable MasteryRewardTable => masteryRewardTable.Value; + public FishingRodTable FishingRodTable => fishingRodTable.Value; + public InstrumentTable InstrumentTable => instrumentTable.Value; + public InteractObjectTable InteractObjectTable => interactObjectTable.Value; + public LapenshardUpgradeTable LapenshardUpgradeTable => lapenshardUpgradeTable.Value; + public ItemSocketTable ItemSocketTable => itemSocketTable.Value; + public GuildTable GuildTable => guildTable.Value; + public PremiumClubTable PremiumClubTable => premiumClubTable.Value; + public IndividualItemDropTable IndividualItemDropTable => individualItemDropTable.Value; + public ColorPaletteTable ColorPaletteTable => colorPaletteTable.Value; + public MeretMarketCategoryTable MeretMarketCategoryTable => meretMarketCategoryTable.Value; + public ShopBeautyCouponTable ShopBeautyCouponTable => shopBeautyCouponTable.Value; + public FurnishingShopTable FurnishingShopTable => furnishingShopTable.Value; + public GachaInfoTable GachaInfoTable => gachaInfoTable.Value; + public InsigniaTable InsigniaTable => insigniaTable.Value; + public ExpTable ExpTable => expTable.Value; + public CommonExpTable CommonExpTable => commonExpTable.Value; + public UgcDesignTable UgcDesignTable => ugcDesignTable.Value; + public MasteryUgcHousingTable MasteryUgcHousingTable => masteryUgcHousingTable.Value; + public UgcHousingPointRewardTable UgcHousingPointRewardTable => ugcHousingPointRewardTable.Value; + public LearningQuestTable LearningQuestTable => learningQuestTable.Value; + public PrestigeLevelAbilityTable PrestigeLevelAbilityTable => prestigeLevelAbilityTable.Value; + public PrestigeLevelRewardTable PrestigeLevelRewardTable => prestigeLevelRewardTable.Value; + public PrestigeMissionTable PrestigeMissionTable => prestigeMissionTable.Value; + public BlackMarketTable BlackMarketTable => blackMarketTable.Value; + public ChangeJobTable ChangeJobTable => changeJobTable.Value; + public ChapterBookTable ChapterBookTable => chapterBookTable.Value; + public FieldMissionTable FieldMissionTable => fieldMissionTable.Value; + public WorldMapTable WorldMapTable => worldMapTable.Value; + public SurvivalSkinInfoTable SurvivalSkinInfoTable => survivalSkinInfoTable.Value; + public BannerTable BannerTable => bannerTable.Value; + public WeddingTable WeddingTable => weddingTable.Value; + public RewardContentTable RewardContentTable => rewardContentTable.Value; + public SeasonDataTable SeasonDataTable => seasonDataTable.Value; + public SmartPushTable SmartPushTable => smartPushTable.Value; + public AutoActionTable AutoActionTable => autoActionTable.Value; + + public EnchantScrollTable EnchantScrollTable => enchantScrollTable.Value; + public ItemRemakeScrollTable ItemRemakeScrollTable => itemRemakeScrollTable.Value; + public ItemRepackingScrollTable ItemRepackingScrollTable => itemRepackingScrollTable.Value; + public ItemSocketScrollTable ItemSocketScrollTable => itemSocketScrollTable.Value; + public ItemExchangeScrollTable ItemExchangeScrollTable => itemExchangeScrollTable.Value; + + public ItemOptionConstantTable ItemOptionConstantTable => itemOptionConstantTable.Value; + public ItemOptionRandomTable ItemOptionRandomTable => itemOptionRandomTable.Value; + public ItemOptionStaticTable ItemOptionStaticTable => itemOptionStaticTable.Value; + public ItemOptionPickTable ItemOptionPickTable => itemOptionPickTable.Value; + public ItemVariationTable ItemVariationTable => itemVariationTable.Value; + public ItemEquipVariationTable AccessoryVariationTable => accVariationTable.Value; + public ItemEquipVariationTable ArmorVariationTable => armorVariationTable.Value; + public ItemEquipVariationTable PetVariationTable => petVariationTable.Value; + public ItemEquipVariationTable WeaponVariationTable => weaponVariationTable.Value; + + public DungeonRoomTable DungeonRoomTable => dungeonRoomTable.Value; + public DungeonRankRewardTable DungeonRankRewardTable => dungeonRankRewardTable.Value; + public DungeonConfigTable DungeonConfigTable => dungeonConfigTable.Value; + public DungeonMissionTable DungeonMissionTable => dungeonMissionTable.Value; + + public TableMetadataStorage(MetadataContext context) { + chatStickerTable = Retrieve(context, TableNames.CHAT_EMOTICON); + defaultItemsTable = Retrieve(context, TableNames.DEFAULT_ITEMS); + itemBreakTable = Retrieve(context, TableNames.ITEM_BREAK_INGREDIENT); + itemExtractionTable = Retrieve(context, TableNames.ITEM_EXTRACTION); + gemstoneUpgradeTable = Retrieve(context, TableNames.ITEM_GEMSTONE_UPGRADE); + jobTable = Retrieve(context, TableNames.JOB); + magicPathTable = Retrieve(context, TableNames.MAGIC_PATH); + masteryRecipeTable = Retrieve(context, TableNames.MASTERY_RECIPE); + masteryRewardTable = Retrieve(context, TableNames.MASTERY); + fishingRodTable = Retrieve(context, TableNames.FISHING_ROD); + instrumentTable = Retrieve(context, TableNames.INSTRUMENT_CATEGORY_INFO); + interactObjectTable = Retrieve(context, TableNames.INTERACT_OBJECT); + lapenshardUpgradeTable = Retrieve(context, TableNames.ITEM_LAPENSHARD_UPGRADE); + itemSocketTable = Retrieve(context, TableNames.ITEM_SOCKET); + guildTable = Retrieve(context, TableNames.GUILD); + premiumClubTable = Retrieve(context, TableNames.VIP); + individualItemDropTable = Retrieve(context, TableNames.INDIVIDUAL_ITEM_DROP); + colorPaletteTable = Retrieve(context, TableNames.COLOR_PALETTE); + meretMarketCategoryTable = Retrieve(context, TableNames.MERET_MARKET_CATEGORY); + shopBeautyCouponTable = Retrieve(context, TableNames.SHOP_BEAUTY_COUPON); + furnishingShopTable = Retrieve(context, TableNames.SHOP_FURNISHING); + gachaInfoTable = Retrieve(context, TableNames.GACHA_INFO); + insigniaTable = Retrieve(context, TableNames.NAME_TAG_SYMBOL); + expTable = Retrieve(context, TableNames.EXP); + commonExpTable = Retrieve(context, TableNames.COMMON_EXP); + ugcDesignTable = Retrieve(context, TableNames.UGC_DESIGN); + masteryUgcHousingTable = Retrieve(context, TableNames.MASTERY_UGC_HOUSING); + ugcHousingPointRewardTable = Retrieve(context, TableNames.UGC_HOUSING_POINT_REWARD); + learningQuestTable = Retrieve(context, TableNames.LEARNING_QUEST); + prestigeLevelAbilityTable = Retrieve(context, TableNames.PRESTIGE_LEVEL_ABILITY); + prestigeLevelRewardTable = Retrieve(context, TableNames.PRESTIGE_LEVEL_REWARD); + prestigeMissionTable = Retrieve(context, TableNames.PRESTIGE_MISSION); + blackMarketTable = Retrieve(context, TableNames.BLACK_MARKET_TABLE); + changeJobTable = Retrieve(context, TableNames.CHANGE_JOB); + chapterBookTable = Retrieve(context, TableNames.CHAPTER_BOOK); + fieldMissionTable = Retrieve(context, TableNames.FIELD_MISSION); + worldMapTable = Retrieve(context, TableNames.WORLD_MAP); + survivalSkinInfoTable = Retrieve(context, TableNames.SURVIVAL_SKIN_INFO); + bannerTable = Retrieve(context, TableNames.BANNER); + weddingTable = Retrieve(context, TableNames.WEDDING); + rewardContentTable = Retrieve(context, TableNames.REWARD_CONTENT); + enchantScrollTable = Retrieve(context, TableNames.ENCHANT_SCROLL); + itemRemakeScrollTable = Retrieve(context, TableNames.ITEM_REMAKE_SCROLL); + itemRepackingScrollTable = Retrieve(context, TableNames.ITEM_REPACKING_SCROLL); + itemSocketScrollTable = Retrieve(context, TableNames.ITEM_SOCKET_SCROLL); + itemExchangeScrollTable = Retrieve(context, TableNames.ITEM_EXCHANGE_SCROLL); + itemOptionConstantTable = Retrieve(context, TableNames.ITEM_OPTION_CONSTANT); + itemOptionRandomTable = Retrieve(context, TableNames.ITEM_OPTION_RANDOM); + itemOptionStaticTable = Retrieve(context, TableNames.ITEM_OPTION_STATIC); + itemOptionPickTable = Retrieve(context, TableNames.ITEM_OPTION_PICK); + itemVariationTable = Retrieve(context, TableNames.ITEM_OPTION_VARIATION); + accVariationTable = Retrieve(context, TableNames.ITEM_OPTION_VARIATION_ACC); + armorVariationTable = Retrieve(context, TableNames.ITEM_OPTION_VARIATION_ARMOR); + petVariationTable = Retrieve(context, TableNames.ITEM_OPTION_VARIATION_PET); + weaponVariationTable = Retrieve(context, TableNames.ITEM_OPTION_VARIATION_WEAPON); + dungeonRoomTable = Retrieve(context, TableNames.DUNGEON_ROOM); + dungeonRankRewardTable = Retrieve(context, TableNames.DUNGEON_RANK_REWARD); + dungeonConfigTable = Retrieve(context, TableNames.DUNGEON_CONFIG); + dungeonMissionTable = Retrieve(context, TableNames.DUNGEON_MISSION); + seasonDataTable = Retrieve(context, TableNames.SEASON_DATA); + smartPushTable = Retrieve(context, TableNames.SMART_PUSH); + autoActionTable = Retrieve(context, TableNames.AUTO_ACTION); + + } + + private static Lazy Retrieve(MetadataContext context, string key) where T : Table { + var result = new Lazy(() => { + lock (context) { + TableMetadata? row = context.TableMetadata.Find(key); + if (row?.Table is not T result) { + throw new InvalidOperationException($"Row does not exist: {key}"); + } + + return result; + } + }); + +#if !DEBUG + // No lazy loading for RELEASE build. + _ = result.Value; +#endif + return result; + } +} diff --git a/Maple2.Database/Storage/Metadata/TriggerScriptMetadata.cs b/Maple2.Database/Storage/Metadata/TriggerScriptMetadata.cs index 7c9bc6762..335ce5e71 100644 --- a/Maple2.Database/Storage/Metadata/TriggerScriptMetadata.cs +++ b/Maple2.Database/Storage/Metadata/TriggerScriptMetadata.cs @@ -1,32 +1,32 @@ -using System.Diagnostics.CodeAnalysis; -using Maple2.Database.Context; -using Maple2.Model.Metadata; - -namespace Maple2.Database.Storage; - -public class TriggerScriptMetadata(MetadataContext context) : MetadataStorage<(string, string), TriggerMetadata>(context, CACHE_SIZE) { - private const int CACHE_SIZE = 5000; // ~5k total triggers - - public bool TryGet(string mapXBlock, string triggerName, [NotNullWhen(true)] out TriggerMetadata? trigger) { - if (Cache.TryGet((mapXBlock, triggerName), out trigger)) { - return true; - } - - lock (Context) { - // Double-checked locking - if (Cache.TryGet((mapXBlock, triggerName), out trigger)) { - return true; - } - - trigger = Context.TriggerMetadata.Find(mapXBlock, triggerName); - - if (trigger == null) { - return false; - } - - Cache.AddReplace((mapXBlock, triggerName), trigger); - } - - return true; - } -} +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Context; +using Maple2.Model.Metadata; + +namespace Maple2.Database.Storage; + +public class TriggerScriptMetadata(MetadataContext context) : MetadataStorage<(string, string), TriggerMetadata>(context, CACHE_SIZE) { + private const int CACHE_SIZE = 5000; // ~5k total triggers + + public bool TryGet(string mapXBlock, string triggerName, [NotNullWhen(true)] out TriggerMetadata? trigger) { + if (Cache.TryGet((mapXBlock, triggerName), out trigger)) { + return true; + } + + lock (Context) { + // Double-checked locking + if (Cache.TryGet((mapXBlock, triggerName), out trigger)) { + return true; + } + + trigger = Context.TriggerMetadata.Find(mapXBlock, triggerName); + + if (trigger == null) { + return false; + } + + Cache.AddReplace((mapXBlock, triggerName), trigger); + } + + return true; + } +} diff --git a/Maple2.Database/Storage/Metadata/TriggerStorage.cs b/Maple2.Database/Storage/Metadata/TriggerStorage.cs index a030dea39..31f728c27 100644 --- a/Maple2.Database/Storage/Metadata/TriggerStorage.cs +++ b/Maple2.Database/Storage/Metadata/TriggerStorage.cs @@ -1,109 +1,109 @@ -using System.Collections; -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using Maple2.Model.Metadata; - -namespace Maple2.Database.Storage; - -internal class TriggerStorage : ITriggerStorage { - private readonly ImmutableDictionary triggers; - public ImmutableArray Actors { get; } - public ImmutableArray Agents { get; } - public ImmutableArray Boxes { get; } - public ImmutableArray Cameras { get; } - public ImmutableArray Cubes { get; } - public ImmutableArray Effects { get; } - public ImmutableArray Ladders { get; } - public ImmutableArray Meshes { get; } - public ImmutableArray Ropes { get; } - public ImmutableArray Skills { get; } - public ImmutableArray Sounds { get; } - - public TriggerStorage(List triggers) { - var builder = ImmutableDictionary.CreateBuilder(); - var actorBuilder = ImmutableArray.CreateBuilder(); - var agentBuilder = ImmutableArray.CreateBuilder(); - var boxBuilder = ImmutableArray.CreateBuilder(); - var cameraBuilder = ImmutableArray.CreateBuilder(); - var cubeBuilder = ImmutableArray.CreateBuilder(); - var effectBuilder = ImmutableArray.CreateBuilder(); - var ladderBuilder = ImmutableArray.CreateBuilder(); - var meshBuilder = ImmutableArray.CreateBuilder(); - var ropeBuilder = ImmutableArray.CreateBuilder(); - var skillBuilder = ImmutableArray.CreateBuilder(); - var soundBuilder = ImmutableArray.CreateBuilder(); - - foreach (Ms2Trigger trigger in triggers) { - switch (trigger) { - case Ms2TriggerActor actor: - actorBuilder.Add(actor); - break; - case Ms2TriggerAgent agent: - agentBuilder.Add(agent); - break; - case Ms2TriggerBox box: - boxBuilder.Add(box); - break; - case Ms2TriggerCamera camera: - cameraBuilder.Add(camera); - break; - case Ms2TriggerCube cube: - cubeBuilder.Add(cube); - break; - case Ms2TriggerEffect effect: - effectBuilder.Add(effect); - break; - case Ms2TriggerLadder ladder: - ladderBuilder.Add(ladder); - break; - case Ms2TriggerMesh mesh: - meshBuilder.Add(mesh); - break; - case Ms2TriggerRope rope: - ropeBuilder.Add(rope); - break; - case Ms2TriggerSkill skill: - skillBuilder.Add(skill); - break; - case Ms2TriggerSound sound: - soundBuilder.Add(sound); - break; - default: - continue; - } - - builder.Add(trigger.TriggerId, trigger); - } - - this.triggers = builder.ToImmutable(); - Actors = actorBuilder.ToImmutable(); - Agents = agentBuilder.ToImmutable(); - Boxes = boxBuilder.ToImmutable(); - Cameras = cameraBuilder.ToImmutable(); - Cubes = cubeBuilder.ToImmutable(); - Effects = effectBuilder.ToImmutable(); - Ladders = ladderBuilder.ToImmutable(); - Meshes = meshBuilder.ToImmutable(); - Ropes = ropeBuilder.ToImmutable(); - Skills = skillBuilder.ToImmutable(); - Sounds = soundBuilder.ToImmutable(); - } - - public bool TryGet(int key, [NotNullWhen(true)] out T? trigger) where T : Ms2Trigger { - triggers.TryGetValue(key, out Ms2Trigger? result); - trigger = result as T; - return trigger != null; - } - - public int Count => triggers.Count; - public Ms2Trigger this[int key] => triggers[key]; - - public IEnumerable Keys => triggers.Keys; - public IEnumerable Values => triggers.Values; - - public bool ContainsKey(int key) => triggers.ContainsKey(key); - public bool TryGetValue(int key, [NotNullWhen(true)] out Ms2Trigger? value) => triggers.TryGetValue(key, out value); - - public IEnumerator> GetEnumerator() => triggers.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => triggers.GetEnumerator(); -} +using System.Collections; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using Maple2.Model.Metadata; + +namespace Maple2.Database.Storage; + +internal class TriggerStorage : ITriggerStorage { + private readonly ImmutableDictionary triggers; + public ImmutableArray Actors { get; } + public ImmutableArray Agents { get; } + public ImmutableArray Boxes { get; } + public ImmutableArray Cameras { get; } + public ImmutableArray Cubes { get; } + public ImmutableArray Effects { get; } + public ImmutableArray Ladders { get; } + public ImmutableArray Meshes { get; } + public ImmutableArray Ropes { get; } + public ImmutableArray Skills { get; } + public ImmutableArray Sounds { get; } + + public TriggerStorage(List triggers) { + var builder = ImmutableDictionary.CreateBuilder(); + var actorBuilder = ImmutableArray.CreateBuilder(); + var agentBuilder = ImmutableArray.CreateBuilder(); + var boxBuilder = ImmutableArray.CreateBuilder(); + var cameraBuilder = ImmutableArray.CreateBuilder(); + var cubeBuilder = ImmutableArray.CreateBuilder(); + var effectBuilder = ImmutableArray.CreateBuilder(); + var ladderBuilder = ImmutableArray.CreateBuilder(); + var meshBuilder = ImmutableArray.CreateBuilder(); + var ropeBuilder = ImmutableArray.CreateBuilder(); + var skillBuilder = ImmutableArray.CreateBuilder(); + var soundBuilder = ImmutableArray.CreateBuilder(); + + foreach (Ms2Trigger trigger in triggers) { + switch (trigger) { + case Ms2TriggerActor actor: + actorBuilder.Add(actor); + break; + case Ms2TriggerAgent agent: + agentBuilder.Add(agent); + break; + case Ms2TriggerBox box: + boxBuilder.Add(box); + break; + case Ms2TriggerCamera camera: + cameraBuilder.Add(camera); + break; + case Ms2TriggerCube cube: + cubeBuilder.Add(cube); + break; + case Ms2TriggerEffect effect: + effectBuilder.Add(effect); + break; + case Ms2TriggerLadder ladder: + ladderBuilder.Add(ladder); + break; + case Ms2TriggerMesh mesh: + meshBuilder.Add(mesh); + break; + case Ms2TriggerRope rope: + ropeBuilder.Add(rope); + break; + case Ms2TriggerSkill skill: + skillBuilder.Add(skill); + break; + case Ms2TriggerSound sound: + soundBuilder.Add(sound); + break; + default: + continue; + } + + builder.Add(trigger.TriggerId, trigger); + } + + this.triggers = builder.ToImmutable(); + Actors = actorBuilder.ToImmutable(); + Agents = agentBuilder.ToImmutable(); + Boxes = boxBuilder.ToImmutable(); + Cameras = cameraBuilder.ToImmutable(); + Cubes = cubeBuilder.ToImmutable(); + Effects = effectBuilder.ToImmutable(); + Ladders = ladderBuilder.ToImmutable(); + Meshes = meshBuilder.ToImmutable(); + Ropes = ropeBuilder.ToImmutable(); + Skills = skillBuilder.ToImmutable(); + Sounds = soundBuilder.ToImmutable(); + } + + public bool TryGet(int key, [NotNullWhen(true)] out T? trigger) where T : Ms2Trigger { + triggers.TryGetValue(key, out Ms2Trigger? result); + trigger = result as T; + return trigger != null; + } + + public int Count => triggers.Count; + public Ms2Trigger this[int key] => triggers[key]; + + public IEnumerable Keys => triggers.Keys; + public IEnumerable Values => triggers.Values; + + public bool ContainsKey(int key) => triggers.ContainsKey(key); + public bool TryGetValue(int key, [NotNullWhen(true)] out Ms2Trigger? value) => triggers.TryGetValue(key, out value); + + public IEnumerator> GetEnumerator() => triggers.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => triggers.GetEnumerator(); +} diff --git a/Maple2.Database/Storage/Web/WebStorage.cs b/Maple2.Database/Storage/Web/WebStorage.cs index 08fda35ee..67f89c442 100644 --- a/Maple2.Database/Storage/Web/WebStorage.cs +++ b/Maple2.Database/Storage/Web/WebStorage.cs @@ -1,63 +1,63 @@ -using Maple2.Database.Context; -using Maple2.Database.Extensions; -using Maple2.Model.Enum; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using UgcResource = Maple2.Model.Game.UgcResource; - -namespace Maple2.Database.Storage; - -public partial class WebStorage { - private readonly ILogger logger; - private readonly DbContextOptions options; - - public WebStorage(DbContextOptions options, ILogger logger) { - this.options = options; - this.logger = logger; - } - - public Request Context() { - // We use NoTracking by default since most requests are Read or Overwrite. - // If we need tracking for modifying data, we can set it individually as needed. - var context = new WebContext(options); - context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; - - return new Request(context, logger); - } - - public partial class Request(WebContext context, ILogger logger) : DatabaseRequest(context, logger) { - - public UgcResource? CreateUgc(UgcType type, long ownerId) { - var model = new Model.UgcResource { - Type = type, - OwnerId = ownerId, - }; - Context.UgcResource.Add(model); - - return Context.TrySaveChanges() ? model : null; - } - - public bool SaveUgc(UgcResource ugc, long ownerId) { - Model.UgcResource model = ugc; - model.OwnerId = ownerId; - Context.UgcResource.Update(model); - - return SaveChanges(); - } - - public bool UpdatePath(long id, string path) { - Model.UgcResource? model = Context.UgcResource.Find(id); - if (model == null) { - return false; - } - - model.Path = path; - Context.UgcResource.Update(model); - return SaveChanges(); - } - - public UgcResource? GetUgc(long id) { - return Context.UgcResource.Find(id); - } - } -} +using Maple2.Database.Context; +using Maple2.Database.Extensions; +using Maple2.Model.Enum; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using UgcResource = Maple2.Model.Game.UgcResource; + +namespace Maple2.Database.Storage; + +public partial class WebStorage { + private readonly ILogger logger; + private readonly DbContextOptions options; + + public WebStorage(DbContextOptions options, ILogger logger) { + this.options = options; + this.logger = logger; + } + + public Request Context() { + // We use NoTracking by default since most requests are Read or Overwrite. + // If we need tracking for modifying data, we can set it individually as needed. + var context = new WebContext(options); + context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; + + return new Request(context, logger); + } + + public partial class Request(WebContext context, ILogger logger) : DatabaseRequest(context, logger) { + + public UgcResource? CreateUgc(UgcType type, long ownerId) { + var model = new Model.UgcResource { + Type = type, + OwnerId = ownerId, + }; + Context.UgcResource.Add(model); + + return Context.TrySaveChanges() ? model : null; + } + + public bool SaveUgc(UgcResource ugc, long ownerId) { + Model.UgcResource model = ugc; + model.OwnerId = ownerId; + Context.UgcResource.Update(model); + + return SaveChanges(); + } + + public bool UpdatePath(long id, string path) { + Model.UgcResource? model = Context.UgcResource.Find(id); + if (model == null) { + return false; + } + + model.Path = path; + Context.UgcResource.Update(model); + return SaveChanges(); + } + + public UgcResource? GetUgc(long id) { + return Context.UgcResource.Find(id); + } + } +} diff --git a/Maple2.File.Ingest/Helpers/NifParserHelper.cs b/Maple2.File.Ingest/Helpers/NifParserHelper.cs index 4b07e8a6f..c4b1c49e0 100644 --- a/Maple2.File.Ingest/Helpers/NifParserHelper.cs +++ b/Maple2.File.Ingest/Helpers/NifParserHelper.cs @@ -1,110 +1,110 @@ -using Maple2.File.IO.Nif; -using Maple2.File.Parser; -using Maple2.Model.Metadata; -using Maple2.Tools.VectorMath; -using System.Numerics; - -namespace Maple2.File.Ingest.Helpers; - -public static class NifParserHelper { - public static Dictionary nifDocuments { get; private set; } = []; - public static Dictionary nifBounds { get; private set; } = []; - public static Dictionary nxsMeshIndexMap { get; private set; } = []; - public static List nxsMeshes { get; private set; } = []; - - public static void ParseNif(List modelReaders) { - Console.WriteLine("Parsing NIF files..."); - - NifParser nifParser = new(modelReaders); - - Parallel.ForEach(nifParser.Parse(), (item) => { - ParseNifDocument(item.llid, item.document); - }); - - nifDocuments = nifDocuments.OrderBy(item => item.Key).ToDictionary(item => item.Key, item => item.Value); - - foreach (KeyValuePair nifDocument in nifDocuments) { - nifBounds.Add(nifDocument.Key, GenerateNxsMeshMetadata(nifDocument.Value)); - } - } - - private static void ParseNifDocument(uint llid, NifDocument document) { - try { - document.Parse(); - } catch (InvalidOperationException ex) { - if (ex.InnerException is NifVersionNotSupportedException) { -#if DEBUG - if (ex.InnerException.Message.StartsWith("[/library/triggerslibrary/gamebryodata/generic")) { - return; - } - - if (ex.InnerException.Message.StartsWith("[/model/tool/shadersphere.nif]:")) { - return; - } - - if (ex.InnerException.Message.StartsWith("[/model/tool/triggerproxy_")) { - return; - } - - Console.WriteLine(ex.InnerException.Message); -#endif - return; - } - throw; - } - - lock (nifDocuments) { - nifDocuments[llid] = document; - } - } - - private static BoundingBox3 GenerateNxsMeshMetadata(NifDocument document) { - foreach (NiPhysXMeshDesc meshDesc in document.Blocks.OfType()) { - string meshDataString = Convert.ToBase64String(meshDesc.MeshData); - if (!nxsMeshIndexMap.ContainsKey(meshDataString)) { - int value = nxsMeshes.Count + 1; // 1-based index - nxsMeshIndexMap[meshDataString] = value; - - PhysXMesh mesh = new PhysXMesh(meshDesc.MeshData); - - nxsMeshes.Add(new NxsMeshMetadata(value, meshDesc.MeshData, BoundingBox3.Compute(mesh.Vertices))); - } - } - - BoundingBox3 bounds = new BoundingBox3(); - bool firstSet = true; - - foreach (NifBlock item in document.Blocks) { - if (item is not NiPhysXProp prop) { - continue; - } - - if (prop.Snapshot is null) { - continue; - } - - foreach (NiPhysXActorDesc actorDesc in prop.Snapshot.Actors) { - foreach (NiPhysXShapeDesc shapeDesc in actorDesc.ShapeDescriptions) { - if (shapeDesc.Mesh is null) { - continue; - } - - PhysXMesh mesh = new PhysXMesh(shapeDesc.Mesh.MeshData); - Matrix4x4 transform = Matrix4x4.CreateScale(prop.PhysXToWorldScale) * actorDesc.Poses[0] * shapeDesc.LocalPose; - BoundingBox3 meshBounds = BoundingBox3.Transform(BoundingBox3.Compute(mesh.Vertices), transform); - - if (!firstSet) { - bounds = bounds.Expand(meshBounds); - - continue; - } - - bounds = meshBounds; - firstSet = false; - } - } - } - - return bounds; - } -} +using Maple2.File.IO.Nif; +using Maple2.File.Parser; +using Maple2.Model.Metadata; +using Maple2.Tools.VectorMath; +using System.Numerics; + +namespace Maple2.File.Ingest.Helpers; + +public static class NifParserHelper { + public static Dictionary nifDocuments { get; private set; } = []; + public static Dictionary nifBounds { get; private set; } = []; + public static Dictionary nxsMeshIndexMap { get; private set; } = []; + public static List nxsMeshes { get; private set; } = []; + + public static void ParseNif(List modelReaders) { + Console.WriteLine("Parsing NIF files..."); + + NifParser nifParser = new(modelReaders); + + Parallel.ForEach(nifParser.Parse(), (item) => { + ParseNifDocument(item.llid, item.document); + }); + + nifDocuments = nifDocuments.OrderBy(item => item.Key).ToDictionary(item => item.Key, item => item.Value); + + foreach (KeyValuePair nifDocument in nifDocuments) { + nifBounds.Add(nifDocument.Key, GenerateNxsMeshMetadata(nifDocument.Value)); + } + } + + private static void ParseNifDocument(uint llid, NifDocument document) { + try { + document.Parse(); + } catch (InvalidOperationException ex) { + if (ex.InnerException is NifVersionNotSupportedException) { +#if DEBUG + if (ex.InnerException.Message.StartsWith("[/library/triggerslibrary/gamebryodata/generic")) { + return; + } + + if (ex.InnerException.Message.StartsWith("[/model/tool/shadersphere.nif]:")) { + return; + } + + if (ex.InnerException.Message.StartsWith("[/model/tool/triggerproxy_")) { + return; + } + + Console.WriteLine(ex.InnerException.Message); +#endif + return; + } + throw; + } + + lock (nifDocuments) { + nifDocuments[llid] = document; + } + } + + private static BoundingBox3 GenerateNxsMeshMetadata(NifDocument document) { + foreach (NiPhysXMeshDesc meshDesc in document.Blocks.OfType()) { + string meshDataString = Convert.ToBase64String(meshDesc.MeshData); + if (!nxsMeshIndexMap.ContainsKey(meshDataString)) { + int value = nxsMeshes.Count + 1; // 1-based index + nxsMeshIndexMap[meshDataString] = value; + + PhysXMesh mesh = new PhysXMesh(meshDesc.MeshData); + + nxsMeshes.Add(new NxsMeshMetadata(value, meshDesc.MeshData, BoundingBox3.Compute(mesh.Vertices))); + } + } + + BoundingBox3 bounds = new BoundingBox3(); + bool firstSet = true; + + foreach (NifBlock item in document.Blocks) { + if (item is not NiPhysXProp prop) { + continue; + } + + if (prop.Snapshot is null) { + continue; + } + + foreach (NiPhysXActorDesc actorDesc in prop.Snapshot.Actors) { + foreach (NiPhysXShapeDesc shapeDesc in actorDesc.ShapeDescriptions) { + if (shapeDesc.Mesh is null) { + continue; + } + + PhysXMesh mesh = new PhysXMesh(shapeDesc.Mesh.MeshData); + Matrix4x4 transform = Matrix4x4.CreateScale(prop.PhysXToWorldScale) * actorDesc.Poses[0] * shapeDesc.LocalPose; + BoundingBox3 meshBounds = BoundingBox3.Transform(BoundingBox3.Compute(mesh.Vertices), transform); + + if (!firstSet) { + bounds = bounds.Expand(meshBounds); + + continue; + } + + bounds = meshBounds; + firstSet = false; + } + } + } + + return bounds; + } +} diff --git a/Maple2.File.Ingest/Mapper/AchievementMapper.cs b/Maple2.File.Ingest/Mapper/AchievementMapper.cs index 0ef1966d9..31cf603bd 100644 --- a/Maple2.File.Ingest/Mapper/AchievementMapper.cs +++ b/Maple2.File.Ingest/Mapper/AchievementMapper.cs @@ -1,62 +1,62 @@ -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Enum; -using Maple2.File.Parser.Xml.Achieve; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using ConditionType = Maple2.Model.Enum.ConditionType; - -namespace Maple2.File.Ingest.Mapper; - -public class AchievementMapper : TypeMapper { - private readonly AchieveParser parser; - - public AchievementMapper(M2dReader xmlReader) { - parser = new AchieveParser(xmlReader); - } - - protected override IEnumerable Map() { - foreach ((int id, string name, AchieveData data) in parser.Parse()) { - var grades = new Dictionary(); - foreach (Grade grade in data.grade) { - grades.Add(grade.value, new AchievementMetadataGrade( - Grade: grade.value, - Condition: new ConditionMetadata( - Type: (ConditionType) grade.condition.type, - Value: grade.condition.value == 0 ? 1 : grade.condition.value, - Codes: grade.condition.code.ConvertCodes(), - Target: grade.condition.target.ConvertCodes()), - Reward: grade.reward is null || grade.reward.type == AchieveRewardType.unknown ? null : new AchievementMetadataReward( - Type: (AchievementRewardType) grade.reward.type, - Code: grade.reward.code, - Value: grade.reward.value, - Rank: grade.reward.rank))); - } - - var category = AchievementCategory.Life; - string[] tags = data.categoryTag; - if (data.categoryTag.Length > 0) { - // skip the first in the array and use it as the trophy category - category = GetTrophyCategory(tags[0]); - tags = tags.Skip(1).ToArray(); - } - - yield return new AchievementMetadata( - Id: id, - Name: name, - AccountWide: data.account, - Category: category, - CategoryTags: tags, - Grades: grades); - } - } - - private static AchievementCategory GetTrophyCategory(string tag) { - return tag switch { - "combat" => AchievementCategory.Combat, - "adventure" => AchievementCategory.Adventure, - "living" => AchievementCategory.Life, - _ => AchievementCategory.Life, - }; - } -} +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Enum; +using Maple2.File.Parser.Xml.Achieve; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using ConditionType = Maple2.Model.Enum.ConditionType; + +namespace Maple2.File.Ingest.Mapper; + +public class AchievementMapper : TypeMapper { + private readonly AchieveParser parser; + + public AchievementMapper(M2dReader xmlReader) { + parser = new AchieveParser(xmlReader); + } + + protected override IEnumerable Map() { + foreach ((int id, string name, AchieveData data) in parser.Parse()) { + var grades = new Dictionary(); + foreach (Grade grade in data.grade) { + grades.Add(grade.value, new AchievementMetadataGrade( + Grade: grade.value, + Condition: new ConditionMetadata( + Type: (ConditionType) grade.condition.type, + Value: grade.condition.value == 0 ? 1 : grade.condition.value, + Codes: grade.condition.code.ConvertCodes(), + Target: grade.condition.target.ConvertCodes()), + Reward: grade.reward is null || grade.reward.type == AchieveRewardType.unknown ? null : new AchievementMetadataReward( + Type: (AchievementRewardType) grade.reward.type, + Code: grade.reward.code, + Value: grade.reward.value, + Rank: grade.reward.rank))); + } + + var category = AchievementCategory.Life; + string[] tags = data.categoryTag; + if (data.categoryTag.Length > 0) { + // skip the first in the array and use it as the trophy category + category = GetTrophyCategory(tags[0]); + tags = tags.Skip(1).ToArray(); + } + + yield return new AchievementMetadata( + Id: id, + Name: name, + AccountWide: data.account, + Category: category, + CategoryTags: tags, + Grades: grades); + } + } + + private static AchievementCategory GetTrophyCategory(string tag) { + return tag switch { + "combat" => AchievementCategory.Combat, + "adventure" => AchievementCategory.Adventure, + "living" => AchievementCategory.Life, + _ => AchievementCategory.Life, + }; + } +} diff --git a/Maple2.File.Ingest/Mapper/AdditionalEffectMapper.cs b/Maple2.File.Ingest/Mapper/AdditionalEffectMapper.cs index 428651366..b657067cb 100644 --- a/Maple2.File.Ingest/Mapper/AdditionalEffectMapper.cs +++ b/Maple2.File.Ingest/Mapper/AdditionalEffectMapper.cs @@ -1,265 +1,265 @@ -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Xml.AdditionalEffect; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.Tools.Extensions; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -namespace Maple2.File.Ingest.Mapper; - -public class AdditionalEffectMapper : TypeMapper { - private readonly AdditionalEffectParser parser; - - public AdditionalEffectMapper(M2dReader xmlReader) { - parser = new AdditionalEffectParser(xmlReader); - } - - protected override IEnumerable Map() { - foreach ((int id, IList datas) in parser.Parse()) { - foreach (AdditionalEffectData data in datas) { - yield return new AdditionalEffectMetadata( - Id: id, - Level: data.BasicProperty.level, - Condition: data.beginCondition.Convert(), - Property: new AdditionalEffectMetadataProperty( - Type: (BuffType) data.BasicProperty.buffType, - SubType: (BuffSubType) data.BasicProperty.buffSubType, - Category: (BuffCategory) data.BasicProperty.buffCategory, - EventType: (BuffEventType) data.BasicProperty.eventBuffType, - Group: data.BasicProperty.group, - DurationTick: data.BasicProperty.durationTick, - IntervalTick: data.BasicProperty.intervalTick, - DelayTick: data.BasicProperty.delayTick, - MaxCount: data.BasicProperty.maxBuffCount, - UseInGameTime: data.BasicProperty.useInGameTime, - KeepOnDeath: data.BasicProperty.deadKeepEffect, - RemoveOnLogout: data.BasicProperty.logoutClearEffect, - RemoveOnLeaveField: data.BasicProperty.leaveFieldClearEffect, - RemoveOnPvpZone: data.BasicProperty.clearEffectFromPVPZone, - KeepOnEnterPvpZone: data.BasicProperty.doNotClearEffectFromEnterPVPZone, - CasterIndividualBuff: data.BasicProperty.casterIndividualEffect, - Exp: data.ExpProperty.value, - CooldownTime: (int) TimeSpan.FromSeconds(float.TryParse(data.BasicProperty.coolDownTime, out float cooldown) ? cooldown : 0).TotalMilliseconds, - RideId: data.RideeProperty == null! ? 0 : data.RideeProperty.rideeID, - ImmuneBreak: data.MotionProperty.abnormalImmuneBreak, - Stun: data.MotionProperty.stun, - KeepCondition: (BuffKeepCondition) data.BasicProperty.keepCondition, - ResetCondition: (BuffResetCondition) data.BasicProperty.resetCondition, - DotCondition: (BuffDotCondition) data.BasicProperty.dotCondition, - ClearOnDistanceFromCaster: int.TryParse(data.BasicProperty.clearDistanceFromCaster, out int distance) ? distance : 0), - Consume: new AdditionalEffectMetadataConsume( - HpRate: data.ConsumeProperty.hpRate, - SpRate: data.ConsumeProperty.spRate), - Reflect: Convert(data.ReflectProperty), - Update: Convert(data), - Status: Convert(data.StatusProperty, data.OffensiveProperty, data.DefensiveProperty), - Recovery: Convert(data.RecoveryProperty), - Dot: new AdditionalEffectMetadataDot( - Damage: Convert(data.DotDamageProperty), - Buff: Convert(data.DotBuffProperty)), - Shield: Convert(data.ShieldProperty), - InvokeEffect: Convert(data.InvokeEffectProperty), - ModifyOverlapCount: data.ModifyOverlapCountProperty == null! ? [] : data.ModifyOverlapCountProperty.effectCodes - .Zip(data.ModifyOverlapCountProperty.offsetCounts) - .Select(tuple => new AdditionalEffectMetadataModifyOverlapCount( - Id: tuple.First, - OffsetCount: tuple.Second)) - .ToArray(), - Skills: data.conditionSkill.Concat(data.splashSkill).Where(skill => !skill.activeByIntervalTick).Select(skill => skill.Convert()).ToArray(), - TickSkills: data.conditionSkill.Concat(data.splashSkill).Where(skill => skill.activeByIntervalTick).Select(skill => skill.Convert()).ToArray()); - } - } - } - - private static AdditionalEffectMetadataUpdate Convert(AdditionalEffectData data) { - CancelEffectProperty cancel = data.CancelEffectProperty; - AdditionalEffectMetadataUpdate.CancelEffect? cancelEffect = null; - if (cancel.cancelEffectCodes.Length != 0 || cancel.cancelBuffCategories.Length != 0) { - cancelEffect = new AdditionalEffectMetadataUpdate.CancelEffect( - CheckSameCaster: cancel.cancelCheckSameCaster, - PassiveEffect: cancel.cancelPassiveEffect, - Ids: cancel.cancelEffectCodes, - Categories: Array.ConvertAll(cancel.cancelBuffCategories, category => (BuffCategory) category)); - } - - ModifyEffectDurationProperty modify = data.ModifyEffectDurationProperty; - var modifyDuration = new AdditionalEffectMetadataUpdate.ModifyDuration[modify.effectCodes.Length]; - for (int i = 0; i < modifyDuration.Length; i++) { - modifyDuration[i] = new AdditionalEffectMetadataUpdate.ModifyDuration(modify.effectCodes[i], modify.durationFactors[i], modify.durationValues[i]); - } - - return new AdditionalEffectMetadataUpdate( - Cancel: cancelEffect, - ImmuneIds: data.ImmuneEffectProperty.immuneEffectCodes, - ImmuneCategories: Array.ConvertAll(data.ImmuneEffectProperty.immuneBuffCategories, category => (BuffCategory) category), - ResetCooldown: data.ResetSkillCoolDownTimeProperty.skillCodes, - Duration: modifyDuration); - } - - private static AdditionalEffectMetadataReflect Convert(ReflectProperty reflect) { - var values = new Dictionary(); - var rates = new Dictionary(); - - values.AddIfNotDefault(BasicAttribute.PhysicalAtk, reflect.physicalReflectionValue); - values.AddIfNotDefault(BasicAttribute.MagicalAtk, reflect.magicalReflectionValue); - - rates.AddIfNotDefault(BasicAttribute.PhysicalAtk, reflect.physicalReflectionRate); - rates.AddIfNotDefault(BasicAttribute.MagicalAtk, reflect.magicalReflectionRate); - return new AdditionalEffectMetadataReflect( - Rate: reflect.reflectionRate, - EffectId: reflect.reflectionAdditionalEffectId, - EffectLevel: reflect.reflectionAdditionalEffectLevel, - Count: reflect.reflectionCount, - PhysicalRateLimit: reflect.physicalReflectionRateLimit, - MagicalRateLimit: reflect.magicalReflectionRateLimit, - Values: values, - Rates: rates); - } - - private static AdditionalEffectMetadataStatus Convert(StatusProperty status, OffensiveProperty offensive, DefensiveProperty defensive) { - var values = new Dictionary(); - var rates = new Dictionary(); - var specialValues = new Dictionary(); - var specialRates = new Dictionary(); - - if (status.Stat != null) { - foreach (BasicAttribute attribute in Enum.GetValues()) { - values.AddIfNotDefault(attribute, status.Stat.Value((byte) attribute)); - rates.AddIfNotDefault(attribute, status.Stat.Rate((byte) attribute)); - } - } - - if (status.SpecialAbility != null) { - foreach (SpecialAttribute attribute in Enum.GetValues()) { - byte attributeIndex = attribute.OptionIndex(); - if (attributeIndex == byte.MaxValue) { - continue; - } - - specialValues.AddIfNotDefault(attribute, status.SpecialAbility.Value(attributeIndex)); - specialRates.AddIfNotDefault(attribute, status.SpecialAbility.Rate(attributeIndex)); - } - } - - specialValues.AddIfNotDefault(SpecialAttribute.OffensiveMagicalDamage, offensive.mapDamageV); - specialRates.AddIfNotDefault(SpecialAttribute.OffensiveMagicalDamage, offensive.mapDamageR); - specialValues.AddIfNotDefault(SpecialAttribute.OffensivePhysicalDamage, offensive.papDamageV); - specialRates.AddIfNotDefault(SpecialAttribute.OffensivePhysicalDamage, offensive.papDamageR); - - var resistances = new Dictionary(); - resistances.AddIfNotDefault(BasicAttribute.MaxWeaponAtk, status.resWapR); - resistances.AddIfNotDefault(BasicAttribute.BonusAtk, status.resBapR); - resistances.AddIfNotDefault(BasicAttribute.CriticalDamage, status.resCadR); - resistances.AddIfNotDefault(BasicAttribute.Accuracy, status.resAtpR); - resistances.AddIfNotDefault(BasicAttribute.Evasion, status.resEvpR); - resistances.AddIfNotDefault(BasicAttribute.Piercing, status.resPenR); - resistances.AddIfNotDefault(BasicAttribute.AttackSpeed, status.resAspR); - - Debug.Assert(status.compulsionEventTypes.Length <= 1 && status.compulsionEventRate.Length <= 1); - var compulsionEventType = BuffCompulsionEventType.None; - if (status.compulsionEventTypes.Length > 0) { - compulsionEventType = (BuffCompulsionEventType) status.compulsionEventTypes[0]; - } - - AdditionalEffectMetadataStatus.CompulsionEvent? compulsionEvent = null; - if (compulsionEventType != BuffCompulsionEventType.None) { - float compulsionEventRate = 0; - if (status.compulsionEventRate.Length > 0) { - compulsionEventRate = status.compulsionEventRate[0]; - } - - compulsionEvent = new AdditionalEffectMetadataStatus.CompulsionEvent(compulsionEventType, compulsionEventRate, status.compulsionEventSkillCodes); - } else { - // Ensure these fields are not set without a CompulsionEventType - Debug.Assert(status.compulsionEventRate.Length == 0 && status.compulsionEventSkillCodes.Length == 0); - } - - AdditionalEffectMetadataStatus.StatConversion? conversion = null; - if (status.statChangeRate != 0) { - conversion = new AdditionalEffectMetadataStatus.StatConversion((BasicAttribute) status.statChangeBase, (BasicAttribute) status.statChangeResult, status.statChangeRate); - } - - return new AdditionalEffectMetadataStatus( - Values: values, - Rates: rates, - SpecialValues: specialValues, - SpecialRates: specialRates, - Resistances: resistances, - DeathResistanceHp: status.deathResistanceHP, - Compulsion: compulsionEvent, - Conversion: conversion, - ImmuneBreak: offensive.hitImmuneBreak, - Invincible: defensive.invincible != 0); - } - - private static AdditionalEffectMetadataRecovery? Convert(RecoveryProperty recovery) { - if (recovery is { RecoveryRate: <= 0, hpValue: <= 0, hpRate: <= 0, spValue: <= 0, spRate: <= 0, spConsumeRate: <= 0, epValue: <= 0, epRate: <= 0 }) { - return null; - } - - return new AdditionalEffectMetadataRecovery( - RecoveryRate: recovery.RecoveryRate, - HpValue: recovery.hpValue, - HpRate: recovery.hpRate, - HpConsumeRate: recovery.hpConsumeRate, - SpValue: recovery.spValue, - SpRate: recovery.spRate, - SpConsumeRate: recovery.spConsumeRate, - EpValue: recovery.epValue, - EpRate: recovery.epRate, - NotCrit: recovery.disableCriticalRecovery); - } - - private static AdditionalEffectMetadataDot.DotDamage? Convert(DotDamageProperty dotDamage) { - if (dotDamage.type <= 0) { - return null; - } - - return new AdditionalEffectMetadataDot.DotDamage( - Type: (AttackType) dotDamage.type, - Element: (Element) dotDamage.element, - UseGrade: dotDamage.useGrade, - Rate: dotDamage.rate, - HpValue: (int) dotDamage.value, - SpValue: dotDamage.spValue, - EpValue: dotDamage.epValue, - DamageByTargetMaxHp: dotDamage.damageByTargetMaxHP, - RecoverHpByDamage: dotDamage.casterRecoveryHpByDamage, - IsConstDamage: dotDamage.isConstDotDamageValue, - NotKill: dotDamage.notKill); - } - - private static AdditionalEffectMetadataDot.DotBuff? Convert(DotBuffProperty? dotBuff) { - if (dotBuff is not { buffID: > 0 }) { - return null; - } - - return new AdditionalEffectMetadataDot.DotBuff(Target: (SkillTargetType) dotBuff.target, Id: dotBuff.buffID, Level: dotBuff.buffLevel); - } - - private static AdditionalEffectMetadataShield? Convert(ShieldProperty shield) { - if (shield is { hpValue: <= 0, hpByTargetMaxHP: <= 0 }) { - return null; - } - - return new AdditionalEffectMetadataShield(HpValue: shield.hpValue, HpByTargetMaxHp: shield.hpByTargetMaxHP); - } - - [return: NotNullIfNotNull(nameof(invokeEffect))] - private static AdditionalEffectMetadataInvokeEffect? Convert(InvokeEffectProperty? invokeEffect) { - if (invokeEffect is null) { - return null; - } - - return new AdditionalEffectMetadataInvokeEffect( - Types: invokeEffect.types.Select(type => (InvokeEffectType) type).ToArray(), - Values: invokeEffect.values, - Rates: invokeEffect.rates, - EffectId: invokeEffect.effectID, - EffectGroupId: invokeEffect.effectGroupID, - SkillId: invokeEffect.skillID, - SkillGroupId: invokeEffect.skillGroupID); - } -} +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Xml.AdditionalEffect; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.Tools.Extensions; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace Maple2.File.Ingest.Mapper; + +public class AdditionalEffectMapper : TypeMapper { + private readonly AdditionalEffectParser parser; + + public AdditionalEffectMapper(M2dReader xmlReader) { + parser = new AdditionalEffectParser(xmlReader); + } + + protected override IEnumerable Map() { + foreach ((int id, IList datas) in parser.Parse()) { + foreach (AdditionalEffectData data in datas) { + yield return new AdditionalEffectMetadata( + Id: id, + Level: data.BasicProperty.level, + Condition: data.beginCondition.Convert(), + Property: new AdditionalEffectMetadataProperty( + Type: (BuffType) data.BasicProperty.buffType, + SubType: (BuffSubType) data.BasicProperty.buffSubType, + Category: (BuffCategory) data.BasicProperty.buffCategory, + EventType: (BuffEventType) data.BasicProperty.eventBuffType, + Group: data.BasicProperty.group, + DurationTick: data.BasicProperty.durationTick, + IntervalTick: data.BasicProperty.intervalTick, + DelayTick: data.BasicProperty.delayTick, + MaxCount: data.BasicProperty.maxBuffCount, + UseInGameTime: data.BasicProperty.useInGameTime, + KeepOnDeath: data.BasicProperty.deadKeepEffect, + RemoveOnLogout: data.BasicProperty.logoutClearEffect, + RemoveOnLeaveField: data.BasicProperty.leaveFieldClearEffect, + RemoveOnPvpZone: data.BasicProperty.clearEffectFromPVPZone, + KeepOnEnterPvpZone: data.BasicProperty.doNotClearEffectFromEnterPVPZone, + CasterIndividualBuff: data.BasicProperty.casterIndividualEffect, + Exp: data.ExpProperty.value, + CooldownTime: (int) TimeSpan.FromSeconds(float.TryParse(data.BasicProperty.coolDownTime, out float cooldown) ? cooldown : 0).TotalMilliseconds, + RideId: data.RideeProperty == null! ? 0 : data.RideeProperty.rideeID, + ImmuneBreak: data.MotionProperty.abnormalImmuneBreak, + Stun: data.MotionProperty.stun, + KeepCondition: (BuffKeepCondition) data.BasicProperty.keepCondition, + ResetCondition: (BuffResetCondition) data.BasicProperty.resetCondition, + DotCondition: (BuffDotCondition) data.BasicProperty.dotCondition, + ClearOnDistanceFromCaster: int.TryParse(data.BasicProperty.clearDistanceFromCaster, out int distance) ? distance : 0), + Consume: new AdditionalEffectMetadataConsume( + HpRate: data.ConsumeProperty.hpRate, + SpRate: data.ConsumeProperty.spRate), + Reflect: Convert(data.ReflectProperty), + Update: Convert(data), + Status: Convert(data.StatusProperty, data.OffensiveProperty, data.DefensiveProperty), + Recovery: Convert(data.RecoveryProperty), + Dot: new AdditionalEffectMetadataDot( + Damage: Convert(data.DotDamageProperty), + Buff: Convert(data.DotBuffProperty)), + Shield: Convert(data.ShieldProperty), + InvokeEffect: Convert(data.InvokeEffectProperty), + ModifyOverlapCount: data.ModifyOverlapCountProperty == null! ? [] : data.ModifyOverlapCountProperty.effectCodes + .Zip(data.ModifyOverlapCountProperty.offsetCounts) + .Select(tuple => new AdditionalEffectMetadataModifyOverlapCount( + Id: tuple.First, + OffsetCount: tuple.Second)) + .ToArray(), + Skills: data.conditionSkill.Concat(data.splashSkill).Where(skill => !skill.activeByIntervalTick).Select(skill => skill.Convert()).ToArray(), + TickSkills: data.conditionSkill.Concat(data.splashSkill).Where(skill => skill.activeByIntervalTick).Select(skill => skill.Convert()).ToArray()); + } + } + } + + private static AdditionalEffectMetadataUpdate Convert(AdditionalEffectData data) { + CancelEffectProperty cancel = data.CancelEffectProperty; + AdditionalEffectMetadataUpdate.CancelEffect? cancelEffect = null; + if (cancel.cancelEffectCodes.Length != 0 || cancel.cancelBuffCategories.Length != 0) { + cancelEffect = new AdditionalEffectMetadataUpdate.CancelEffect( + CheckSameCaster: cancel.cancelCheckSameCaster, + PassiveEffect: cancel.cancelPassiveEffect, + Ids: cancel.cancelEffectCodes, + Categories: Array.ConvertAll(cancel.cancelBuffCategories, category => (BuffCategory) category)); + } + + ModifyEffectDurationProperty modify = data.ModifyEffectDurationProperty; + var modifyDuration = new AdditionalEffectMetadataUpdate.ModifyDuration[modify.effectCodes.Length]; + for (int i = 0; i < modifyDuration.Length; i++) { + modifyDuration[i] = new AdditionalEffectMetadataUpdate.ModifyDuration(modify.effectCodes[i], modify.durationFactors[i], modify.durationValues[i]); + } + + return new AdditionalEffectMetadataUpdate( + Cancel: cancelEffect, + ImmuneIds: data.ImmuneEffectProperty.immuneEffectCodes, + ImmuneCategories: Array.ConvertAll(data.ImmuneEffectProperty.immuneBuffCategories, category => (BuffCategory) category), + ResetCooldown: data.ResetSkillCoolDownTimeProperty.skillCodes, + Duration: modifyDuration); + } + + private static AdditionalEffectMetadataReflect Convert(ReflectProperty reflect) { + var values = new Dictionary(); + var rates = new Dictionary(); + + values.AddIfNotDefault(BasicAttribute.PhysicalAtk, reflect.physicalReflectionValue); + values.AddIfNotDefault(BasicAttribute.MagicalAtk, reflect.magicalReflectionValue); + + rates.AddIfNotDefault(BasicAttribute.PhysicalAtk, reflect.physicalReflectionRate); + rates.AddIfNotDefault(BasicAttribute.MagicalAtk, reflect.magicalReflectionRate); + return new AdditionalEffectMetadataReflect( + Rate: reflect.reflectionRate, + EffectId: reflect.reflectionAdditionalEffectId, + EffectLevel: reflect.reflectionAdditionalEffectLevel, + Count: reflect.reflectionCount, + PhysicalRateLimit: reflect.physicalReflectionRateLimit, + MagicalRateLimit: reflect.magicalReflectionRateLimit, + Values: values, + Rates: rates); + } + + private static AdditionalEffectMetadataStatus Convert(StatusProperty status, OffensiveProperty offensive, DefensiveProperty defensive) { + var values = new Dictionary(); + var rates = new Dictionary(); + var specialValues = new Dictionary(); + var specialRates = new Dictionary(); + + if (status.Stat != null) { + foreach (BasicAttribute attribute in Enum.GetValues()) { + values.AddIfNotDefault(attribute, status.Stat.Value((byte) attribute)); + rates.AddIfNotDefault(attribute, status.Stat.Rate((byte) attribute)); + } + } + + if (status.SpecialAbility != null) { + foreach (SpecialAttribute attribute in Enum.GetValues()) { + byte attributeIndex = attribute.OptionIndex(); + if (attributeIndex == byte.MaxValue) { + continue; + } + + specialValues.AddIfNotDefault(attribute, status.SpecialAbility.Value(attributeIndex)); + specialRates.AddIfNotDefault(attribute, status.SpecialAbility.Rate(attributeIndex)); + } + } + + specialValues.AddIfNotDefault(SpecialAttribute.OffensiveMagicalDamage, offensive.mapDamageV); + specialRates.AddIfNotDefault(SpecialAttribute.OffensiveMagicalDamage, offensive.mapDamageR); + specialValues.AddIfNotDefault(SpecialAttribute.OffensivePhysicalDamage, offensive.papDamageV); + specialRates.AddIfNotDefault(SpecialAttribute.OffensivePhysicalDamage, offensive.papDamageR); + + var resistances = new Dictionary(); + resistances.AddIfNotDefault(BasicAttribute.MaxWeaponAtk, status.resWapR); + resistances.AddIfNotDefault(BasicAttribute.BonusAtk, status.resBapR); + resistances.AddIfNotDefault(BasicAttribute.CriticalDamage, status.resCadR); + resistances.AddIfNotDefault(BasicAttribute.Accuracy, status.resAtpR); + resistances.AddIfNotDefault(BasicAttribute.Evasion, status.resEvpR); + resistances.AddIfNotDefault(BasicAttribute.Piercing, status.resPenR); + resistances.AddIfNotDefault(BasicAttribute.AttackSpeed, status.resAspR); + + Debug.Assert(status.compulsionEventTypes.Length <= 1 && status.compulsionEventRate.Length <= 1); + var compulsionEventType = BuffCompulsionEventType.None; + if (status.compulsionEventTypes.Length > 0) { + compulsionEventType = (BuffCompulsionEventType) status.compulsionEventTypes[0]; + } + + AdditionalEffectMetadataStatus.CompulsionEvent? compulsionEvent = null; + if (compulsionEventType != BuffCompulsionEventType.None) { + float compulsionEventRate = 0; + if (status.compulsionEventRate.Length > 0) { + compulsionEventRate = status.compulsionEventRate[0]; + } + + compulsionEvent = new AdditionalEffectMetadataStatus.CompulsionEvent(compulsionEventType, compulsionEventRate, status.compulsionEventSkillCodes); + } else { + // Ensure these fields are not set without a CompulsionEventType + Debug.Assert(status.compulsionEventRate.Length == 0 && status.compulsionEventSkillCodes.Length == 0); + } + + AdditionalEffectMetadataStatus.StatConversion? conversion = null; + if (status.statChangeRate != 0) { + conversion = new AdditionalEffectMetadataStatus.StatConversion((BasicAttribute) status.statChangeBase, (BasicAttribute) status.statChangeResult, status.statChangeRate); + } + + return new AdditionalEffectMetadataStatus( + Values: values, + Rates: rates, + SpecialValues: specialValues, + SpecialRates: specialRates, + Resistances: resistances, + DeathResistanceHp: status.deathResistanceHP, + Compulsion: compulsionEvent, + Conversion: conversion, + ImmuneBreak: offensive.hitImmuneBreak, + Invincible: defensive.invincible != 0); + } + + private static AdditionalEffectMetadataRecovery? Convert(RecoveryProperty recovery) { + if (recovery is { RecoveryRate: <= 0, hpValue: <= 0, hpRate: <= 0, spValue: <= 0, spRate: <= 0, spConsumeRate: <= 0, epValue: <= 0, epRate: <= 0 }) { + return null; + } + + return new AdditionalEffectMetadataRecovery( + RecoveryRate: recovery.RecoveryRate, + HpValue: recovery.hpValue, + HpRate: recovery.hpRate, + HpConsumeRate: recovery.hpConsumeRate, + SpValue: recovery.spValue, + SpRate: recovery.spRate, + SpConsumeRate: recovery.spConsumeRate, + EpValue: recovery.epValue, + EpRate: recovery.epRate, + NotCrit: recovery.disableCriticalRecovery); + } + + private static AdditionalEffectMetadataDot.DotDamage? Convert(DotDamageProperty dotDamage) { + if (dotDamage.type <= 0) { + return null; + } + + return new AdditionalEffectMetadataDot.DotDamage( + Type: (AttackType) dotDamage.type, + Element: (Element) dotDamage.element, + UseGrade: dotDamage.useGrade, + Rate: dotDamage.rate, + HpValue: (int) dotDamage.value, + SpValue: dotDamage.spValue, + EpValue: dotDamage.epValue, + DamageByTargetMaxHp: dotDamage.damageByTargetMaxHP, + RecoverHpByDamage: dotDamage.casterRecoveryHpByDamage, + IsConstDamage: dotDamage.isConstDotDamageValue, + NotKill: dotDamage.notKill); + } + + private static AdditionalEffectMetadataDot.DotBuff? Convert(DotBuffProperty? dotBuff) { + if (dotBuff is not { buffID: > 0 }) { + return null; + } + + return new AdditionalEffectMetadataDot.DotBuff(Target: (SkillTargetType) dotBuff.target, Id: dotBuff.buffID, Level: dotBuff.buffLevel); + } + + private static AdditionalEffectMetadataShield? Convert(ShieldProperty shield) { + if (shield is { hpValue: <= 0, hpByTargetMaxHP: <= 0 }) { + return null; + } + + return new AdditionalEffectMetadataShield(HpValue: shield.hpValue, HpByTargetMaxHp: shield.hpByTargetMaxHP); + } + + [return: NotNullIfNotNull(nameof(invokeEffect))] + private static AdditionalEffectMetadataInvokeEffect? Convert(InvokeEffectProperty? invokeEffect) { + if (invokeEffect is null) { + return null; + } + + return new AdditionalEffectMetadataInvokeEffect( + Types: invokeEffect.types.Select(type => (InvokeEffectType) type).ToArray(), + Values: invokeEffect.values, + Rates: invokeEffect.rates, + EffectId: invokeEffect.effectID, + EffectGroupId: invokeEffect.effectGroupID, + SkillId: invokeEffect.skillID, + SkillGroupId: invokeEffect.skillGroupID); + } +} diff --git a/Maple2.File.Ingest/Mapper/AiMapper.cs b/Maple2.File.Ingest/Mapper/AiMapper.cs index 77ca071e8..27c4dec58 100644 --- a/Maple2.File.Ingest/Mapper/AiMapper.cs +++ b/Maple2.File.Ingest/Mapper/AiMapper.cs @@ -1,540 +1,540 @@ -using M2dXmlGenerator; -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Xml.AI; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; - -namespace Maple2.File.Ingest.Mapper; - -public class AiMapper : TypeMapper { - private readonly AiParser parser; - - public AiMapper(M2dReader xmlReader) { - parser = new AiParser(xmlReader); - } - - protected override IEnumerable Map() { - foreach ((string name, NpcAi data) in parser.Parse()) { - List reserved = new List(); - List battle = new List(); - List battleEnd = new List(); - List aiPresets = new List(); - - foreach (Entry entry in data.Reserved) { - if (entry is not ConditionEntry node) { - continue; - } - - if (node is FeatureCondition feature && !FeatureLocaleFilter.FeatureEnabled(feature.feature)) { - continue; - } - - reserved.Add(MapCondition(node)); - } - - foreach (Entry entry in data.Battle) { - MapEntry(battle, entry); - } - - foreach (Entry entry in data.BattleEnd) { - MapEntry(battle, entry); - } - - foreach (Entry node in data.AiPresets) { - var childNodes = new List(); - - foreach (Entry entry in node.Entries) { - MapEntry(childNodes, entry); - } - - aiPresets.Add(new AiMetadata.AiPresetDefinition( - Name: node.name, - Entries: childNodes.ToArray() - )); - } - - yield return new AiMetadata( - Name: name, - Reserved: reserved.ToArray(), - Battle: battle.ToArray(), - BattleEnd: battleEnd.ToArray(), - AiPresets: aiPresets.ToArray() - ); - } - } - - AiMetadata.Condition MapCondition(ConditionEntry node) { - var childNodes = new List(); - - foreach (Entry entry in node.Entries) { - MapEntry(childNodes, entry); - } - - switch (node) { - case DistanceOverCondition distanceOver: - return new AiMetadata.DistanceOverCondition( - Name: node.name, - Entries: childNodes.ToArray(), - Value: distanceOver.value - ); - case CombatTimeCondition combatTime: - return new AiMetadata.CombatTimeCondition( - Name: node.name, - Entries: childNodes.ToArray(), - BattleTimeBegin: combatTime.battleTimeBegin, - BattleTimeLoop: combatTime.battleTimeLoop, - BattleTimeEnd: combatTime.battleTimeEnd - ); - case DistanceLessCondition distanceLess: - return new AiMetadata.DistanceLessCondition( - Name: node.name, - Entries: childNodes.ToArray(), - Value: distanceLess.value - ); - case SkillRangeCondition skillRange: - return new AiMetadata.SkillRangeCondition( - Name: node.name, - Entries: childNodes.ToArray(), - SkillIdx: skillRange.skillIdx, - SkillLev: skillRange.skillLev, - IsKeepBattle: skillRange.isKeepBattle - ); - case ExtraDataCondition extraData: - return new AiMetadata.ExtraDataCondition( - Name: node.name, - Entries: childNodes.ToArray(), - Key: extraData.key, - Value: extraData.value, - Op: (AiConditionOp) extraData.op, - IsKeepBattle: extraData.isKeepBattle - ); - case SlaveCountCondition SlaveCount: // these are different enough to warrant having their own nodes. blame nexon - return new AiMetadata.SlaveCountCondition( - Name: node.name, - Entries: childNodes.ToArray(), - Count: SlaveCount.count, - UseSummonGroup: SlaveCount.useSummonGroup, - SummonGroup: SlaveCount.summonGroup - ); - case HpOverCondition hpOver: - return new AiMetadata.HpOverCondition( - Name: node.name, - Entries: childNodes.ToArray(), - Value: hpOver.value - ); - case StateCondition state: - return new AiMetadata.StateCondition( - Name: node.name, - Entries: childNodes.ToArray(), - TargetState: (AiConditionTargetState) state.targetState - ); - case AdditionalCondition additional: - return new AiMetadata.AdditionalCondition( - Name: node.name, - Entries: childNodes.ToArray(), - Id: additional.id, - Level: additional.level, - OverlapCount: additional.overlapCount, - IsTarget: additional.isTarget - ); - case HpLessCondition hpLess: - return new AiMetadata.HpLessCondition( - Name: node.name, - Entries: childNodes.ToArray(), - Value: hpLess.value - ); - case FeatureCondition feature: // feature was converted to TrueCondition - case TrueCondition trueNode: - if (node.name == "feature") { - Console.WriteLine("AI feature condition node is being convered to a true node"); - } - return new AiMetadata.TrueCondition( - Name: node.name, - Entries: childNodes.ToArray() - ); - default: - throw new NotImplementedException("unknown AI condition name: " + node.name); - } - } - - void MapEntry(List entries, Entry entry) { - if (entry is NodeEntry node) { - entries.Add(MapNode(node)); - - return; - } - - if (entry is AiPresetEntry aiPreset) { - entries.Add(new AiMetadata.AiPreset( - Name: aiPreset.name - )); - - return; - } - - throw new NotImplementedException($"unknown entry type {entry.GetType().Name}"); - } - - AiMetadata.Node MapNode(NodeEntry node) { - var childNodes = new List(); - var childConditions = new List(); - - foreach (Entry entry in node.Entries) { - if (entry is ConditionEntry) { - continue; - } - - MapEntry(childNodes, entry); - } - - foreach (Entry entry in node.Entries) { - if (entry is not ConditionEntry child) { - continue; - } - - if (child is FeatureCondition feature && !FeatureLocaleFilter.FeatureEnabled(feature.feature)) { - continue; - } - - childConditions.Add(MapCondition(child)); - } - - switch (node) { - case TraceNode trace: - return new AiMetadata.TraceNode( - Name: node.name, - Entries: childNodes.ToArray(), - Limit: trace.limit, - SkillIdx: trace.skillIdx, - Animation: trace.animation, - Speed: trace.speed, - Till: trace.till, - InitialCooltime: trace.initialCooltime, - Cooltime: trace.cooltime, - IsKeepBattle: trace.isKeepBattle - ); - case SkillNode skill: - return new AiMetadata.SkillNode( - Name: node.name, - Entries: childNodes.ToArray(), - Idx: skill.idx, - Level: skill.level, - Prob: skill.prob, - Sequence: skill.sequence, - FacePos: skill.facePos, - FaceTarget: skill.faceTarget, - FaceTargetTick: skill.faceTargetTick, - InitialCooltime: skill.initialCooltime, - Cooltime: skill.cooltime, - Limit: skill.limit, - IsKeepBattle: skill.isKeepBattle - ); - case TeleportNode teleport: - return new AiMetadata.TeleportNode( - Name: node.name, - Entries: childNodes.ToArray(), - Pos: teleport.pos, - Prob: teleport.prob, - FacePos: teleport.facePos, - FaceTarget: teleport.faceTarget, - InitialCooltime: teleport.initialCooltime, - Cooltime: teleport.cooltime, - IsKeepBattle: teleport.isKeepBattle - ); - case StandbyNode standby: - return new AiMetadata.StandbyNode( - Name: node.name, - Entries: childNodes.ToArray(), - Limit: standby.limit, - Prob: standby.prob, - Animation: standby.animation, - FacePos: standby.facePos, - FaceTarget: standby.faceTarget, - InitialCooltime: standby.initialCooltime, - Cooltime: standby.cooltime, - IsKeepBattle: standby.isKeepBattle - ); - case SetDataNode setData: - return new AiMetadata.SetDataNode( - Name: node.name, - Entries: childNodes.ToArray(), - Key: setData.key, - Value: setData.value, - Cooltime: setData.cooltime - ); - case TargetNode target: - return new AiMetadata.TargetNode( - Name: node.name, - Entries: childNodes.ToArray(), - Type: (NodeTargetType) target.type, - Prob: target.prob, - Rank: target.rank, - AdditionalId: target.additionalId, - AdditionalLevel: target.additionalLevel, - From: target.from, - To: target.to, - Center: target.center, - Target: (NodeAiTarget) target.target, - NoChangeWhenNoTarget: target.noChangeWhenNoTarget, - InitialCooltime: target.initialCooltime, - Cooltime: target.cooltime, - IsKeepBattle: target.isKeepBattle - ); - case SayNode say: - return new AiMetadata.SayNode( - Name: node.name, - Entries: childNodes.ToArray(), - Message: say.message, - Prob: say.prob, - DurationTick: say.durationTick, - DelayTick: say.delayTick, - InitialCooltime: say.initialCooltime, - Cooltime: say.cooltime, - IsKeepBattle: say.isKeepBattle - ); - case SetValueNode setValue: - return new AiMetadata.SetValueNode( - Name: node.name, - Entries: childNodes.ToArray(), - Key: setValue.key, - Value: setValue.value, - InitialCooltime: setValue.initialCooltime, - Cooltime: setValue.cooltime, - IsModify: setValue.isModify, - IsKeepBattle: setValue.isKeepBattle - ); - case ConditionsNode conditions: - return new AiMetadata.ConditionsNode( - Name: node.name, - Entries: childNodes.ToArray(), - Conditions: childConditions.ToArray(), - InitialCooltime: conditions.initialCooltime, - Cooltime: conditions.cooltime, - IsKeepBattle: conditions.isKeepBattle - ); - case JumpNode jump: - return new AiMetadata.JumpNode( - Name: node.name, - Entries: childNodes.ToArray(), - Pos: jump.pos, - Speed: jump.speed, - HeightMultiplier: jump.heightMultiplier, - Type: (NodeJumpType) jump.type, - Cooltime: jump.cooltime, - IsKeepBattle: jump.isKeepBattle - ); - case SelectNode select: - return new AiMetadata.SelectNode( - Name: node.name, - Entries: childNodes.ToArray(), - Prob: select.prob, - useNpcProb: select.useNpcProb - ); - case MoveNode move: - return new AiMetadata.MoveNode( - Name: node.name, - Entries: childNodes.ToArray(), - Destination: move.destination, - Prob: move.prob, - Animation: move.animation, - Limit: move.limit, - Speed: move.speed, - FaceTarget: move.faceTarget, - InitialCooltime: move.initialCooltime, - Cooltime: move.cooltime, - IsKeepBattle: move.isKeepBattle - ); - case SummonNode summon: - return new AiMetadata.SummonNode( - Name: node.name, - Entries: childNodes.ToArray(), - NpcId: summon.npcId, - NpcCountMax: summon.npcCountMax, - NpcCount: summon.npcCount, - DelayTick: summon.delayTick, - LifeTime: summon.lifeTime, - SummonRot: summon.summonRot, - SummonPos: summon.summonPos, - SummonPosOffset: summon.summonPosOffset, - SummonTargetOffset: summon.summonTargetOffset, - SummonRadius: summon.summonRadius, - Group: summon.group, - Master: (NodeSummonMaster) summon.master, - Option: Array.ConvertAll(summon.option, value => (NodeSummonOption) value), - Cooltime: summon.cooltime, - IsKeepBattle: summon.isKeepBattle - ); - case TriggerSetUserValueNode triggerSetUserValue: - return new AiMetadata.TriggerSetUserValueNode( - Name: node.name, - Entries: childNodes.ToArray(), - TriggerID: triggerSetUserValue.triggerID, - Key: triggerSetUserValue.key, - Value: triggerSetUserValue.value, - Cooltime: triggerSetUserValue.cooltime, - IsKeepBattle: triggerSetUserValue.isKeepBattle - ); - case RideNode ride: - return new AiMetadata.RideNode( - Name: node.name, - Entries: childNodes.ToArray(), - Type: (NodeRideType) ride.type, - IsRideOff: ride.isRideOff, - RideNpcIDs: ride.rideNpcIDs - ); - case SetSlaveValueNode setSlaveValue: - return new AiMetadata.SetSlaveValueNode( - Name: node.name, - Entries: childNodes.ToArray(), - Key: setSlaveValue.key, - Value: setSlaveValue.value, - IsRandom: setSlaveValue.isRandom, - Cooltime: setSlaveValue.cooltime, - IsModify: setSlaveValue.isModify, - IsKeepBattle: setSlaveValue.isKeepBattle - ); - case SetMasterValueNode setMasterValue: - return new AiMetadata.SetMasterValueNode( - Name: node.name, - Entries: childNodes.ToArray(), - Key: setMasterValue.key, - Value: setMasterValue.value, - IsRandom: setMasterValue.isRandom, - Cooltime: setMasterValue.cooltime, - IsModify: setMasterValue.isModify, - IsKeepBattle: setMasterValue.isKeepBattle - ); - case RunawayNode runaway: - return new AiMetadata.RunawayNode( - Name: node.name, - Entries: childNodes.ToArray(), - Animation: runaway.animation, - SkillIdx: runaway.skillIdx, - Till: runaway.till, - Limit: runaway.limit, - FacePos: runaway.facePos, - InitialCooltime: runaway.initialCooltime, - Cooltime: runaway.cooltime - ); - case MinimumHpNode minimumHp: - return new AiMetadata.MinimumHpNode( - Name: node.name, - Entries: childNodes.ToArray(), - HpPercent: minimumHp.hpPercent - ); - case BuffNode buff: - return new AiMetadata.BuffNode( - Name: node.name, - Entries: childNodes.ToArray(), - Id: buff.id, - Type: (NodeBuffType) buff.type, - Level: buff.level, - Prob: buff.prob, - InitialCooltime: buff.initialCooltime, - Cooltime: buff.cooltime, - IsTarget: buff.isTarget, - IsKeepBattle: buff.isKeepBattle - ); - case TargetEffectNode targetEffect: - return new AiMetadata.TargetEffectNode( - Name: node.name, - Entries: childNodes.ToArray(), - EffectName: targetEffect.effectName - ); - case ShowVibrateNode showVibrate: - return new AiMetadata.ShowVibrateNode( - Name: node.name, - Entries: childNodes.ToArray(), - GroupId: showVibrate.groupID - ); - case SidePopupNode sidePopup: - return new AiMetadata.SidePopupNode( - Name: node.name, - Entries: childNodes.ToArray(), - Type: (NodePopupType) sidePopup.type, - Illust: sidePopup.illust, - Duration: sidePopup.duration, - Script: sidePopup.script, - Sound: sidePopup.sound, - Voice: sidePopup.voice - ); - case SetValueRangeTargetNode setValueRangeTarget: - return new AiMetadata.SetValueRangeTargetNode( - Name: node.name, - Entries: childNodes.ToArray(), - Key: setValueRangeTarget.key, - Value: setValueRangeTarget.value, - Height: setValueRangeTarget.height, - Radius: setValueRangeTarget.radius, - Cooltime: setValueRangeTarget.cooltime, - IsModify: setValueRangeTarget.isModify, - IsKeepBattle: setValueRangeTarget.isKeepBattle - ); - case AnnounceNode announce: - return new AiMetadata.AnnounceNode( - Name: node.name, - Entries: childNodes.ToArray(), - Message: announce.message, - DurationTick: announce.durationTick, - Cooltime: announce.cooltime - ); - case ModifyRoomTimeNode modifyRoomTime: - return new AiMetadata.ModifyRoomTimeNode( - Name: node.name, - Entries: childNodes.ToArray(), - TimeTick: modifyRoomTime.timeTick, - IsShowEffect: modifyRoomTime.isShowEffect - ); - case HideVibrateAllNode hideVibrateAll: - return new AiMetadata.HideVibrateAllNode( - Name: node.name, - Entries: childNodes.ToArray(), - IsKeepBattle: hideVibrateAll.isKeepBattle - ); - case TriggerModifyUserValueNode triggerModifyUserValue: - return new AiMetadata.TriggerModifyUserValueNode( - Name: node.name, - Entries: childNodes.ToArray(), - TriggerID: triggerModifyUserValue.triggerID, - Key: triggerModifyUserValue.key, - Value: triggerModifyUserValue.value - ); - case RemoveSlavesNode removeSlaves: - return new AiMetadata.RemoveSlavesNode( - Name: node.name, - Entries: childNodes.ToArray(), - IsKeepBattle: removeSlaves.isKeepBattle - ); - case CreateRandomRoomNode createRandomRoom: - return new AiMetadata.CreateRandomRoomNode( - Name: node.name, - Entries: childNodes.ToArray(), - RandomRoomId: createRandomRoom.randomRoomID, - PortalDuration: createRandomRoom.portalDuration - ); - case CreateInteractObjectNode createInteractObject: - return new AiMetadata.CreateInteractObjectNode( - Name: node.name, - Entries: childNodes.ToArray(), - Normal: createInteractObject.normal, - InteractID: createInteractObject.interactID, - LifeTime: createInteractObject.lifeTime, - KfmName: createInteractObject.kfmName, - Reactable: createInteractObject.reactable - ); - case RemoveMeNode removeMe: - return new AiMetadata.RemoveMeNode( - Name: node.name, - Entries: childNodes.ToArray() - ); - case SuicideNode Suicide: - return new AiMetadata.SuicideNode( - Name: node.name, - Entries: childNodes.ToArray() - ); - default: - throw new NotImplementedException("unknown AI node name: " + node.name); - } - } -} +using M2dXmlGenerator; +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Xml.AI; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; + +namespace Maple2.File.Ingest.Mapper; + +public class AiMapper : TypeMapper { + private readonly AiParser parser; + + public AiMapper(M2dReader xmlReader) { + parser = new AiParser(xmlReader); + } + + protected override IEnumerable Map() { + foreach ((string name, NpcAi data) in parser.Parse()) { + List reserved = new List(); + List battle = new List(); + List battleEnd = new List(); + List aiPresets = new List(); + + foreach (Entry entry in data.Reserved) { + if (entry is not ConditionEntry node) { + continue; + } + + if (node is FeatureCondition feature && !FeatureLocaleFilter.FeatureEnabled(feature.feature)) { + continue; + } + + reserved.Add(MapCondition(node)); + } + + foreach (Entry entry in data.Battle) { + MapEntry(battle, entry); + } + + foreach (Entry entry in data.BattleEnd) { + MapEntry(battle, entry); + } + + foreach (Entry node in data.AiPresets) { + var childNodes = new List(); + + foreach (Entry entry in node.Entries) { + MapEntry(childNodes, entry); + } + + aiPresets.Add(new AiMetadata.AiPresetDefinition( + Name: node.name, + Entries: childNodes.ToArray() + )); + } + + yield return new AiMetadata( + Name: name, + Reserved: reserved.ToArray(), + Battle: battle.ToArray(), + BattleEnd: battleEnd.ToArray(), + AiPresets: aiPresets.ToArray() + ); + } + } + + AiMetadata.Condition MapCondition(ConditionEntry node) { + var childNodes = new List(); + + foreach (Entry entry in node.Entries) { + MapEntry(childNodes, entry); + } + + switch (node) { + case DistanceOverCondition distanceOver: + return new AiMetadata.DistanceOverCondition( + Name: node.name, + Entries: childNodes.ToArray(), + Value: distanceOver.value + ); + case CombatTimeCondition combatTime: + return new AiMetadata.CombatTimeCondition( + Name: node.name, + Entries: childNodes.ToArray(), + BattleTimeBegin: combatTime.battleTimeBegin, + BattleTimeLoop: combatTime.battleTimeLoop, + BattleTimeEnd: combatTime.battleTimeEnd + ); + case DistanceLessCondition distanceLess: + return new AiMetadata.DistanceLessCondition( + Name: node.name, + Entries: childNodes.ToArray(), + Value: distanceLess.value + ); + case SkillRangeCondition skillRange: + return new AiMetadata.SkillRangeCondition( + Name: node.name, + Entries: childNodes.ToArray(), + SkillIdx: skillRange.skillIdx, + SkillLev: skillRange.skillLev, + IsKeepBattle: skillRange.isKeepBattle + ); + case ExtraDataCondition extraData: + return new AiMetadata.ExtraDataCondition( + Name: node.name, + Entries: childNodes.ToArray(), + Key: extraData.key, + Value: extraData.value, + Op: (AiConditionOp) extraData.op, + IsKeepBattle: extraData.isKeepBattle + ); + case SlaveCountCondition SlaveCount: // these are different enough to warrant having their own nodes. blame nexon + return new AiMetadata.SlaveCountCondition( + Name: node.name, + Entries: childNodes.ToArray(), + Count: SlaveCount.count, + UseSummonGroup: SlaveCount.useSummonGroup, + SummonGroup: SlaveCount.summonGroup + ); + case HpOverCondition hpOver: + return new AiMetadata.HpOverCondition( + Name: node.name, + Entries: childNodes.ToArray(), + Value: hpOver.value + ); + case StateCondition state: + return new AiMetadata.StateCondition( + Name: node.name, + Entries: childNodes.ToArray(), + TargetState: (AiConditionTargetState) state.targetState + ); + case AdditionalCondition additional: + return new AiMetadata.AdditionalCondition( + Name: node.name, + Entries: childNodes.ToArray(), + Id: additional.id, + Level: additional.level, + OverlapCount: additional.overlapCount, + IsTarget: additional.isTarget + ); + case HpLessCondition hpLess: + return new AiMetadata.HpLessCondition( + Name: node.name, + Entries: childNodes.ToArray(), + Value: hpLess.value + ); + case FeatureCondition feature: // feature was converted to TrueCondition + case TrueCondition trueNode: + if (node.name == "feature") { + Console.WriteLine("AI feature condition node is being convered to a true node"); + } + return new AiMetadata.TrueCondition( + Name: node.name, + Entries: childNodes.ToArray() + ); + default: + throw new NotImplementedException("unknown AI condition name: " + node.name); + } + } + + void MapEntry(List entries, Entry entry) { + if (entry is NodeEntry node) { + entries.Add(MapNode(node)); + + return; + } + + if (entry is AiPresetEntry aiPreset) { + entries.Add(new AiMetadata.AiPreset( + Name: aiPreset.name + )); + + return; + } + + throw new NotImplementedException($"unknown entry type {entry.GetType().Name}"); + } + + AiMetadata.Node MapNode(NodeEntry node) { + var childNodes = new List(); + var childConditions = new List(); + + foreach (Entry entry in node.Entries) { + if (entry is ConditionEntry) { + continue; + } + + MapEntry(childNodes, entry); + } + + foreach (Entry entry in node.Entries) { + if (entry is not ConditionEntry child) { + continue; + } + + if (child is FeatureCondition feature && !FeatureLocaleFilter.FeatureEnabled(feature.feature)) { + continue; + } + + childConditions.Add(MapCondition(child)); + } + + switch (node) { + case TraceNode trace: + return new AiMetadata.TraceNode( + Name: node.name, + Entries: childNodes.ToArray(), + Limit: trace.limit, + SkillIdx: trace.skillIdx, + Animation: trace.animation, + Speed: trace.speed, + Till: trace.till, + InitialCooltime: trace.initialCooltime, + Cooltime: trace.cooltime, + IsKeepBattle: trace.isKeepBattle + ); + case SkillNode skill: + return new AiMetadata.SkillNode( + Name: node.name, + Entries: childNodes.ToArray(), + Idx: skill.idx, + Level: skill.level, + Prob: skill.prob, + Sequence: skill.sequence, + FacePos: skill.facePos, + FaceTarget: skill.faceTarget, + FaceTargetTick: skill.faceTargetTick, + InitialCooltime: skill.initialCooltime, + Cooltime: skill.cooltime, + Limit: skill.limit, + IsKeepBattle: skill.isKeepBattle + ); + case TeleportNode teleport: + return new AiMetadata.TeleportNode( + Name: node.name, + Entries: childNodes.ToArray(), + Pos: teleport.pos, + Prob: teleport.prob, + FacePos: teleport.facePos, + FaceTarget: teleport.faceTarget, + InitialCooltime: teleport.initialCooltime, + Cooltime: teleport.cooltime, + IsKeepBattle: teleport.isKeepBattle + ); + case StandbyNode standby: + return new AiMetadata.StandbyNode( + Name: node.name, + Entries: childNodes.ToArray(), + Limit: standby.limit, + Prob: standby.prob, + Animation: standby.animation, + FacePos: standby.facePos, + FaceTarget: standby.faceTarget, + InitialCooltime: standby.initialCooltime, + Cooltime: standby.cooltime, + IsKeepBattle: standby.isKeepBattle + ); + case SetDataNode setData: + return new AiMetadata.SetDataNode( + Name: node.name, + Entries: childNodes.ToArray(), + Key: setData.key, + Value: setData.value, + Cooltime: setData.cooltime + ); + case TargetNode target: + return new AiMetadata.TargetNode( + Name: node.name, + Entries: childNodes.ToArray(), + Type: (NodeTargetType) target.type, + Prob: target.prob, + Rank: target.rank, + AdditionalId: target.additionalId, + AdditionalLevel: target.additionalLevel, + From: target.from, + To: target.to, + Center: target.center, + Target: (NodeAiTarget) target.target, + NoChangeWhenNoTarget: target.noChangeWhenNoTarget, + InitialCooltime: target.initialCooltime, + Cooltime: target.cooltime, + IsKeepBattle: target.isKeepBattle + ); + case SayNode say: + return new AiMetadata.SayNode( + Name: node.name, + Entries: childNodes.ToArray(), + Message: say.message, + Prob: say.prob, + DurationTick: say.durationTick, + DelayTick: say.delayTick, + InitialCooltime: say.initialCooltime, + Cooltime: say.cooltime, + IsKeepBattle: say.isKeepBattle + ); + case SetValueNode setValue: + return new AiMetadata.SetValueNode( + Name: node.name, + Entries: childNodes.ToArray(), + Key: setValue.key, + Value: setValue.value, + InitialCooltime: setValue.initialCooltime, + Cooltime: setValue.cooltime, + IsModify: setValue.isModify, + IsKeepBattle: setValue.isKeepBattle + ); + case ConditionsNode conditions: + return new AiMetadata.ConditionsNode( + Name: node.name, + Entries: childNodes.ToArray(), + Conditions: childConditions.ToArray(), + InitialCooltime: conditions.initialCooltime, + Cooltime: conditions.cooltime, + IsKeepBattle: conditions.isKeepBattle + ); + case JumpNode jump: + return new AiMetadata.JumpNode( + Name: node.name, + Entries: childNodes.ToArray(), + Pos: jump.pos, + Speed: jump.speed, + HeightMultiplier: jump.heightMultiplier, + Type: (NodeJumpType) jump.type, + Cooltime: jump.cooltime, + IsKeepBattle: jump.isKeepBattle + ); + case SelectNode select: + return new AiMetadata.SelectNode( + Name: node.name, + Entries: childNodes.ToArray(), + Prob: select.prob, + useNpcProb: select.useNpcProb + ); + case MoveNode move: + return new AiMetadata.MoveNode( + Name: node.name, + Entries: childNodes.ToArray(), + Destination: move.destination, + Prob: move.prob, + Animation: move.animation, + Limit: move.limit, + Speed: move.speed, + FaceTarget: move.faceTarget, + InitialCooltime: move.initialCooltime, + Cooltime: move.cooltime, + IsKeepBattle: move.isKeepBattle + ); + case SummonNode summon: + return new AiMetadata.SummonNode( + Name: node.name, + Entries: childNodes.ToArray(), + NpcId: summon.npcId, + NpcCountMax: summon.npcCountMax, + NpcCount: summon.npcCount, + DelayTick: summon.delayTick, + LifeTime: summon.lifeTime, + SummonRot: summon.summonRot, + SummonPos: summon.summonPos, + SummonPosOffset: summon.summonPosOffset, + SummonTargetOffset: summon.summonTargetOffset, + SummonRadius: summon.summonRadius, + Group: summon.group, + Master: (NodeSummonMaster) summon.master, + Option: Array.ConvertAll(summon.option, value => (NodeSummonOption) value), + Cooltime: summon.cooltime, + IsKeepBattle: summon.isKeepBattle + ); + case TriggerSetUserValueNode triggerSetUserValue: + return new AiMetadata.TriggerSetUserValueNode( + Name: node.name, + Entries: childNodes.ToArray(), + TriggerID: triggerSetUserValue.triggerID, + Key: triggerSetUserValue.key, + Value: triggerSetUserValue.value, + Cooltime: triggerSetUserValue.cooltime, + IsKeepBattle: triggerSetUserValue.isKeepBattle + ); + case RideNode ride: + return new AiMetadata.RideNode( + Name: node.name, + Entries: childNodes.ToArray(), + Type: (NodeRideType) ride.type, + IsRideOff: ride.isRideOff, + RideNpcIDs: ride.rideNpcIDs + ); + case SetSlaveValueNode setSlaveValue: + return new AiMetadata.SetSlaveValueNode( + Name: node.name, + Entries: childNodes.ToArray(), + Key: setSlaveValue.key, + Value: setSlaveValue.value, + IsRandom: setSlaveValue.isRandom, + Cooltime: setSlaveValue.cooltime, + IsModify: setSlaveValue.isModify, + IsKeepBattle: setSlaveValue.isKeepBattle + ); + case SetMasterValueNode setMasterValue: + return new AiMetadata.SetMasterValueNode( + Name: node.name, + Entries: childNodes.ToArray(), + Key: setMasterValue.key, + Value: setMasterValue.value, + IsRandom: setMasterValue.isRandom, + Cooltime: setMasterValue.cooltime, + IsModify: setMasterValue.isModify, + IsKeepBattle: setMasterValue.isKeepBattle + ); + case RunawayNode runaway: + return new AiMetadata.RunawayNode( + Name: node.name, + Entries: childNodes.ToArray(), + Animation: runaway.animation, + SkillIdx: runaway.skillIdx, + Till: runaway.till, + Limit: runaway.limit, + FacePos: runaway.facePos, + InitialCooltime: runaway.initialCooltime, + Cooltime: runaway.cooltime + ); + case MinimumHpNode minimumHp: + return new AiMetadata.MinimumHpNode( + Name: node.name, + Entries: childNodes.ToArray(), + HpPercent: minimumHp.hpPercent + ); + case BuffNode buff: + return new AiMetadata.BuffNode( + Name: node.name, + Entries: childNodes.ToArray(), + Id: buff.id, + Type: (NodeBuffType) buff.type, + Level: buff.level, + Prob: buff.prob, + InitialCooltime: buff.initialCooltime, + Cooltime: buff.cooltime, + IsTarget: buff.isTarget, + IsKeepBattle: buff.isKeepBattle + ); + case TargetEffectNode targetEffect: + return new AiMetadata.TargetEffectNode( + Name: node.name, + Entries: childNodes.ToArray(), + EffectName: targetEffect.effectName + ); + case ShowVibrateNode showVibrate: + return new AiMetadata.ShowVibrateNode( + Name: node.name, + Entries: childNodes.ToArray(), + GroupId: showVibrate.groupID + ); + case SidePopupNode sidePopup: + return new AiMetadata.SidePopupNode( + Name: node.name, + Entries: childNodes.ToArray(), + Type: (NodePopupType) sidePopup.type, + Illust: sidePopup.illust, + Duration: sidePopup.duration, + Script: sidePopup.script, + Sound: sidePopup.sound, + Voice: sidePopup.voice + ); + case SetValueRangeTargetNode setValueRangeTarget: + return new AiMetadata.SetValueRangeTargetNode( + Name: node.name, + Entries: childNodes.ToArray(), + Key: setValueRangeTarget.key, + Value: setValueRangeTarget.value, + Height: setValueRangeTarget.height, + Radius: setValueRangeTarget.radius, + Cooltime: setValueRangeTarget.cooltime, + IsModify: setValueRangeTarget.isModify, + IsKeepBattle: setValueRangeTarget.isKeepBattle + ); + case AnnounceNode announce: + return new AiMetadata.AnnounceNode( + Name: node.name, + Entries: childNodes.ToArray(), + Message: announce.message, + DurationTick: announce.durationTick, + Cooltime: announce.cooltime + ); + case ModifyRoomTimeNode modifyRoomTime: + return new AiMetadata.ModifyRoomTimeNode( + Name: node.name, + Entries: childNodes.ToArray(), + TimeTick: modifyRoomTime.timeTick, + IsShowEffect: modifyRoomTime.isShowEffect + ); + case HideVibrateAllNode hideVibrateAll: + return new AiMetadata.HideVibrateAllNode( + Name: node.name, + Entries: childNodes.ToArray(), + IsKeepBattle: hideVibrateAll.isKeepBattle + ); + case TriggerModifyUserValueNode triggerModifyUserValue: + return new AiMetadata.TriggerModifyUserValueNode( + Name: node.name, + Entries: childNodes.ToArray(), + TriggerID: triggerModifyUserValue.triggerID, + Key: triggerModifyUserValue.key, + Value: triggerModifyUserValue.value + ); + case RemoveSlavesNode removeSlaves: + return new AiMetadata.RemoveSlavesNode( + Name: node.name, + Entries: childNodes.ToArray(), + IsKeepBattle: removeSlaves.isKeepBattle + ); + case CreateRandomRoomNode createRandomRoom: + return new AiMetadata.CreateRandomRoomNode( + Name: node.name, + Entries: childNodes.ToArray(), + RandomRoomId: createRandomRoom.randomRoomID, + PortalDuration: createRandomRoom.portalDuration + ); + case CreateInteractObjectNode createInteractObject: + return new AiMetadata.CreateInteractObjectNode( + Name: node.name, + Entries: childNodes.ToArray(), + Normal: createInteractObject.normal, + InteractID: createInteractObject.interactID, + LifeTime: createInteractObject.lifeTime, + KfmName: createInteractObject.kfmName, + Reactable: createInteractObject.reactable + ); + case RemoveMeNode removeMe: + return new AiMetadata.RemoveMeNode( + Name: node.name, + Entries: childNodes.ToArray() + ); + case SuicideNode Suicide: + return new AiMetadata.SuicideNode( + Name: node.name, + Entries: childNodes.ToArray() + ); + default: + throw new NotImplementedException("unknown AI node name: " + node.name); + } + } +} diff --git a/Maple2.File.Ingest/Mapper/AnimationMapper.cs b/Maple2.File.Ingest/Mapper/AnimationMapper.cs index 2160e4573..443585623 100644 --- a/Maple2.File.Ingest/Mapper/AnimationMapper.cs +++ b/Maple2.File.Ingest/Mapper/AnimationMapper.cs @@ -1,40 +1,40 @@ -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Xml; -using Maple2.Model.Metadata; - -namespace Maple2.File.Ingest.Mapper; - -public class AnimationMapper : TypeMapper { - private readonly AniKeyTextParser parser; - - public AnimationMapper(M2dReader xmlReader) { - parser = new AniKeyTextParser(xmlReader); - } - - protected override IEnumerable Map() { - foreach (AnimationData data in parser.Parse()) { - foreach (KeyFrameMotion kfm in data.kfm) { - IEnumerable<(string Name, AnimationSequenceMetadata Sequence)> sequences = kfm.seq.Select(sequence => { - List keys = sequence.key.Select(key => new AnimationKey(key.name, (float) key.time)).ToList(); - return (sequence.name, - new AnimationSequenceMetadata( - Name: sequence.name, - Id: (short) sequence.id, - Time: (float) (sequence.key.FirstOrDefault(key => key.name == "end")?.time ?? 0), keys) - ); - }); - - var lookup = new Dictionary(); - foreach ((string name, AnimationSequenceMetadata sequence) in sequences) { - if (!lookup.TryAdd(name, sequence)) { - Console.WriteLine($"Ignore Duplicate: {name} for {kfm.name}"); - } - - } - - yield return new AnimationMetadata(kfm.name, lookup); - } - } - } -} +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Xml; +using Maple2.Model.Metadata; + +namespace Maple2.File.Ingest.Mapper; + +public class AnimationMapper : TypeMapper { + private readonly AniKeyTextParser parser; + + public AnimationMapper(M2dReader xmlReader) { + parser = new AniKeyTextParser(xmlReader); + } + + protected override IEnumerable Map() { + foreach (AnimationData data in parser.Parse()) { + foreach (KeyFrameMotion kfm in data.kfm) { + IEnumerable<(string Name, AnimationSequenceMetadata Sequence)> sequences = kfm.seq.Select(sequence => { + List keys = sequence.key.Select(key => new AnimationKey(key.name, (float) key.time)).ToList(); + return (sequence.name, + new AnimationSequenceMetadata( + Name: sequence.name, + Id: (short) sequence.id, + Time: (float) (sequence.key.FirstOrDefault(key => key.name == "end")?.time ?? 0), keys) + ); + }); + + var lookup = new Dictionary(); + foreach ((string name, AnimationSequenceMetadata sequence) in sequences) { + if (!lookup.TryAdd(name, sequence)) { + Console.WriteLine($"Ignore Duplicate: {name} for {kfm.name}"); + } + + } + + yield return new AnimationMetadata(kfm.name, lookup); + } + } + } +} diff --git a/Maple2.File.Ingest/Mapper/FunctionCubeMapper.cs b/Maple2.File.Ingest/Mapper/FunctionCubeMapper.cs index e2849e87c..e873c858e 100644 --- a/Maple2.File.Ingest/Mapper/FunctionCubeMapper.cs +++ b/Maple2.File.Ingest/Mapper/FunctionCubeMapper.cs @@ -1,71 +1,71 @@ -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Xml.Object; -using Maple2.Model.Enum; -using Maple2.Model.Game; -using Maple2.Model.Metadata; -using Nurturing = Maple2.File.Parser.Xml.Object.Nurturing; - -namespace Maple2.File.Ingest.Mapper; - -public class FunctionCubeMapper : TypeMapper { - private readonly FunctionCubeParser parser; - - public FunctionCubeMapper(M2dReader xmlReader) { - parser = new FunctionCubeParser(xmlReader); - } - - protected override IEnumerable Map() { - foreach ((int id, FunctionCubeRoot functionCubeRoot) in parser.Parse()) { - FunctionCube functionCube = functionCubeRoot.FunctionCube; - - ConfigurableCube? configurableCube = functionCubeRoot.ConfigurableCube; - yield return new FunctionCubeMetadata( - Id: id, - RecipeId: functionCube.receipeID, - ConfigurableCubeType: configurableCube is not null ? (ConfigurableCubeType) configurableCube.id : ConfigurableCubeType.None, - DefaultState: (InteractCubeState) functionCube.DefaultState, - ControlType: Enum.TryParse(functionCube.ControlType, out InteractCubeControlType controlType) ? controlType : InteractCubeControlType.None, - AutoStateChange: functionCube.AutoStateChange, - AutoStateChangeTime: functionCube.AutoStateChangeTime, - Nurturing: ParseNurturing(functionCube.nurturing) - ); - } - } - - private static FunctionCubeMetadata.NurturingData? ParseNurturing(Nurturing? functionCubeNurturing) { - if (functionCubeNurturing is null || functionCubeNurturing.rewardItem.Length == 0 || functionCubeNurturing.rewardItemByFeeding.Length == 0) { - return null; - } - return new FunctionCubeMetadata.NurturingData( - Feed: new RewardItem( - itemId: functionCubeNurturing.rewardItem[0], - rarity: (short) functionCubeNurturing.rewardItem[1], - amount: functionCubeNurturing.rewardItem[2] - ), - RewardFeed: new RewardItem( - itemId: functionCubeNurturing.rewardItemByFeeding[0], - rarity: (short) functionCubeNurturing.rewardItemByFeeding[1], - amount: functionCubeNurturing.rewardItemByFeeding[2] - ), - RequiredGrowth: ParseRequiredGrowth(functionCubeNurturing), - QuestTag: functionCubeNurturing.nurturingQuestTag - ); - - FunctionCubeMetadata.NurturingData.Growth[] ParseRequiredGrowth(Nurturing nurturing) { - List result = []; - for (int i = 0; i < nurturing.rewardItemByGrowth.Length; i += 3) { - result.Add(new FunctionCubeMetadata.NurturingData.Growth( - Exp: nurturing.requiredGrowth[i / 3], - Stage: (short) (i / 3 + 1), - Reward: new RewardItem( - itemId: nurturing.rewardItemByGrowth[i], - rarity: (short) nurturing.rewardItemByGrowth[i + 1], - amount: nurturing.rewardItemByGrowth[i + 2] - ) - )); - } - return result.ToArray(); - } - } -} +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Xml.Object; +using Maple2.Model.Enum; +using Maple2.Model.Game; +using Maple2.Model.Metadata; +using Nurturing = Maple2.File.Parser.Xml.Object.Nurturing; + +namespace Maple2.File.Ingest.Mapper; + +public class FunctionCubeMapper : TypeMapper { + private readonly FunctionCubeParser parser; + + public FunctionCubeMapper(M2dReader xmlReader) { + parser = new FunctionCubeParser(xmlReader); + } + + protected override IEnumerable Map() { + foreach ((int id, FunctionCubeRoot functionCubeRoot) in parser.Parse()) { + FunctionCube functionCube = functionCubeRoot.FunctionCube; + + ConfigurableCube? configurableCube = functionCubeRoot.ConfigurableCube; + yield return new FunctionCubeMetadata( + Id: id, + RecipeId: functionCube.receipeID, + ConfigurableCubeType: configurableCube is not null ? (ConfigurableCubeType) configurableCube.id : ConfigurableCubeType.None, + DefaultState: (InteractCubeState) functionCube.DefaultState, + ControlType: Enum.TryParse(functionCube.ControlType, out InteractCubeControlType controlType) ? controlType : InteractCubeControlType.None, + AutoStateChange: functionCube.AutoStateChange, + AutoStateChangeTime: functionCube.AutoStateChangeTime, + Nurturing: ParseNurturing(functionCube.nurturing) + ); + } + } + + private static FunctionCubeMetadata.NurturingData? ParseNurturing(Nurturing? functionCubeNurturing) { + if (functionCubeNurturing is null || functionCubeNurturing.rewardItem.Length == 0 || functionCubeNurturing.rewardItemByFeeding.Length == 0) { + return null; + } + return new FunctionCubeMetadata.NurturingData( + Feed: new RewardItem( + itemId: functionCubeNurturing.rewardItem[0], + rarity: (short) functionCubeNurturing.rewardItem[1], + amount: functionCubeNurturing.rewardItem[2] + ), + RewardFeed: new RewardItem( + itemId: functionCubeNurturing.rewardItemByFeeding[0], + rarity: (short) functionCubeNurturing.rewardItemByFeeding[1], + amount: functionCubeNurturing.rewardItemByFeeding[2] + ), + RequiredGrowth: ParseRequiredGrowth(functionCubeNurturing), + QuestTag: functionCubeNurturing.nurturingQuestTag + ); + + FunctionCubeMetadata.NurturingData.Growth[] ParseRequiredGrowth(Nurturing nurturing) { + List result = []; + for (int i = 0; i < nurturing.rewardItemByGrowth.Length; i += 3) { + result.Add(new FunctionCubeMetadata.NurturingData.Growth( + Exp: nurturing.requiredGrowth[i / 3], + Stage: (short) (i / 3 + 1), + Reward: new RewardItem( + itemId: nurturing.rewardItemByGrowth[i], + rarity: (short) nurturing.rewardItemByGrowth[i + 1], + amount: nurturing.rewardItemByGrowth[i + 2] + ) + )); + } + return result.ToArray(); + } + } +} diff --git a/Maple2.File.Ingest/Mapper/ItemMapper.cs b/Maple2.File.Ingest/Mapper/ItemMapper.cs index 53a817993..5946c3930 100644 --- a/Maple2.File.Ingest/Mapper/ItemMapper.cs +++ b/Maple2.File.Ingest/Mapper/ItemMapper.cs @@ -1,238 +1,238 @@ -using M2dXmlGenerator; -using Maple2.Database.Extensions; -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Xml.Item; -using Maple2.File.Parser.Xml.Table; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Slot = Maple2.File.Parser.Xml.Item.Slot; - -namespace Maple2.File.Ingest.Mapper; - -public class ItemMapper : TypeMapper { - private readonly ItemParser parser; - private readonly TableParser tableParser; - private readonly bool newXml; - - public static Dictionary ItemMetadataById { get; } = new(); - - public ItemMapper(M2dReader xmlReader, string language, bool newXml) { - parser = new ItemParser(xmlReader, language); - tableParser = new TableParser(xmlReader, language); - this.newXml = newXml; - } - - protected override IEnumerable Map() { - Dictionary itemExtractionTryCount = tableParser.ParseItemExtraction() - .ToDictionary(entry => entry.Id, entry => entry.Extraction.TryCount); - - var itemSetBonuses = new Dictionary>(); - foreach ((int id, _, SetItemInfo info) in tableParser.ParseSetItemInfo()) { - foreach (int itemId in info.itemIDs) { - itemSetBonuses.TryAdd(itemId, []); - itemSetBonuses[itemId].Add(id); - } - } - - foreach ((int id, string name, ItemData data) in parser.Parse()) { - int transferType = data.limit.transferType; - int tradableCount = data.property.tradableCount; - int tradableCountDeduction = data.property.tradableCountDeduction; - int repackingLimitCount = data.property.rePackingLimitCount; - int repackingItemConsumeCount = data.property.rePackingItemConsumeCount; - int[] repackingScrollIds = data.property.globalRePackingScrollID; - if (FeatureLocaleFilter.FeatureEnabled("GlobalTransferType")) { - transferType = data.limit.globalTransferType ?? transferType; - tradableCount = data.property.globalTradableCount ?? tradableCount; - tradableCountDeduction = data.property.globalTradableCountDeduction ?? tradableCountDeduction; - repackingLimitCount = data.property.globalRePackingLimitCount ?? repackingLimitCount; - repackingItemConsumeCount = data.property.globalRePackingItemConsumeCount ?? repackingItemConsumeCount; - repackingScrollIds = data.property.globalRePackingScrollID ?? repackingScrollIds; - } - if (FeatureLocaleFilter.FeatureEnabled("GlobalTransferTypeNA")) { - transferType = data.limit.globalTransferTypeNA ?? transferType; - tradableCount = data.property.globalTradableCountNA ?? tradableCount; - } - - long expirationTimestamp = 0; - if (data.life.expirationPeriod.Length > 0) { - expirationTimestamp = new DateTime(data.life.expirationPeriod[0], data.life.expirationPeriod[1], data.life.expirationPeriod[2], data.life.expirationPeriod[3], data.life.expirationPeriod[4], - data.life.expirationPeriod[5]).ToEpochSeconds(); - } - - long expirationDuration = 0; - if (data.life.expirationType > 0) { - expirationDuration = data.life.expirationType switch { - 1 => // Week - DateTime.UnixEpoch.AddDays(7 * data.life.numberOfWeeksMonths).ToEpochSeconds(), - 2 => // Month - DateTime.UnixEpoch.AddMonths(1 * data.life.numberOfWeeksMonths).ToEpochSeconds(), - _ => 0, - }; - } else if (data.life.usePeriod > 0) { - expirationDuration = (long) TimeSpan.FromMinutes(data.life.usePeriod).TotalSeconds; - } - - var hairList = new List(); - // parse default hair positions - foreach (Slot slot in data.slots.slot.Where(dataSlots => dataSlots.name == "HR")) { - - // not sure what the difference/significance is within the multiple scale entries. Currently just using the first one - float minScale = slot.scale.ElementAtOrDefault(0)?.min ?? 0f; - float maxScale = slot.scale.ElementAtOrDefault(0)?.max ?? 0f; - switch (slot.asset.Count) { - case 3: // Hair has front and back positionable hair section - for (int index = 0; index < slot.asset[1].custom.Count; index++) { - hairList.Add(new DefaultHairMetadata( - BackPosition: slot.asset[1].custom[index].position, - BackRotation: slot.asset[1].custom[index].rotation, - FrontPosition: slot.asset[2].custom[index].position, - FrontRotation: slot.asset[2].custom[index].rotation, - MinScale: minScale, - MaxScale: maxScale)); - } - break; - case 2: // Hair has one positionable hair section - foreach (Slot.Custom custom in slot.asset[1].custom) { - hairList.Add(new DefaultHairMetadata( - BackPosition: custom.position, - BackRotation: custom.rotation, - MinScale: minScale, - MaxScale: maxScale)); - } - break; - default: // No positionable hair section - hairList.Add(new DefaultHairMetadata()); - break; - } - } - - ItemMetadataSkill? skill = data.skill.skillID == 0 && data.objectWeaponSkill.skillID == 0 ? null : new ItemMetadataSkill( - Id: data.skill.skillID, - Level: data.skill.skillID != 0 ? data.skill.skillLevel : (short) 0, - WeaponId: data.objectWeaponSkill.skillID, - WeaponLevel: data.objectWeaponSkill.skillID != 0 ? data.objectWeaponSkill.skillLevel : (short) 0); - ItemMetadataFunction? function = string.IsNullOrWhiteSpace(data.function.name) ? null : new ItemMetadataFunction( - Type: Enum.Parse(data.function.name), - Name: data.function.name, // Temp duplicate data makes it easier to read DB - Parameters: data.function.parameter, - OnlyShadowWorld: data.function.onlyShadowContinent == 1); - - bool hasOption = data.option.@static > 0 || data.option.constant > 0 || data.option.random > 0 || data.option.optionID > 0; - int levelFactor = (int) data.option.optionLevelFactor; - if (FeatureLocaleFilter.FeatureEnabled("HiddenStatAdd01") || FeatureLocaleFilter.FeatureEnabled("HiddenStatAdd03") || FeatureLocaleFilter.FeatureEnabled("HiddenStatAdd04")) { - levelFactor = (int) (data.option.globalOptionLevelFactor ?? levelFactor); - } - ItemMetadataOption? option = !hasOption ? null : new ItemMetadataOption( - StaticId: data.option.@static, - StaticType: (ItemOptionMakeType) data.option.staticMakeType, - RandomId: data.option.random, - ItemOptionType: (ItemOptionMakeType) data.option.randomMakeType, - ConstantId: data.option.constant, - ConstantType: (ItemOptionMakeType) data.option.constantMakeType, - LevelFactor: levelFactor, - PickId: data.option.optionID); - ItemMetadataMusic? music = data.property.type != 12 ? null : new ItemMetadataMusic( - PlayCount: data.MusicScore.playCount, - MasteryValue: data.MusicScore.masteryValue, - MasteryValueMax: data.MusicScore.masteryValueMax, - IsCustomNote: data.MusicScore.isCustomNote, - NoteLengthMax: data.MusicScore.noteLengthMax, - FileName: data.MusicScore.fileName, - PlayTime: data.MusicScore.playTime); - var housingCategory = HousingCategory.None; - if (!string.IsNullOrEmpty(data.housing.categoryTag)) { - string[] tags = data.housing.categoryTag.Split(','); - housingCategory = Enum.Parse(tags[0]); - } - ItemMetadataHousing? housing = data.property.type != 6 ? null : new ItemMetadataHousing( - TrophyId: data.housing.trophyID, - TrophyLevel: data.housing.trophyLevel, - InteriorLevel: data.housing.interiorLevel, - HousingCategory: housingCategory, - IsNotAllowedInBlueprint: data.housing.doNotInstallBlueprint); - ItemMetadataInstall? install = data.property.type != 6 ? null : new ItemMetadataInstall( - IsSolidCube: data.install.cubeProp == 1, - FunctionId: data.install.funcCode, - ObjectCubeId: data.install.objCode, - MapAttribute: Enum.TryParse(data.install.mapAttribute, true, out MapAttribute mapAttribute) ? mapAttribute : MapAttribute.none); - - var itemMetadata = new ItemMetadata( - Id: id, - Name: name, - SlotNames: data.slots.slot - .Where(slot => !string.IsNullOrEmpty(slot.name)) - .Select(slot => Enum.Parse(slot.name, true)) - .ToArray(), - Mesh: data.ucc.mesh, - DefaultHairs: hairList.ToArray(), - Life: new ItemMetadataLife( - ExpirationDuration: expirationDuration, - ExpirationTimestamp: expirationTimestamp - ), - Property: new ItemMetadataProperty( - IsSkin: data.property.skin, - SkinType: data.property.skinType, - SlotMax: data.property.slotMax, - Type: data.property.type, - SubType: data.property.subtype, - Category: data.property.category, - BlackMarketCategory: data.property.blackMarketCategory, - Tag: string.IsNullOrWhiteSpace(data.basic.stringTag) ? ItemTag.None : Enum.Parse(data.basic.stringTag), - Group: data.property.itemGroup, - Collection: data.property.collection, - GearScore: data.property.gearScore, - PetId: data.pet.petID, - Ride: data.ride.rideMonster, - TradableCount: tradableCount, - TradableCountDeduction: tradableCountDeduction, - RepackCount: repackingLimitCount, - RepackConsumeCount: repackingItemConsumeCount, - RepackScrollIds: repackingScrollIds, - DisableDrop: data.property.disableDrop, - SocketId: data.property.socketDataId, - IsFragment: data.property.functionTags == "piece", - SetOptionIds: itemSetBonuses.GetValueOrDefault(id)?.ToArray() ?? [], - SellPrices: data.property.sell.price, - CustomSellPrices: data.property.sell.priceCustom, - ShopId: data.Shop?.systemShopID ?? 0, - LimitBreakMaxLevel: data.property.unlimitedEnchantMaxGrade - ), - Customize: new ItemMetadataCustomize( - ColorPalette: data.customize.colorPalette, - DefaultColorIndex: data.customize.defaultColorIndex - ), - Limit: new ItemMetadataLimit( - Gender: (Gender) data.limit.genderLimit, - Level: data.limit.levelLimit, - TransferType: (TransferType) transferType, - TradeMaxRarity: data.limit.tradeLimitRank, - ShopSell: data.limit.shopSell, - EnableBreak: data.limit.enableBreak, - EnableEnchant: !data.limit.exceptEnchant, - EnableMeretMarket: data.limit.enableRegisterMeratMarket, - EnableSocketTransfer: data.limit.enableSocketTransfer, - RequireVip: data.limit.vip, - RequireWedding: data.limit.wedding, - GlamorForgeCount: itemExtractionTryCount.GetValueOrDefault(id), - JobLimits: data.limit.jobLimit.Select(job => (JobCode) job).ToArray(), - JobRecommends: data.limit.recommendJobs.Select(job => (JobCode) job).ToArray() - ), - Skill: skill, - Function: function, - AdditionalEffects: data.AdditionalEffect.id - .Zip(data.AdditionalEffect.level, (skillId, level) => new ItemMetadataAdditionalEffect(skillId, level, data.AdditionalEffect.dropEffect)) - .ToArray(), - Option: option, - Music: music, - Housing: housing, - Install: install - ); - - ItemMetadataById[id] = itemMetadata; - - yield return itemMetadata; - } - } -} +using M2dXmlGenerator; +using Maple2.Database.Extensions; +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Xml.Item; +using Maple2.File.Parser.Xml.Table; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Slot = Maple2.File.Parser.Xml.Item.Slot; + +namespace Maple2.File.Ingest.Mapper; + +public class ItemMapper : TypeMapper { + private readonly ItemParser parser; + private readonly TableParser tableParser; + private readonly bool newXml; + + public static Dictionary ItemMetadataById { get; } = new(); + + public ItemMapper(M2dReader xmlReader, string language, bool newXml) { + parser = new ItemParser(xmlReader, language); + tableParser = new TableParser(xmlReader, language); + this.newXml = newXml; + } + + protected override IEnumerable Map() { + Dictionary itemExtractionTryCount = tableParser.ParseItemExtraction() + .ToDictionary(entry => entry.Id, entry => entry.Extraction.TryCount); + + var itemSetBonuses = new Dictionary>(); + foreach ((int id, _, SetItemInfo info) in tableParser.ParseSetItemInfo()) { + foreach (int itemId in info.itemIDs) { + itemSetBonuses.TryAdd(itemId, []); + itemSetBonuses[itemId].Add(id); + } + } + + foreach ((int id, string name, ItemData data) in parser.Parse()) { + int transferType = data.limit.transferType; + int tradableCount = data.property.tradableCount; + int tradableCountDeduction = data.property.tradableCountDeduction; + int repackingLimitCount = data.property.rePackingLimitCount; + int repackingItemConsumeCount = data.property.rePackingItemConsumeCount; + int[] repackingScrollIds = data.property.globalRePackingScrollID; + if (FeatureLocaleFilter.FeatureEnabled("GlobalTransferType")) { + transferType = data.limit.globalTransferType ?? transferType; + tradableCount = data.property.globalTradableCount ?? tradableCount; + tradableCountDeduction = data.property.globalTradableCountDeduction ?? tradableCountDeduction; + repackingLimitCount = data.property.globalRePackingLimitCount ?? repackingLimitCount; + repackingItemConsumeCount = data.property.globalRePackingItemConsumeCount ?? repackingItemConsumeCount; + repackingScrollIds = data.property.globalRePackingScrollID ?? repackingScrollIds; + } + if (FeatureLocaleFilter.FeatureEnabled("GlobalTransferTypeNA")) { + transferType = data.limit.globalTransferTypeNA ?? transferType; + tradableCount = data.property.globalTradableCountNA ?? tradableCount; + } + + long expirationTimestamp = 0; + if (data.life.expirationPeriod.Length > 0) { + expirationTimestamp = new DateTime(data.life.expirationPeriod[0], data.life.expirationPeriod[1], data.life.expirationPeriod[2], data.life.expirationPeriod[3], data.life.expirationPeriod[4], + data.life.expirationPeriod[5]).ToEpochSeconds(); + } + + long expirationDuration = 0; + if (data.life.expirationType > 0) { + expirationDuration = data.life.expirationType switch { + 1 => // Week + DateTime.UnixEpoch.AddDays(7 * data.life.numberOfWeeksMonths).ToEpochSeconds(), + 2 => // Month + DateTime.UnixEpoch.AddMonths(1 * data.life.numberOfWeeksMonths).ToEpochSeconds(), + _ => 0, + }; + } else if (data.life.usePeriod > 0) { + expirationDuration = (long) TimeSpan.FromMinutes(data.life.usePeriod).TotalSeconds; + } + + var hairList = new List(); + // parse default hair positions + foreach (Slot slot in data.slots.slot.Where(dataSlots => dataSlots.name == "HR")) { + + // not sure what the difference/significance is within the multiple scale entries. Currently just using the first one + float minScale = slot.scale.ElementAtOrDefault(0)?.min ?? 0f; + float maxScale = slot.scale.ElementAtOrDefault(0)?.max ?? 0f; + switch (slot.asset.Count) { + case 3: // Hair has front and back positionable hair section + for (int index = 0; index < slot.asset[1].custom.Count; index++) { + hairList.Add(new DefaultHairMetadata( + BackPosition: slot.asset[1].custom[index].position, + BackRotation: slot.asset[1].custom[index].rotation, + FrontPosition: slot.asset[2].custom[index].position, + FrontRotation: slot.asset[2].custom[index].rotation, + MinScale: minScale, + MaxScale: maxScale)); + } + break; + case 2: // Hair has one positionable hair section + foreach (Slot.Custom custom in slot.asset[1].custom) { + hairList.Add(new DefaultHairMetadata( + BackPosition: custom.position, + BackRotation: custom.rotation, + MinScale: minScale, + MaxScale: maxScale)); + } + break; + default: // No positionable hair section + hairList.Add(new DefaultHairMetadata()); + break; + } + } + + ItemMetadataSkill? skill = data.skill.skillID == 0 && data.objectWeaponSkill.skillID == 0 ? null : new ItemMetadataSkill( + Id: data.skill.skillID, + Level: data.skill.skillID != 0 ? data.skill.skillLevel : (short) 0, + WeaponId: data.objectWeaponSkill.skillID, + WeaponLevel: data.objectWeaponSkill.skillID != 0 ? data.objectWeaponSkill.skillLevel : (short) 0); + ItemMetadataFunction? function = string.IsNullOrWhiteSpace(data.function.name) ? null : new ItemMetadataFunction( + Type: Enum.Parse(data.function.name), + Name: data.function.name, // Temp duplicate data makes it easier to read DB + Parameters: data.function.parameter, + OnlyShadowWorld: data.function.onlyShadowContinent == 1); + + bool hasOption = data.option.@static > 0 || data.option.constant > 0 || data.option.random > 0 || data.option.optionID > 0; + int levelFactor = (int) data.option.optionLevelFactor; + if (FeatureLocaleFilter.FeatureEnabled("HiddenStatAdd01") || FeatureLocaleFilter.FeatureEnabled("HiddenStatAdd03") || FeatureLocaleFilter.FeatureEnabled("HiddenStatAdd04")) { + levelFactor = (int) (data.option.globalOptionLevelFactor ?? levelFactor); + } + ItemMetadataOption? option = !hasOption ? null : new ItemMetadataOption( + StaticId: data.option.@static, + StaticType: (ItemOptionMakeType) data.option.staticMakeType, + RandomId: data.option.random, + ItemOptionType: (ItemOptionMakeType) data.option.randomMakeType, + ConstantId: data.option.constant, + ConstantType: (ItemOptionMakeType) data.option.constantMakeType, + LevelFactor: levelFactor, + PickId: data.option.optionID); + ItemMetadataMusic? music = data.property.type != 12 ? null : new ItemMetadataMusic( + PlayCount: data.MusicScore.playCount, + MasteryValue: data.MusicScore.masteryValue, + MasteryValueMax: data.MusicScore.masteryValueMax, + IsCustomNote: data.MusicScore.isCustomNote, + NoteLengthMax: data.MusicScore.noteLengthMax, + FileName: data.MusicScore.fileName, + PlayTime: data.MusicScore.playTime); + var housingCategory = HousingCategory.None; + if (!string.IsNullOrEmpty(data.housing.categoryTag)) { + string[] tags = data.housing.categoryTag.Split(','); + housingCategory = Enum.Parse(tags[0]); + } + ItemMetadataHousing? housing = data.property.type != 6 ? null : new ItemMetadataHousing( + TrophyId: data.housing.trophyID, + TrophyLevel: data.housing.trophyLevel, + InteriorLevel: data.housing.interiorLevel, + HousingCategory: housingCategory, + IsNotAllowedInBlueprint: data.housing.doNotInstallBlueprint); + ItemMetadataInstall? install = data.property.type != 6 ? null : new ItemMetadataInstall( + IsSolidCube: data.install.cubeProp == 1, + FunctionId: data.install.funcCode, + ObjectCubeId: data.install.objCode, + MapAttribute: Enum.TryParse(data.install.mapAttribute, true, out MapAttribute mapAttribute) ? mapAttribute : MapAttribute.none); + + var itemMetadata = new ItemMetadata( + Id: id, + Name: name, + SlotNames: data.slots.slot + .Where(slot => !string.IsNullOrEmpty(slot.name)) + .Select(slot => Enum.Parse(slot.name, true)) + .ToArray(), + Mesh: data.ucc.mesh, + DefaultHairs: hairList.ToArray(), + Life: new ItemMetadataLife( + ExpirationDuration: expirationDuration, + ExpirationTimestamp: expirationTimestamp + ), + Property: new ItemMetadataProperty( + IsSkin: data.property.skin, + SkinType: data.property.skinType, + SlotMax: data.property.slotMax, + Type: data.property.type, + SubType: data.property.subtype, + Category: data.property.category, + BlackMarketCategory: data.property.blackMarketCategory, + Tag: string.IsNullOrWhiteSpace(data.basic.stringTag) ? ItemTag.None : Enum.Parse(data.basic.stringTag), + Group: data.property.itemGroup, + Collection: data.property.collection, + GearScore: data.property.gearScore, + PetId: data.pet.petID, + Ride: data.ride.rideMonster, + TradableCount: tradableCount, + TradableCountDeduction: tradableCountDeduction, + RepackCount: repackingLimitCount, + RepackConsumeCount: repackingItemConsumeCount, + RepackScrollIds: repackingScrollIds, + DisableDrop: data.property.disableDrop, + SocketId: data.property.socketDataId, + IsFragment: data.property.functionTags == "piece", + SetOptionIds: itemSetBonuses.GetValueOrDefault(id)?.ToArray() ?? [], + SellPrices: data.property.sell.price, + CustomSellPrices: data.property.sell.priceCustom, + ShopId: data.Shop?.systemShopID ?? 0, + LimitBreakMaxLevel: data.property.unlimitedEnchantMaxGrade + ), + Customize: new ItemMetadataCustomize( + ColorPalette: data.customize.colorPalette, + DefaultColorIndex: data.customize.defaultColorIndex + ), + Limit: new ItemMetadataLimit( + Gender: (Gender) data.limit.genderLimit, + Level: data.limit.levelLimit, + TransferType: (TransferType) transferType, + TradeMaxRarity: data.limit.tradeLimitRank, + ShopSell: data.limit.shopSell, + EnableBreak: data.limit.enableBreak, + EnableEnchant: !data.limit.exceptEnchant, + EnableMeretMarket: data.limit.enableRegisterMeratMarket, + EnableSocketTransfer: data.limit.enableSocketTransfer, + RequireVip: data.limit.vip, + RequireWedding: data.limit.wedding, + GlamorForgeCount: itemExtractionTryCount.GetValueOrDefault(id), + JobLimits: data.limit.jobLimit.Select(job => (JobCode) job).ToArray(), + JobRecommends: data.limit.recommendJobs.Select(job => (JobCode) job).ToArray() + ), + Skill: skill, + Function: function, + AdditionalEffects: data.AdditionalEffect.id + .Zip(data.AdditionalEffect.level, (skillId, level) => new ItemMetadataAdditionalEffect(skillId, level, data.AdditionalEffect.dropEffect)) + .ToArray(), + Option: option, + Music: music, + Housing: housing, + Install: install + ); + + ItemMetadataById[id] = itemMetadata; + + yield return itemMetadata; + } + } +} diff --git a/Maple2.File.Ingest/Mapper/MapDataMapper.cs b/Maple2.File.Ingest/Mapper/MapDataMapper.cs index 9e28b7ee7..dd052d047 100644 --- a/Maple2.File.Ingest/Mapper/MapDataMapper.cs +++ b/Maple2.File.Ingest/Mapper/MapDataMapper.cs @@ -1,376 +1,376 @@ -using Maple2.Database.Context; -using Maple2.File.Flat; -using Maple2.File.Flat.maplestory2library; -using Maple2.File.Flat.physxmodellibrary; -using Maple2.File.Flat.standardmodellibrary; -using Maple2.File.Ingest.Helpers; -using Maple2.File.Parser.MapXBlock; -using Maple2.Model.Common; -using Maple2.Model.Game.Field; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools.Extensions; -using Maple2.Tools.VectorMath; -using System.IO.Compression; -using System.Numerics; -using Maple2.Model.Enum; -using Maple2.Model.Metadata.FieldEntity; - -namespace Maple2.File.Ingest.Mapper; - -public class MapDataMapper : TypeMapper { - private const float BLOCK_SIZE = (float) Constant.BlockSize; - private const float HALF_BLOCK = 0.5f * BLOCK_SIZE; - - private readonly HashSet xBlocks; - private readonly XBlockParser parser; - - private readonly StatsTracker mapByteStats = new StatsTracker(); - private readonly StatsTracker mapGridByteStats = new StatsTracker(); - private readonly StatsTracker mapGridBytePercentStats = new StatsTracker(); - private readonly StatsTracker mapXStats = new StatsTracker(); - private readonly StatsTracker mapYStats = new StatsTracker(); - private readonly StatsTracker mapZStats = new StatsTracker(); - private readonly StatsTracker alignedStats = new StatsTracker(); - private readonly StatsTracker alignedTrimmedStats = new StatsTracker(); - private readonly StatsTracker unalignedStats = new StatsTracker(); - private readonly HashSet invalidLlids = []; - private readonly HashSet missingLlids = []; - - public MapDataMapper(MetadataContext db, XBlockParser parser) { - xBlocks = db.MapMetadata.Select(metadata => metadata.XBlock).ToHashSet(); - - this.parser = parser; - } - - private class StatsTracker { - - public ulong MinValue = ulong.MaxValue; - public ulong MaxValue; - public ulong AvgValue { - get { - if (Entries == 0) return 0; - return TotalValue / Entries; - } - } - public ulong Entries; - public ulong TotalValue; - - public StatsTracker() { } - - public void AddValue(ulong value) { - ++Entries; - TotalValue += value; - MinValue = ulong.Min(MinValue, value); - MaxValue = ulong.Max(MaxValue, value); - } - } - - private FieldAccelerationStructure ParseMapEntities(IEnumerable entities) { - var gridAlignedEntities = new Dictionary>(); - var unalignedEntities = new List(); - var minIndex = new Vector3S(short.MaxValue, short.MaxValue, short.MaxValue); - var maxIndex = new Vector3S(short.MinValue, short.MinValue, short.MinValue); - var transform = new Transform(); - - int vibrateObjectId = 0; - - foreach (IMapEntity entity in entities) { - var nearestCubeIndex = new Vector3S(); - - if (entity is not IPlaceable placeable) { - continue; - } - - transform.Transformation = Matrix4x4.Identity; - transform.Position = placeable.Position; - transform.RotationAnglesDegrees = placeable.Rotation; - transform.Scale = placeable.Scale; - - Vector3 position = (1 / BLOCK_SIZE) * (placeable.Position - new Vector3(0, 0, HALF_BLOCK)); // offset to round to nearest - nearestCubeIndex = new Vector3S((short) Math.Floor(position.X + 0.5f), (short) Math.Floor(position.Y + 0.5f), (short) Math.Floor(position.Z + 0.5f)); - Vector3 voxelPosition = BLOCK_SIZE * new Vector3(nearestCubeIndex.X, nearestCubeIndex.Y, nearestCubeIndex.Z); - var entityBounds = new BoundingBox3(); - - bool isHexId = entity.EntityId.Length == 32; - - for (int i = 0; isHexId && i < entity.EntityId.Length; ++i) { - isHexId = entity.EntityId[i].IsHexDigit(); - } - - FieldEntity? fieldEntity; - var entityId = new FieldEntityId(0, 0, string.Empty); - - if (isHexId) { - entityId = FieldEntityId.FromString(entity.EntityId); - } - - switch (entity) { - /* - PhysXProp | WhiteboxCube - PhysXProp, MS2MapProperties, MS2Vibrate | PhysXCube, DoesMakeTok - MS2MapProperties | PhysXCube - MS2MapProperties, MS2Vibrate | None - MS2MapProperties, MS2Breakable | PhysXCube, NxCube, BothCube, OnlyNxCube - MS2Breakable | NxCube - */ - case IMS2Breakable breakable: - continue; // intentionally skip breakables. these are dynamic so should be handled at run time - case IPhysXWhitebox whitebox: - entityBounds.Min = -new Vector3(whitebox.ShapeDimensions.X, whitebox.ShapeDimensions.Y, 0.5f * whitebox.ShapeDimensions.Z); - entityBounds.Max = new Vector3(whitebox.ShapeDimensions.X, whitebox.ShapeDimensions.Y, 0.5f * whitebox.ShapeDimensions.Z); - fieldEntity = new FieldBoxColliderEntity( - Id: entityId, - Position: placeable.Position - new Vector3(0, 0, 0.5f * whitebox.ShapeDimensions.Z), - Rotation: placeable.Rotation, - Scale: placeable.Scale, - Bounds: BoundingBox3.Transform(entityBounds, transform.Transformation), - Size: whitebox.ShapeDimensions, - IsWhiteBox: true, - IsFluid: false, - MapAttribute: MapAttribute.none); - break; - case IMesh mesh: - if (entity is IMS2Vibrate { Enabled: true } vibrate) { - entityBounds.Min = -new Vector3(HALF_BLOCK, HALF_BLOCK, 0); - entityBounds.Max = new Vector3(HALF_BLOCK, HALF_BLOCK, BLOCK_SIZE); - - fieldEntity = new FieldVibrateEntity( - Id: entityId, - Position: placeable.Position, - Rotation: placeable.Rotation, - Scale: placeable.Scale, - Bounds: BoundingBox3.Transform(entityBounds, transform.Transformation), - BreakDefense: vibrate.brokenDefence, - BreakTick: vibrate.brokenTick, - VibrateIndex: vibrateObjectId++); - - break; - } - - if (entity is IMS2CubeProp cube && cube.CubeSalableGroup != 0) { - entityBounds.Min = -new Vector3(HALF_BLOCK, HALF_BLOCK, 0); - entityBounds.Max = new Vector3(HALF_BLOCK, HALF_BLOCK, BLOCK_SIZE); - fieldEntity = new FieldSellableTile( - Id: entityId, - Position: placeable.Position, - Rotation: placeable.Rotation, - Scale: placeable.Scale, - Bounds: BoundingBox3.Transform(entityBounds, transform.Transformation), - SellableGroup: cube.CubeSalableGroup - ); - - break; - } - - bool isFluid = false; - var attribute = MapAttribute.none; - if (entity is IMS2MapProperties meshMapProperties) { - if (meshMapProperties.DisableCollision) { - continue; - } - - isFluid = meshMapProperties.CubeType == "Fluid"; - attribute = Enum.TryParse(meshMapProperties.MapAttribute, out MapAttribute mapAttribute) ? mapAttribute : MapAttribute.none; - if (meshMapProperties.GeneratePhysX) { - var meshPhysXDimension = new Vector3(BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE); - - if (meshMapProperties.GeneratePhysXDimension != Vector3.Zero) { - meshPhysXDimension = meshMapProperties.GeneratePhysXDimension; - } - - entityBounds.Min = -new Vector3(0.5f * meshPhysXDimension.X, 0.5f * meshPhysXDimension.Y, 0); - entityBounds.Max = new Vector3(0.5f * meshPhysXDimension.X, 0.5f * meshPhysXDimension.Y, meshPhysXDimension.Z); - - fieldEntity = new FieldBoxColliderEntity( - Id: entityId, - Position: placeable.Position, - Rotation: placeable.Rotation, - Scale: placeable.Scale, - Bounds: BoundingBox3.Transform(entityBounds, transform.Transformation), - Size: meshPhysXDimension, - IsWhiteBox: false, - IsFluid: isFluid, - MapAttribute: attribute); - - break; - } - } - - if (mesh.NifAsset.Length < 9 || !mesh.NifAsset[..9].Equals("urn:llid:", StringComparison.CurrentCultureIgnoreCase)) { - if (invalidLlids.Add(mesh.NifAsset)) { - Console.WriteLine($"Non llid NifAsset: '{mesh.NifAsset}'"); - } - - continue; - } - - // require length of "urn:llid:XXXXXXXX" - if (mesh.NifAsset.Length < 9 + 8) { - continue; - } - - uint llid = Convert.ToUInt32(mesh.NifAsset.Substring(mesh.NifAsset.LastIndexOf(':') + 1, 8), 16); - - if (!NifParserHelper.nifBounds.TryGetValue(llid, out entityBounds)) { - if (missingLlids.Add(llid)) { - Console.WriteLine($"NIF with LLID {llid:X} not found"); - } - - continue; - } - - if (isFluid && mesh is IMS2MapProperties fluidMapProperties) { - fieldEntity = new FieldFluidEntity( - Id: entityId, - Position: placeable.Position, - Rotation: placeable.Rotation, - Scale: placeable.Scale, - LiquidType: Enum.TryParse(fluidMapProperties.MapAttribute, out LiquidType liquidType) ? liquidType : LiquidType.none, - Bounds: BoundingBox3.Transform(entityBounds, transform.Transformation), - MeshLlid: llid, - IsShallow: false, - IsSurface: true, - MapAttribute: attribute); - break; - } - - fieldEntity = new FieldMeshColliderEntity( - Id: entityId, - Position: placeable.Position, - Rotation: placeable.Rotation, - Scale: placeable.Scale, - Bounds: BoundingBox3.Transform(entityBounds, transform.Transformation), - MeshLlid: llid, - MapAttribute: attribute); - break; - case IMS2MapProperties mapProperties: // GeneratePhysX - if (mapProperties.DisableCollision) { - continue; - } - - if (!mapProperties.GeneratePhysX) { - continue; - } - - var physXDimension = new Vector3(BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE); - - if (mapProperties.GeneratePhysXDimension != Vector3.Zero) { - physXDimension = mapProperties.GeneratePhysXDimension; - } - - entityBounds.Min = -new Vector3(0.5f * physXDimension.X, 0.5f * physXDimension.Y, 0); - entityBounds.Max = new Vector3(0.5f * physXDimension.X, 0.5f * physXDimension.Y, physXDimension.Z); - - fieldEntity = new FieldBoxColliderEntity( - Id: entityId, - Position: placeable.Position, - Rotation: placeable.Rotation, - Scale: placeable.Scale, - Bounds: BoundingBox3.Transform(entityBounds, transform.Transformation), - Size: physXDimension, - IsWhiteBox: false, - IsFluid: false, - MapAttribute: MapAttribute.none); - break; - default: - continue; - } - - entityBounds = BoundingBox3.Transform(entityBounds, transform.Transformation); - - var cellBounds = new BoundingBox3( - min: voxelPosition - new Vector3(HALF_BLOCK, HALF_BLOCK, 0), - max: voxelPosition + new Vector3(HALF_BLOCK, HALF_BLOCK, BLOCK_SIZE)); - - if (!cellBounds.Contains(entityBounds, 1e-5f)) { - // put in list for aabb tree - unalignedEntities.Add(fieldEntity); - - continue; - } - - // grid aligned - minIndex = new Vector3S(Math.Min(minIndex.X, nearestCubeIndex.X), Math.Min(minIndex.Y, nearestCubeIndex.Y), Math.Min(minIndex.Z, nearestCubeIndex.Z)); - maxIndex = new Vector3S(Math.Max(maxIndex.X, nearestCubeIndex.X), Math.Max(maxIndex.Y, nearestCubeIndex.Y), Math.Max(maxIndex.Z, nearestCubeIndex.Z)); - - if (!gridAlignedEntities.TryGetValue(nearestCubeIndex, out List? cellEntities)) { - cellEntities = new List(); - gridAlignedEntities.Add(nearestCubeIndex, cellEntities); - } - - cellEntities.Add(fieldEntity); - } - - maxIndex += new Vector3S(0, 0, 1); // make room for potential spawn tiles - - var fieldData = new FieldAccelerationStructure(); - - fieldData.AddEntities(gridAlignedEntities, minIndex, maxIndex, unalignedEntities, vibrateObjectId); - - return fieldData; - } - - private static byte[] GetEmptyMap() { - var mapData = new FieldAccelerationStructure(); - - var writer = new ByteWriter(); - - writer.WriteClass(mapData); - - return writer.ToArray(); - } - - protected override IEnumerable Map() { - return parser.Parallel().Select(map => { - string xblock = map.xblock.ToLower(); - if (!xBlocks.Contains(xblock)) { - return new MapDataMetadata(xblock, GetEmptyMap()); - } - - FieldAccelerationStructure mapData = ParseMapEntities(map.entities); - - var writer = new ByteWriter(); - - writer.WriteClass(mapData); - - byte[] data = writer.ToArray(); - var dataStream = new MemoryStream(); - - using (var dstream = new DeflateStream(dataStream, CompressionLevel.SmallestSize)) { - dstream.Write(data, 0, data.Length); - } - - data = dataStream.ToArray(); - - lock (this) { - mapByteStats.AddValue((ulong) data.LongLength); - mapGridByteStats.AddValue(mapData.GridBytesWritten); - mapGridBytePercentStats.AddValue((ulong) (10000 * (float) mapData.GridBytesWritten / data.LongLength)); - mapXStats.AddValue((ulong) mapData.GridSize.X); - mapYStats.AddValue((ulong) mapData.GridSize.Y); - mapZStats.AddValue((ulong) mapData.GridSize.Z); - alignedStats.AddValue((ulong) mapData.AlignedEntities.Length); - alignedTrimmedStats.AddValue((ulong) mapData.AlignedTrimmedEntities.Length); - unalignedStats.AddValue((ulong) mapData.UnalignedEntities.Length); - } - - return new MapDataMetadata(xblock, data); - }); - } - - public void ReportStats() { - Console.WriteLine($"Total maps parsed: {mapByteStats.Entries.ToString().ColorBlue()}"); - Console.WriteLine($"Total bytes: {mapByteStats.TotalValue.ToString().ColorBlue()} "); - Console.WriteLine($"Average map bytes: {mapByteStats.AvgValue.ToString().ColorBlue()} "); - Console.WriteLine($"Largest map bytes: {mapByteStats.MaxValue.ToString().ColorBlue()} "); - Console.WriteLine($"Largest map dimensions: {$"< {mapXStats.MaxValue}, {mapYStats.MaxValue}, {mapZStats.MaxValue} >".ColorBlue()} "); - Console.WriteLine($"Average map dimensions: {$"< {mapXStats.AvgValue}, {mapYStats.AvgValue}, {mapZStats.AvgValue} >".ColorBlue()} "); - Console.WriteLine($"Largest aligned entities: {alignedStats.MaxValue.ToString().ColorBlue()} "); - Console.WriteLine($"Average aligned entities: {alignedStats.AvgValue.ToString().ColorBlue()} "); - Console.WriteLine($"Largest trimmed aligned entities: {alignedTrimmedStats.MaxValue.ToString().ColorBlue()} "); - Console.WriteLine($"Average trimmed aligned entities: {alignedTrimmedStats.AvgValue.ToString().ColorBlue()} "); - Console.WriteLine($"Largest unaligned entities: {unalignedStats.MaxValue.ToString().ColorBlue()} "); - Console.WriteLine($"Average unaligned entities: {unalignedStats.AvgValue.ToString().ColorBlue()} "); - } -} +using Maple2.Database.Context; +using Maple2.File.Flat; +using Maple2.File.Flat.maplestory2library; +using Maple2.File.Flat.physxmodellibrary; +using Maple2.File.Flat.standardmodellibrary; +using Maple2.File.Ingest.Helpers; +using Maple2.File.Parser.MapXBlock; +using Maple2.Model.Common; +using Maple2.Model.Game.Field; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools.Extensions; +using Maple2.Tools.VectorMath; +using System.IO.Compression; +using System.Numerics; +using Maple2.Model.Enum; +using Maple2.Model.Metadata.FieldEntity; + +namespace Maple2.File.Ingest.Mapper; + +public class MapDataMapper : TypeMapper { + private const float BLOCK_SIZE = (float) Constant.BlockSize; + private const float HALF_BLOCK = 0.5f * BLOCK_SIZE; + + private readonly HashSet xBlocks; + private readonly XBlockParser parser; + + private readonly StatsTracker mapByteStats = new StatsTracker(); + private readonly StatsTracker mapGridByteStats = new StatsTracker(); + private readonly StatsTracker mapGridBytePercentStats = new StatsTracker(); + private readonly StatsTracker mapXStats = new StatsTracker(); + private readonly StatsTracker mapYStats = new StatsTracker(); + private readonly StatsTracker mapZStats = new StatsTracker(); + private readonly StatsTracker alignedStats = new StatsTracker(); + private readonly StatsTracker alignedTrimmedStats = new StatsTracker(); + private readonly StatsTracker unalignedStats = new StatsTracker(); + private readonly HashSet invalidLlids = []; + private readonly HashSet missingLlids = []; + + public MapDataMapper(MetadataContext db, XBlockParser parser) { + xBlocks = db.MapMetadata.Select(metadata => metadata.XBlock).ToHashSet(); + + this.parser = parser; + } + + private class StatsTracker { + + public ulong MinValue = ulong.MaxValue; + public ulong MaxValue; + public ulong AvgValue { + get { + if (Entries == 0) return 0; + return TotalValue / Entries; + } + } + public ulong Entries; + public ulong TotalValue; + + public StatsTracker() { } + + public void AddValue(ulong value) { + ++Entries; + TotalValue += value; + MinValue = ulong.Min(MinValue, value); + MaxValue = ulong.Max(MaxValue, value); + } + } + + private FieldAccelerationStructure ParseMapEntities(IEnumerable entities) { + var gridAlignedEntities = new Dictionary>(); + var unalignedEntities = new List(); + var minIndex = new Vector3S(short.MaxValue, short.MaxValue, short.MaxValue); + var maxIndex = new Vector3S(short.MinValue, short.MinValue, short.MinValue); + var transform = new Transform(); + + int vibrateObjectId = 0; + + foreach (IMapEntity entity in entities) { + var nearestCubeIndex = new Vector3S(); + + if (entity is not IPlaceable placeable) { + continue; + } + + transform.Transformation = Matrix4x4.Identity; + transform.Position = placeable.Position; + transform.RotationAnglesDegrees = placeable.Rotation; + transform.Scale = placeable.Scale; + + Vector3 position = (1 / BLOCK_SIZE) * (placeable.Position - new Vector3(0, 0, HALF_BLOCK)); // offset to round to nearest + nearestCubeIndex = new Vector3S((short) Math.Floor(position.X + 0.5f), (short) Math.Floor(position.Y + 0.5f), (short) Math.Floor(position.Z + 0.5f)); + Vector3 voxelPosition = BLOCK_SIZE * new Vector3(nearestCubeIndex.X, nearestCubeIndex.Y, nearestCubeIndex.Z); + var entityBounds = new BoundingBox3(); + + bool isHexId = entity.EntityId.Length == 32; + + for (int i = 0; isHexId && i < entity.EntityId.Length; ++i) { + isHexId = entity.EntityId[i].IsHexDigit(); + } + + FieldEntity? fieldEntity; + var entityId = new FieldEntityId(0, 0, string.Empty); + + if (isHexId) { + entityId = FieldEntityId.FromString(entity.EntityId); + } + + switch (entity) { + /* + PhysXProp | WhiteboxCube + PhysXProp, MS2MapProperties, MS2Vibrate | PhysXCube, DoesMakeTok + MS2MapProperties | PhysXCube + MS2MapProperties, MS2Vibrate | None + MS2MapProperties, MS2Breakable | PhysXCube, NxCube, BothCube, OnlyNxCube + MS2Breakable | NxCube + */ + case IMS2Breakable breakable: + continue; // intentionally skip breakables. these are dynamic so should be handled at run time + case IPhysXWhitebox whitebox: + entityBounds.Min = -new Vector3(whitebox.ShapeDimensions.X, whitebox.ShapeDimensions.Y, 0.5f * whitebox.ShapeDimensions.Z); + entityBounds.Max = new Vector3(whitebox.ShapeDimensions.X, whitebox.ShapeDimensions.Y, 0.5f * whitebox.ShapeDimensions.Z); + fieldEntity = new FieldBoxColliderEntity( + Id: entityId, + Position: placeable.Position - new Vector3(0, 0, 0.5f * whitebox.ShapeDimensions.Z), + Rotation: placeable.Rotation, + Scale: placeable.Scale, + Bounds: BoundingBox3.Transform(entityBounds, transform.Transformation), + Size: whitebox.ShapeDimensions, + IsWhiteBox: true, + IsFluid: false, + MapAttribute: MapAttribute.none); + break; + case IMesh mesh: + if (entity is IMS2Vibrate { Enabled: true } vibrate) { + entityBounds.Min = -new Vector3(HALF_BLOCK, HALF_BLOCK, 0); + entityBounds.Max = new Vector3(HALF_BLOCK, HALF_BLOCK, BLOCK_SIZE); + + fieldEntity = new FieldVibrateEntity( + Id: entityId, + Position: placeable.Position, + Rotation: placeable.Rotation, + Scale: placeable.Scale, + Bounds: BoundingBox3.Transform(entityBounds, transform.Transformation), + BreakDefense: vibrate.brokenDefence, + BreakTick: vibrate.brokenTick, + VibrateIndex: vibrateObjectId++); + + break; + } + + if (entity is IMS2CubeProp cube && cube.CubeSalableGroup != 0) { + entityBounds.Min = -new Vector3(HALF_BLOCK, HALF_BLOCK, 0); + entityBounds.Max = new Vector3(HALF_BLOCK, HALF_BLOCK, BLOCK_SIZE); + fieldEntity = new FieldSellableTile( + Id: entityId, + Position: placeable.Position, + Rotation: placeable.Rotation, + Scale: placeable.Scale, + Bounds: BoundingBox3.Transform(entityBounds, transform.Transformation), + SellableGroup: cube.CubeSalableGroup + ); + + break; + } + + bool isFluid = false; + var attribute = MapAttribute.none; + if (entity is IMS2MapProperties meshMapProperties) { + if (meshMapProperties.DisableCollision) { + continue; + } + + isFluid = meshMapProperties.CubeType == "Fluid"; + attribute = Enum.TryParse(meshMapProperties.MapAttribute, out MapAttribute mapAttribute) ? mapAttribute : MapAttribute.none; + if (meshMapProperties.GeneratePhysX) { + var meshPhysXDimension = new Vector3(BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE); + + if (meshMapProperties.GeneratePhysXDimension != Vector3.Zero) { + meshPhysXDimension = meshMapProperties.GeneratePhysXDimension; + } + + entityBounds.Min = -new Vector3(0.5f * meshPhysXDimension.X, 0.5f * meshPhysXDimension.Y, 0); + entityBounds.Max = new Vector3(0.5f * meshPhysXDimension.X, 0.5f * meshPhysXDimension.Y, meshPhysXDimension.Z); + + fieldEntity = new FieldBoxColliderEntity( + Id: entityId, + Position: placeable.Position, + Rotation: placeable.Rotation, + Scale: placeable.Scale, + Bounds: BoundingBox3.Transform(entityBounds, transform.Transformation), + Size: meshPhysXDimension, + IsWhiteBox: false, + IsFluid: isFluid, + MapAttribute: attribute); + + break; + } + } + + if (mesh.NifAsset.Length < 9 || !mesh.NifAsset[..9].Equals("urn:llid:", StringComparison.CurrentCultureIgnoreCase)) { + if (invalidLlids.Add(mesh.NifAsset)) { + Console.WriteLine($"Non llid NifAsset: '{mesh.NifAsset}'"); + } + + continue; + } + + // require length of "urn:llid:XXXXXXXX" + if (mesh.NifAsset.Length < 9 + 8) { + continue; + } + + uint llid = Convert.ToUInt32(mesh.NifAsset.Substring(mesh.NifAsset.LastIndexOf(':') + 1, 8), 16); + + if (!NifParserHelper.nifBounds.TryGetValue(llid, out entityBounds)) { + if (missingLlids.Add(llid)) { + Console.WriteLine($"NIF with LLID {llid:X} not found"); + } + + continue; + } + + if (isFluid && mesh is IMS2MapProperties fluidMapProperties) { + fieldEntity = new FieldFluidEntity( + Id: entityId, + Position: placeable.Position, + Rotation: placeable.Rotation, + Scale: placeable.Scale, + LiquidType: Enum.TryParse(fluidMapProperties.MapAttribute, out LiquidType liquidType) ? liquidType : LiquidType.none, + Bounds: BoundingBox3.Transform(entityBounds, transform.Transformation), + MeshLlid: llid, + IsShallow: false, + IsSurface: true, + MapAttribute: attribute); + break; + } + + fieldEntity = new FieldMeshColliderEntity( + Id: entityId, + Position: placeable.Position, + Rotation: placeable.Rotation, + Scale: placeable.Scale, + Bounds: BoundingBox3.Transform(entityBounds, transform.Transformation), + MeshLlid: llid, + MapAttribute: attribute); + break; + case IMS2MapProperties mapProperties: // GeneratePhysX + if (mapProperties.DisableCollision) { + continue; + } + + if (!mapProperties.GeneratePhysX) { + continue; + } + + var physXDimension = new Vector3(BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE); + + if (mapProperties.GeneratePhysXDimension != Vector3.Zero) { + physXDimension = mapProperties.GeneratePhysXDimension; + } + + entityBounds.Min = -new Vector3(0.5f * physXDimension.X, 0.5f * physXDimension.Y, 0); + entityBounds.Max = new Vector3(0.5f * physXDimension.X, 0.5f * physXDimension.Y, physXDimension.Z); + + fieldEntity = new FieldBoxColliderEntity( + Id: entityId, + Position: placeable.Position, + Rotation: placeable.Rotation, + Scale: placeable.Scale, + Bounds: BoundingBox3.Transform(entityBounds, transform.Transformation), + Size: physXDimension, + IsWhiteBox: false, + IsFluid: false, + MapAttribute: MapAttribute.none); + break; + default: + continue; + } + + entityBounds = BoundingBox3.Transform(entityBounds, transform.Transformation); + + var cellBounds = new BoundingBox3( + min: voxelPosition - new Vector3(HALF_BLOCK, HALF_BLOCK, 0), + max: voxelPosition + new Vector3(HALF_BLOCK, HALF_BLOCK, BLOCK_SIZE)); + + if (!cellBounds.Contains(entityBounds, 1e-5f)) { + // put in list for aabb tree + unalignedEntities.Add(fieldEntity); + + continue; + } + + // grid aligned + minIndex = new Vector3S(Math.Min(minIndex.X, nearestCubeIndex.X), Math.Min(minIndex.Y, nearestCubeIndex.Y), Math.Min(minIndex.Z, nearestCubeIndex.Z)); + maxIndex = new Vector3S(Math.Max(maxIndex.X, nearestCubeIndex.X), Math.Max(maxIndex.Y, nearestCubeIndex.Y), Math.Max(maxIndex.Z, nearestCubeIndex.Z)); + + if (!gridAlignedEntities.TryGetValue(nearestCubeIndex, out List? cellEntities)) { + cellEntities = new List(); + gridAlignedEntities.Add(nearestCubeIndex, cellEntities); + } + + cellEntities.Add(fieldEntity); + } + + maxIndex += new Vector3S(0, 0, 1); // make room for potential spawn tiles + + var fieldData = new FieldAccelerationStructure(); + + fieldData.AddEntities(gridAlignedEntities, minIndex, maxIndex, unalignedEntities, vibrateObjectId); + + return fieldData; + } + + private static byte[] GetEmptyMap() { + var mapData = new FieldAccelerationStructure(); + + var writer = new ByteWriter(); + + writer.WriteClass(mapData); + + return writer.ToArray(); + } + + protected override IEnumerable Map() { + return parser.Parallel().Select(map => { + string xblock = map.xblock.ToLower(); + if (!xBlocks.Contains(xblock)) { + return new MapDataMetadata(xblock, GetEmptyMap()); + } + + FieldAccelerationStructure mapData = ParseMapEntities(map.entities); + + var writer = new ByteWriter(); + + writer.WriteClass(mapData); + + byte[] data = writer.ToArray(); + var dataStream = new MemoryStream(); + + using (var dstream = new DeflateStream(dataStream, CompressionLevel.SmallestSize)) { + dstream.Write(data, 0, data.Length); + } + + data = dataStream.ToArray(); + + lock (this) { + mapByteStats.AddValue((ulong) data.LongLength); + mapGridByteStats.AddValue(mapData.GridBytesWritten); + mapGridBytePercentStats.AddValue((ulong) (10000 * (float) mapData.GridBytesWritten / data.LongLength)); + mapXStats.AddValue((ulong) mapData.GridSize.X); + mapYStats.AddValue((ulong) mapData.GridSize.Y); + mapZStats.AddValue((ulong) mapData.GridSize.Z); + alignedStats.AddValue((ulong) mapData.AlignedEntities.Length); + alignedTrimmedStats.AddValue((ulong) mapData.AlignedTrimmedEntities.Length); + unalignedStats.AddValue((ulong) mapData.UnalignedEntities.Length); + } + + return new MapDataMetadata(xblock, data); + }); + } + + public void ReportStats() { + Console.WriteLine($"Total maps parsed: {mapByteStats.Entries.ToString().ColorBlue()}"); + Console.WriteLine($"Total bytes: {mapByteStats.TotalValue.ToString().ColorBlue()} "); + Console.WriteLine($"Average map bytes: {mapByteStats.AvgValue.ToString().ColorBlue()} "); + Console.WriteLine($"Largest map bytes: {mapByteStats.MaxValue.ToString().ColorBlue()} "); + Console.WriteLine($"Largest map dimensions: {$"< {mapXStats.MaxValue}, {mapYStats.MaxValue}, {mapZStats.MaxValue} >".ColorBlue()} "); + Console.WriteLine($"Average map dimensions: {$"< {mapXStats.AvgValue}, {mapYStats.AvgValue}, {mapZStats.AvgValue} >".ColorBlue()} "); + Console.WriteLine($"Largest aligned entities: {alignedStats.MaxValue.ToString().ColorBlue()} "); + Console.WriteLine($"Average aligned entities: {alignedStats.AvgValue.ToString().ColorBlue()} "); + Console.WriteLine($"Largest trimmed aligned entities: {alignedTrimmedStats.MaxValue.ToString().ColorBlue()} "); + Console.WriteLine($"Average trimmed aligned entities: {alignedTrimmedStats.AvgValue.ToString().ColorBlue()} "); + Console.WriteLine($"Largest unaligned entities: {unalignedStats.MaxValue.ToString().ColorBlue()} "); + Console.WriteLine($"Average unaligned entities: {unalignedStats.AvgValue.ToString().ColorBlue()} "); + } +} diff --git a/Maple2.File.Ingest/Mapper/MapEntityMapper.cs b/Maple2.File.Ingest/Mapper/MapEntityMapper.cs index ad8349fae..f9f9b2957 100644 --- a/Maple2.File.Ingest/Mapper/MapEntityMapper.cs +++ b/Maple2.File.Ingest/Mapper/MapEntityMapper.cs @@ -1,316 +1,316 @@ -using Maple2.Database.Context; -using Maple2.File.Flat; -using Maple2.File.Flat.maplestory2library; -using Maple2.File.Flat.standardmodellibrary; -using Maple2.File.Parser.MapXBlock; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.Tools.Extensions; -using static M2dXmlGenerator.FeatureLocaleFilter; - -namespace Maple2.File.Ingest.Mapper; - -public class MapEntityMapper : TypeMapper { - private readonly HashSet xBlocks; - private readonly XBlockParser parser; - - public MapEntityMapper(MetadataContext db, XBlockParser parser) { - xBlocks = db.MapMetadata.Select(metadata => metadata.XBlock).ToHashSet(); - - this.parser = parser; - } - - private IEnumerable ParseMap(string xblock, IEnumerable entities) { - IMS2Bounding? firstBounding = null; - IMS2Bounding? secondBounding = null; - - Dictionary ms2WayPoints = new(); - foreach (var wayPoint in entities) { - switch (wayPoint) { - case IMS2WayPoint ms2WayPoint: - ms2WayPoints.Add(ms2WayPoint.EntityId, ms2WayPoint); - break; - default: - break; - } - } - - foreach (IMapEntity entity in entities) { - switch (entity) { - case IMS2InteractObject interactObject: - - switch (interactObject) { - case IMS2InteractActor interactActor: - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new Ms2InteractActor(interactActor.interactID, interactActor.Position, interactActor.Rotation), - }; - continue; - case IMS2InteractDisplay interactDisplay: - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new Ms2InteractDisplay(interactDisplay.interactID, interactDisplay.Position, interactDisplay.Rotation), - }; - continue; - case IMS2InteractMesh interactMesh: - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new Ms2InteractMesh(interactMesh.interactID, interactMesh.Position, interactMesh.Rotation), - }; - continue; - case IMS2SimpleUiObject simpleUiObject: - continue; - case IMS2Telescope telescope: - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new Ms2Telescope(telescope.interactID, telescope.Position, telescope.Rotation), - }; - continue; - } - continue; - case IPortal portal: - if (!FeatureEnabled(portal.feature) || !HasLocale(portal.locale)) continue; - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new Portal(portal.PortalID, portal.TargetFieldSN, portal.TargetPortalID, (PortalType) portal.PortalType, (PortalActionType) portal.ActionType, portal.Position, portal.Rotation, portal.PortalDimension, portal.frontOffset, portal.RandomDestRadius, portal.IsVisible, portal.MinimapIconVisible, portal.PortalEnable), - }; - continue; - case ISpawnPoint spawn: - switch (spawn) { - case ISpawnPointPC pcSpawn: - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new SpawnPointPC(pcSpawn.SpawnPointID, pcSpawn.Position, pcSpawn.Rotation, pcSpawn.IsVisible, pcSpawn.Enable), - }; - continue; - case ISpawnPointNPC npcSpawn: - IList npcList = npcSpawn.NpcList.Select(entry => { - if (!int.TryParse(entry.Key, out int npcId)) { - return null; - } - if (!int.TryParse(entry.Value, out int npcCount)) { - npcCount = 1; - } - - NpcMapper.NpcMetadataById.TryGetValue(npcId, out NpcMetadata? npcMetadata); - if (npcMetadata == null) { - return null; - } - - // Some NPC spawns have these be equal, so default to 1 - if (npcId == npcCount) { - npcCount = 1; - } - - return new SpawnPointNPCListEntry(npcId, npcCount); - }).WhereNotNull().ToList(); - if (npcSpawn.NpcCount == 0 || npcList.Count == 0) { - Console.WriteLine($"No NPCs for {xblock}:{entity.EntityId}"); - continue; - } - - switch (npcSpawn) { - case IEventSpawnPointNPC eventNpcSpawn: - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new EventSpawnPointNPC(npcSpawn.EntityId, npcSpawn.SpawnPointID, npcSpawn.Position, npcSpawn.Rotation, npcSpawn.IsVisible, npcSpawn.IsSpawnOnFieldCreate, npcSpawn.SpawnRadius, npcList, (int) npcSpawn.RegenCheckTime, (int) eventNpcSpawn.LifeTime, eventNpcSpawn.SpawnAnimation), - }; - continue; - default: - string? patrolData = npcSpawn.PatrolData != "00000000-0000-0000-0000-000000000000" ? npcSpawn.PatrolData.Replace("-", string.Empty) : null; - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new SpawnPointNPC(npcSpawn.EntityId, npcSpawn.SpawnPointID, npcSpawn.Position, npcSpawn.Rotation, npcSpawn.IsVisible, npcSpawn.IsSpawnOnFieldCreate, npcSpawn.SpawnRadius, npcList, (int) npcSpawn.RegenCheckTime, patrolData), - }; - continue; - } - case IEventSpawnPointItem itemSpawn: - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new EventSpawnPointItem(itemSpawn.SpawnPointID, itemSpawn.Position, itemSpawn.Rotation, itemSpawn.LifeTime, int.TryParse(itemSpawn.individualDropBoxId, out int individualDropBoxId) ? individualDropBoxId : 0, int.TryParse(itemSpawn.globalDropBoxId, out int globalDropBoxId) ? globalDropBoxId : 0, (int) itemSpawn.globalDropLevel, itemSpawn.IsVisible), - }; - continue; - } - continue; - case IMS2RegionSpawnBase spawn: - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new Ms2RegionSpawn(spawn.SpawnPointID, spawn.UseRotAsSpawnDir, spawn.Position, spawn.Rotation), - }; - continue; - case IMS2TriggerObject triggerObject: - MapEntity? trigger = ParseTrigger(xblock, triggerObject); - if (trigger != null) { - yield return trigger; - } - continue; - case IMS2RegionSkill skill: - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new Ms2RegionSkill(skill.skillID, (short) skill.skillLevel, skill.Interval, skill.Position, skill.Rotation), - }; - continue; - // case IMS2Breakable breakable: { - // switch (breakable) { - // case IMS2BreakableNIF nif: - // yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - // Block = new Breakable(nif.IsVisible, (int) nif.TriggerBreakableID, nif.hideTimer, nif.resetTimer, nif.Position, nif.Rotation) - // }; - // continue; - // } - // continue; - // } - case IMS2TriggerModel triggerModel: - string name = Path.GetFileNameWithoutExtension(triggerModel.XmlFilePath); - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new TriggerModel(triggerModel.TriggerModelID, name, triggerModel.Position, triggerModel.Rotation), - }; - continue; - case IMS2Bounding bounding: - if (firstBounding == null) { - firstBounding = bounding; - continue; - } - // Map 020000118 has 3 bounding boxes. Quick fix to ignore the 3rd. - if (secondBounding == null) { - secondBounding = bounding; - yield return new MapEntity(xblock, new Guid(entity.EntityId), $"{firstBounding.EntityName},{bounding.EntityName}") { - Block = new Ms2Bounding(firstBounding.Position, bounding.Position), - }; - } - continue; - case IMS2LiftableTargetBox liftableTargetBox: - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new LiftableTargetBox(liftableTargetBox.Position, liftableTargetBox.Rotation, liftableTargetBox.isForceFinish, liftableTargetBox.liftableTarget), - }; - continue; - case IMS2MapProperties mapProperties: - switch (mapProperties) { - case IMS2PhysXProp physXProp: - if (mapProperties.IsObjectWeapon) { - int[] itemIds = physXProp.ObjectWeaponItemCode.Split(',').Select(int.Parse).ToArray(); - if (physXProp.ObjectWeaponSpawnNpcCode == 0) { - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new ObjectWeapon(itemIds, (int) physXProp.ObjectWeaponRespawnTick, physXProp.ObjectWeaponActiveDistance, physXProp.Position, physXProp.Rotation), - }; - } else { - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new ObjectWeapon(itemIds, (int) physXProp.ObjectWeaponRespawnTick, physXProp.ObjectWeaponActiveDistance, physXProp.Position, physXProp.Rotation, (int) physXProp.ObjectWeaponSpawnNpcCode, (int) physXProp.ObjectWeaponSpawnNpcCount, physXProp.ObjectWeaponSpawnNpcRate, (int) physXProp.ObjectWeaponSpawnNpcLifeTick), - }; - } - continue; - } - - switch (physXProp) { - case IMS2Liftable liftable: - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new Liftable((int) liftable.ItemID, liftable.ItemStackCount, liftable.ItemLifeTime, liftable.LiftableRegenCheckTime, liftable.LiftableFinishTime, liftable.MaskQuestID, liftable.MaskQuestState, liftable.EffectQuestID, liftable.EffectQuestState, liftable.IsReactEffect, liftable.Position, liftable.Rotation), - }; - continue; - case IMS2TaxiStation taxiStation: - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new TaxiStation(taxiStation.Position, taxiStation.Rotation), - }; - continue; - // Intentionally do not parse IMS2Vibrate, there are 4M entries. - // case IMS2Vibrate vibrate: - } - continue; - } - continue; - case IActor actor: { - switch (actor) { - case IMS2BreakableActor breakable: - int.TryParse(breakable.additionGlobalDropBoxId, out int globalDropBoxId); - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new BreakableActor(actor.IsVisible, (int) breakable.TriggerBreakableID, breakable.hideTimer, breakable.resetTimer, globalDropBoxId, breakable.Position, breakable.Rotation), - }; - continue; - } - continue; - } - case IMS2PatrolData patrolData: - List wayPoints = []; - foreach (KeyValuePair wayPointDict in patrolData.WayPoints) { - IMS2WayPoint? wayPoint = ms2WayPoints.GetValueOrDefault(wayPointDict.Value.Replace("-", string.Empty)); - if (wayPoint is null) { - continue; - } - - patrolData.ApproachAnims.TryGetValue(wayPointDict.Key, out string? approachAnimation); - patrolData.ArriveAnims.TryGetValue(wayPointDict.Key, out string? arriveAnimation); - patrolData.ArriveAnimsTime.TryGetValue(wayPointDict.Key, out uint arriveAnimationTime); - wayPoints.Add(new MS2WayPoint(wayPoint.EntityId, wayPoint.IsVisible, wayPoint.Position, wayPoint.Rotation, approachAnimation ?? "", arriveAnimation ?? "", (int) arriveAnimationTime, patrolData.IsAirWayPoint)); - } - - - yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { - Block = new MS2PatrolData(patrolData.EntityId, patrolData.EntityName, patrolData.IsAirWayPoint, (int) patrolData.PatrolSpeed, patrolData.IsLoop, wayPoints), - }; - continue; - } - } - - } - - private MapEntity? ParseTrigger(string xblock, IMS2TriggerObject trigger) { - switch (trigger) { - case IMS2TriggerActor actor: - return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { - Block = new Ms2TriggerActor(actor.InitialSequence, actor.TriggerObjectID, actor.IsVisible), - }; - case IMS2TriggerAgent agent: - return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { - Block = new Ms2TriggerAgent(agent.TriggerObjectID, agent.IsVisible), - }; - case IMS2TriggerBlock block: - return null; - case IMS2TriggerBox box: - return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { - Block = new Ms2TriggerBox(box.Position, box.ShapeDimensions, box.TriggerObjectID, box.IsVisible), - }; - case IMS2TriggerCamera camera: - return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { - Block = new Ms2TriggerCamera(camera.TriggerObjectID, camera.IsVisible), - }; - case IMS2TriggerCube cube: - return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { - Block = new Ms2TriggerCube(cube.TriggerObjectID, cube.IsVisible), - }; - case IMS2TriggerEffect effect: - return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { - Block = new Ms2TriggerEffect(effect.TriggerObjectID, effect.IsVisible), - }; - case IMS2TriggerLadder ladder: - return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { - Block = new Ms2TriggerLadder(ladder.TriggerObjectID, ladder.IsVisible), - }; - case IMS2TriggerMesh mesh: - return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { - Block = new Ms2TriggerMesh(mesh.Scale, mesh.TriggerObjectID, mesh.IsVisible, mesh.MinimapInVisible), - }; - case IMS2TriggerPortal _: - throw new InvalidOperationException("IMS2TriggerPortal should be parsed as IPortal."); - case IMS2TriggerRope rope: - return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { - Block = new Ms2TriggerRope(rope.TriggerObjectID, rope.IsVisible), - }; - case IMS2TriggerSkill skill: - if (skill.skillID <= 0 || skill.skillLevel <= 0) { - return null; - } - return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { - Block = new Ms2TriggerSkill(skill.skillID, (short) skill.skillLevel, skill.Position, skill.Rotation, skill.TriggerObjectID, skill.IsVisible), - }; - case IMS2TriggerSound sound: - return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { - Block = new Ms2TriggerSound(sound.TriggerObjectID, sound.IsVisible), - }; - } - - // Generic MS2TriggerObject - return null; - } - - protected override IEnumerable Map() { - return parser.Parallel().SelectMany(map => { - string xblock = map.xblock.ToLower(); - if (!xBlocks.Contains(xblock)) { - return []; - } - - return ParseMap(xblock, map.entities); - }) // Ordering to ensure deterministic checksums. - .OrderBy(entity => entity.XBlock) - .ThenBy(entity => entity.Guid); - } -} +using Maple2.Database.Context; +using Maple2.File.Flat; +using Maple2.File.Flat.maplestory2library; +using Maple2.File.Flat.standardmodellibrary; +using Maple2.File.Parser.MapXBlock; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.Tools.Extensions; +using static M2dXmlGenerator.FeatureLocaleFilter; + +namespace Maple2.File.Ingest.Mapper; + +public class MapEntityMapper : TypeMapper { + private readonly HashSet xBlocks; + private readonly XBlockParser parser; + + public MapEntityMapper(MetadataContext db, XBlockParser parser) { + xBlocks = db.MapMetadata.Select(metadata => metadata.XBlock).ToHashSet(); + + this.parser = parser; + } + + private IEnumerable ParseMap(string xblock, IEnumerable entities) { + IMS2Bounding? firstBounding = null; + IMS2Bounding? secondBounding = null; + + Dictionary ms2WayPoints = new(); + foreach (var wayPoint in entities) { + switch (wayPoint) { + case IMS2WayPoint ms2WayPoint: + ms2WayPoints.Add(ms2WayPoint.EntityId, ms2WayPoint); + break; + default: + break; + } + } + + foreach (IMapEntity entity in entities) { + switch (entity) { + case IMS2InteractObject interactObject: + + switch (interactObject) { + case IMS2InteractActor interactActor: + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new Ms2InteractActor(interactActor.interactID, interactActor.Position, interactActor.Rotation), + }; + continue; + case IMS2InteractDisplay interactDisplay: + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new Ms2InteractDisplay(interactDisplay.interactID, interactDisplay.Position, interactDisplay.Rotation), + }; + continue; + case IMS2InteractMesh interactMesh: + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new Ms2InteractMesh(interactMesh.interactID, interactMesh.Position, interactMesh.Rotation), + }; + continue; + case IMS2SimpleUiObject simpleUiObject: + continue; + case IMS2Telescope telescope: + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new Ms2Telescope(telescope.interactID, telescope.Position, telescope.Rotation), + }; + continue; + } + continue; + case IPortal portal: + if (!FeatureEnabled(portal.feature) || !HasLocale(portal.locale)) continue; + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new Portal(portal.PortalID, portal.TargetFieldSN, portal.TargetPortalID, (PortalType) portal.PortalType, (PortalActionType) portal.ActionType, portal.Position, portal.Rotation, portal.PortalDimension, portal.frontOffset, portal.RandomDestRadius, portal.IsVisible, portal.MinimapIconVisible, portal.PortalEnable), + }; + continue; + case ISpawnPoint spawn: + switch (spawn) { + case ISpawnPointPC pcSpawn: + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new SpawnPointPC(pcSpawn.SpawnPointID, pcSpawn.Position, pcSpawn.Rotation, pcSpawn.IsVisible, pcSpawn.Enable), + }; + continue; + case ISpawnPointNPC npcSpawn: + IList npcList = npcSpawn.NpcList.Select(entry => { + if (!int.TryParse(entry.Key, out int npcId)) { + return null; + } + if (!int.TryParse(entry.Value, out int npcCount)) { + npcCount = 1; + } + + NpcMapper.NpcMetadataById.TryGetValue(npcId, out NpcMetadata? npcMetadata); + if (npcMetadata == null) { + return null; + } + + // Some NPC spawns have these be equal, so default to 1 + if (npcId == npcCount) { + npcCount = 1; + } + + return new SpawnPointNPCListEntry(npcId, npcCount); + }).WhereNotNull().ToList(); + if (npcSpawn.NpcCount == 0 || npcList.Count == 0) { + Console.WriteLine($"No NPCs for {xblock}:{entity.EntityId}"); + continue; + } + + switch (npcSpawn) { + case IEventSpawnPointNPC eventNpcSpawn: + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new EventSpawnPointNPC(npcSpawn.EntityId, npcSpawn.SpawnPointID, npcSpawn.Position, npcSpawn.Rotation, npcSpawn.IsVisible, npcSpawn.IsSpawnOnFieldCreate, npcSpawn.SpawnRadius, npcList, (int) npcSpawn.RegenCheckTime, (int) eventNpcSpawn.LifeTime, eventNpcSpawn.SpawnAnimation), + }; + continue; + default: + string? patrolData = npcSpawn.PatrolData != "00000000-0000-0000-0000-000000000000" ? npcSpawn.PatrolData.Replace("-", string.Empty) : null; + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new SpawnPointNPC(npcSpawn.EntityId, npcSpawn.SpawnPointID, npcSpawn.Position, npcSpawn.Rotation, npcSpawn.IsVisible, npcSpawn.IsSpawnOnFieldCreate, npcSpawn.SpawnRadius, npcList, (int) npcSpawn.RegenCheckTime, patrolData), + }; + continue; + } + case IEventSpawnPointItem itemSpawn: + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new EventSpawnPointItem(itemSpawn.SpawnPointID, itemSpawn.Position, itemSpawn.Rotation, itemSpawn.LifeTime, int.TryParse(itemSpawn.individualDropBoxId, out int individualDropBoxId) ? individualDropBoxId : 0, int.TryParse(itemSpawn.globalDropBoxId, out int globalDropBoxId) ? globalDropBoxId : 0, (int) itemSpawn.globalDropLevel, itemSpawn.IsVisible), + }; + continue; + } + continue; + case IMS2RegionSpawnBase spawn: + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new Ms2RegionSpawn(spawn.SpawnPointID, spawn.UseRotAsSpawnDir, spawn.Position, spawn.Rotation), + }; + continue; + case IMS2TriggerObject triggerObject: + MapEntity? trigger = ParseTrigger(xblock, triggerObject); + if (trigger != null) { + yield return trigger; + } + continue; + case IMS2RegionSkill skill: + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new Ms2RegionSkill(skill.skillID, (short) skill.skillLevel, skill.Interval, skill.Position, skill.Rotation), + }; + continue; + // case IMS2Breakable breakable: { + // switch (breakable) { + // case IMS2BreakableNIF nif: + // yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + // Block = new Breakable(nif.IsVisible, (int) nif.TriggerBreakableID, nif.hideTimer, nif.resetTimer, nif.Position, nif.Rotation) + // }; + // continue; + // } + // continue; + // } + case IMS2TriggerModel triggerModel: + string name = Path.GetFileNameWithoutExtension(triggerModel.XmlFilePath); + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new TriggerModel(triggerModel.TriggerModelID, name, triggerModel.Position, triggerModel.Rotation), + }; + continue; + case IMS2Bounding bounding: + if (firstBounding == null) { + firstBounding = bounding; + continue; + } + // Map 020000118 has 3 bounding boxes. Quick fix to ignore the 3rd. + if (secondBounding == null) { + secondBounding = bounding; + yield return new MapEntity(xblock, new Guid(entity.EntityId), $"{firstBounding.EntityName},{bounding.EntityName}") { + Block = new Ms2Bounding(firstBounding.Position, bounding.Position), + }; + } + continue; + case IMS2LiftableTargetBox liftableTargetBox: + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new LiftableTargetBox(liftableTargetBox.Position, liftableTargetBox.Rotation, liftableTargetBox.isForceFinish, liftableTargetBox.liftableTarget), + }; + continue; + case IMS2MapProperties mapProperties: + switch (mapProperties) { + case IMS2PhysXProp physXProp: + if (mapProperties.IsObjectWeapon) { + int[] itemIds = physXProp.ObjectWeaponItemCode.Split(',').Select(int.Parse).ToArray(); + if (physXProp.ObjectWeaponSpawnNpcCode == 0) { + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new ObjectWeapon(itemIds, (int) physXProp.ObjectWeaponRespawnTick, physXProp.ObjectWeaponActiveDistance, physXProp.Position, physXProp.Rotation), + }; + } else { + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new ObjectWeapon(itemIds, (int) physXProp.ObjectWeaponRespawnTick, physXProp.ObjectWeaponActiveDistance, physXProp.Position, physXProp.Rotation, (int) physXProp.ObjectWeaponSpawnNpcCode, (int) physXProp.ObjectWeaponSpawnNpcCount, physXProp.ObjectWeaponSpawnNpcRate, (int) physXProp.ObjectWeaponSpawnNpcLifeTick), + }; + } + continue; + } + + switch (physXProp) { + case IMS2Liftable liftable: + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new Liftable((int) liftable.ItemID, liftable.ItemStackCount, liftable.ItemLifeTime, liftable.LiftableRegenCheckTime, liftable.LiftableFinishTime, liftable.MaskQuestID, liftable.MaskQuestState, liftable.EffectQuestID, liftable.EffectQuestState, liftable.IsReactEffect, liftable.Position, liftable.Rotation), + }; + continue; + case IMS2TaxiStation taxiStation: + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new TaxiStation(taxiStation.Position, taxiStation.Rotation), + }; + continue; + // Intentionally do not parse IMS2Vibrate, there are 4M entries. + // case IMS2Vibrate vibrate: + } + continue; + } + continue; + case IActor actor: { + switch (actor) { + case IMS2BreakableActor breakable: + int.TryParse(breakable.additionGlobalDropBoxId, out int globalDropBoxId); + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new BreakableActor(actor.IsVisible, (int) breakable.TriggerBreakableID, breakable.hideTimer, breakable.resetTimer, globalDropBoxId, breakable.Position, breakable.Rotation), + }; + continue; + } + continue; + } + case IMS2PatrolData patrolData: + List wayPoints = []; + foreach (KeyValuePair wayPointDict in patrolData.WayPoints) { + IMS2WayPoint? wayPoint = ms2WayPoints.GetValueOrDefault(wayPointDict.Value.Replace("-", string.Empty)); + if (wayPoint is null) { + continue; + } + + patrolData.ApproachAnims.TryGetValue(wayPointDict.Key, out string? approachAnimation); + patrolData.ArriveAnims.TryGetValue(wayPointDict.Key, out string? arriveAnimation); + patrolData.ArriveAnimsTime.TryGetValue(wayPointDict.Key, out uint arriveAnimationTime); + wayPoints.Add(new MS2WayPoint(wayPoint.EntityId, wayPoint.IsVisible, wayPoint.Position, wayPoint.Rotation, approachAnimation ?? "", arriveAnimation ?? "", (int) arriveAnimationTime, patrolData.IsAirWayPoint)); + } + + + yield return new MapEntity(xblock, new Guid(entity.EntityId), entity.EntityName) { + Block = new MS2PatrolData(patrolData.EntityId, patrolData.EntityName, patrolData.IsAirWayPoint, (int) patrolData.PatrolSpeed, patrolData.IsLoop, wayPoints), + }; + continue; + } + } + + } + + private MapEntity? ParseTrigger(string xblock, IMS2TriggerObject trigger) { + switch (trigger) { + case IMS2TriggerActor actor: + return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { + Block = new Ms2TriggerActor(actor.InitialSequence, actor.TriggerObjectID, actor.IsVisible), + }; + case IMS2TriggerAgent agent: + return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { + Block = new Ms2TriggerAgent(agent.TriggerObjectID, agent.IsVisible), + }; + case IMS2TriggerBlock block: + return null; + case IMS2TriggerBox box: + return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { + Block = new Ms2TriggerBox(box.Position, box.ShapeDimensions, box.TriggerObjectID, box.IsVisible), + }; + case IMS2TriggerCamera camera: + return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { + Block = new Ms2TriggerCamera(camera.TriggerObjectID, camera.IsVisible), + }; + case IMS2TriggerCube cube: + return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { + Block = new Ms2TriggerCube(cube.TriggerObjectID, cube.IsVisible), + }; + case IMS2TriggerEffect effect: + return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { + Block = new Ms2TriggerEffect(effect.TriggerObjectID, effect.IsVisible), + }; + case IMS2TriggerLadder ladder: + return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { + Block = new Ms2TriggerLadder(ladder.TriggerObjectID, ladder.IsVisible), + }; + case IMS2TriggerMesh mesh: + return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { + Block = new Ms2TriggerMesh(mesh.Scale, mesh.TriggerObjectID, mesh.IsVisible, mesh.MinimapInVisible), + }; + case IMS2TriggerPortal _: + throw new InvalidOperationException("IMS2TriggerPortal should be parsed as IPortal."); + case IMS2TriggerRope rope: + return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { + Block = new Ms2TriggerRope(rope.TriggerObjectID, rope.IsVisible), + }; + case IMS2TriggerSkill skill: + if (skill.skillID <= 0 || skill.skillLevel <= 0) { + return null; + } + return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { + Block = new Ms2TriggerSkill(skill.skillID, (short) skill.skillLevel, skill.Position, skill.Rotation, skill.TriggerObjectID, skill.IsVisible), + }; + case IMS2TriggerSound sound: + return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { + Block = new Ms2TriggerSound(sound.TriggerObjectID, sound.IsVisible), + }; + } + + // Generic MS2TriggerObject + return null; + } + + protected override IEnumerable Map() { + return parser.Parallel().SelectMany(map => { + string xblock = map.xblock.ToLower(); + if (!xBlocks.Contains(xblock)) { + return []; + } + + return ParseMap(xblock, map.entities); + }) // Ordering to ensure deterministic checksums. + .OrderBy(entity => entity.XBlock) + .ThenBy(entity => entity.Guid); + } +} diff --git a/Maple2.File.Ingest/Mapper/MapMapper.cs b/Maple2.File.Ingest/Mapper/MapMapper.cs index 7fcd6dc12..e99fec170 100644 --- a/Maple2.File.Ingest/Mapper/MapMapper.cs +++ b/Maple2.File.Ingest/Mapper/MapMapper.cs @@ -1,97 +1,97 @@ -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Xml.Map; -using Maple2.File.Parser.Xml.Table; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; - -namespace Maple2.File.Ingest.Mapper; - -public class MapMapper : TypeMapper { - private readonly TableParser spawnParser; - private readonly MapParser parser; - - public MapMapper(M2dReader xmlReader, string language) { - spawnParser = new TableParser(xmlReader, language); - parser = new MapParser(xmlReader, language); - } - - protected override IEnumerable Map() { - var pets = new Dictionary>(); - foreach ((int mapId, IEnumerable infos) in spawnParser.ParsePetSpawnInfo()) { - pets[mapId] = infos.ToDictionary(info => info.npcID, info => info.petID); - } - - var spawns = new Dictionary>(); - foreach ((int mapId, IEnumerable regions) in spawnParser.ParseMapSpawnTag()) { - spawns[mapId] = regions.Select(region => new MapMetadataSpawn( - Id: region.spawnPointID, - MinDifficulty: region.difficultyMin, - MaxDifficulty: region.difficulty, - Population: region.population, - Cooldown: region.coolTime, - Tags: region.tag, - PetPopulation: region.petPopulation, - PetSpawnRate: region.petSpawnProbability, - PetIds: pets.GetValueOrDefault(mapId, new Dictionary())) - ).ToList(); - } - - foreach ((int id, string name, MapData data) in parser.Parse()) { - yield return new MapMetadata( - Id: id, - Name: name, - XBlock: data.xblock.name.ToLower(), - Property: new MapMetadataProperty( - Continent: (Continent) data.property.continentCode, - Region: (MapRegion) data.property.regionCode, - Category: data.property.mapCategoryCode, - Type: (MapType) data.property.mapType, - BigCity: data.property.bigCity, - ExploreType: data.property.exploreType, - TutorialType: data.property.tutorialType, - RevivalReturnId: data.property.revivalreturnid, - EnterReturnId: data.property.enterreturnid, - AutoRevivalType: (AutoReviveType) data.property.autoRevivalType, - AutoRevivalTime: data.property.autoRevivalTime, - InfiniteMeretRevival: data.property.infinityMeratRevival, - NoRevivalHere: data.property.doNotRevivalHere, - ReviveFullHp: data.property.recoveryFullHP, - UseTimeEvent: data.property.useTimeEvent, - HomeReturnable: data.property.homeReturnable, - DeathPenalty: data.property.deathPenalty, - OnlyDarkTomb: data.property.onlyDarkTomb, - PkMode: data.property.pkMode, - CanFly: data.property.checkFly, - CanClimb: data.property.checkClimb, - IndoorType: data.indoor.type), - Limit: new MapMetadataLimit( - Capacity: data.property.capacity, - MinLevel: data.property.enterMinLevel, - MaxLevel: data.property.enterMaxLevel, - RequireQuest: data.property.requireQuest, - DisableSkills: data.property.skillUseDisable, - Climb: data.property.checkClimb, - Fly: data.property.checkFly, - Move: data.property.limitMove, - FallDamage: data.ui.fallDamage, - Dash: data.ui.useEPSkill, - Ride: data.ui.useRidee, - Pet: data.ui.usePet), - Drop: new MapMetadataDrop( - Level: data.drop.maplevel, - DropRank: data.drop.droprank, - GlobalDropBoxId: data.drop.globalDropBoxID), - Spawns: spawns.GetValueOrDefault(id, new List()), - CashCall: new MapMetadataCashCall( - TaxiDeparture: !data.cashCall.cashTaxiNotDeparture, - TaxiDestination: !data.cashCall.cashTaxiNotDestination, - Medic: !data.cashCall.cashCallMedicProhibit, - Market: !data.cashCall.cashCallMarketProhibit, - Recall: !data.cashCall.RecallOtherUserProhibit), - // TODO: There are also EntranceBuffs for Survival - EntranceBuffs: data.property.enteranceBuffIDs.Zip(data.property.enteranceBuffLevels, - (skillId, level) => new MapEntranceBuff(skillId, level)).ToArray()); - } - } -} +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Xml.Map; +using Maple2.File.Parser.Xml.Table; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; + +namespace Maple2.File.Ingest.Mapper; + +public class MapMapper : TypeMapper { + private readonly TableParser spawnParser; + private readonly MapParser parser; + + public MapMapper(M2dReader xmlReader, string language) { + spawnParser = new TableParser(xmlReader, language); + parser = new MapParser(xmlReader, language); + } + + protected override IEnumerable Map() { + var pets = new Dictionary>(); + foreach ((int mapId, IEnumerable infos) in spawnParser.ParsePetSpawnInfo()) { + pets[mapId] = infos.ToDictionary(info => info.npcID, info => info.petID); + } + + var spawns = new Dictionary>(); + foreach ((int mapId, IEnumerable regions) in spawnParser.ParseMapSpawnTag()) { + spawns[mapId] = regions.Select(region => new MapMetadataSpawn( + Id: region.spawnPointID, + MinDifficulty: region.difficultyMin, + MaxDifficulty: region.difficulty, + Population: region.population, + Cooldown: region.coolTime, + Tags: region.tag, + PetPopulation: region.petPopulation, + PetSpawnRate: region.petSpawnProbability, + PetIds: pets.GetValueOrDefault(mapId, new Dictionary())) + ).ToList(); + } + + foreach ((int id, string name, MapData data) in parser.Parse()) { + yield return new MapMetadata( + Id: id, + Name: name, + XBlock: data.xblock.name.ToLower(), + Property: new MapMetadataProperty( + Continent: (Continent) data.property.continentCode, + Region: (MapRegion) data.property.regionCode, + Category: data.property.mapCategoryCode, + Type: (MapType) data.property.mapType, + BigCity: data.property.bigCity, + ExploreType: data.property.exploreType, + TutorialType: data.property.tutorialType, + RevivalReturnId: data.property.revivalreturnid, + EnterReturnId: data.property.enterreturnid, + AutoRevivalType: (AutoReviveType) data.property.autoRevivalType, + AutoRevivalTime: data.property.autoRevivalTime, + InfiniteMeretRevival: data.property.infinityMeratRevival, + NoRevivalHere: data.property.doNotRevivalHere, + ReviveFullHp: data.property.recoveryFullHP, + UseTimeEvent: data.property.useTimeEvent, + HomeReturnable: data.property.homeReturnable, + DeathPenalty: data.property.deathPenalty, + OnlyDarkTomb: data.property.onlyDarkTomb, + PkMode: data.property.pkMode, + CanFly: data.property.checkFly, + CanClimb: data.property.checkClimb, + IndoorType: data.indoor.type), + Limit: new MapMetadataLimit( + Capacity: data.property.capacity, + MinLevel: data.property.enterMinLevel, + MaxLevel: data.property.enterMaxLevel, + RequireQuest: data.property.requireQuest, + DisableSkills: data.property.skillUseDisable, + Climb: data.property.checkClimb, + Fly: data.property.checkFly, + Move: data.property.limitMove, + FallDamage: data.ui.fallDamage, + Dash: data.ui.useEPSkill, + Ride: data.ui.useRidee, + Pet: data.ui.usePet), + Drop: new MapMetadataDrop( + Level: data.drop.maplevel, + DropRank: data.drop.droprank, + GlobalDropBoxId: data.drop.globalDropBoxID), + Spawns: spawns.GetValueOrDefault(id, new List()), + CashCall: new MapMetadataCashCall( + TaxiDeparture: !data.cashCall.cashTaxiNotDeparture, + TaxiDestination: !data.cashCall.cashTaxiNotDestination, + Medic: !data.cashCall.cashCallMedicProhibit, + Market: !data.cashCall.cashCallMarketProhibit, + Recall: !data.cashCall.RecallOtherUserProhibit), + // TODO: There are also EntranceBuffs for Survival + EntranceBuffs: data.property.enteranceBuffIDs.Zip(data.property.enteranceBuffLevels, + (skillId, level) => new MapEntranceBuff(skillId, level)).ToArray()); + } + } +} diff --git a/Maple2.File.Ingest/Mapper/NXSMeshMapper.cs b/Maple2.File.Ingest/Mapper/NXSMeshMapper.cs index c5f3df016..b1c164dcf 100644 --- a/Maple2.File.Ingest/Mapper/NXSMeshMapper.cs +++ b/Maple2.File.Ingest/Mapper/NXSMeshMapper.cs @@ -1,12 +1,12 @@ -using Maple2.File.Ingest.Helpers; -using Maple2.Model.Metadata; - -namespace Maple2.File.Ingest.Mapper; - -public class NxsMeshMapper : TypeMapper { - protected override IEnumerable Map() { - foreach (NxsMeshMetadata mesh in NifParserHelper.nxsMeshes) { - yield return mesh; - } - } -} +using Maple2.File.Ingest.Helpers; +using Maple2.Model.Metadata; + +namespace Maple2.File.Ingest.Mapper; + +public class NxsMeshMapper : TypeMapper { + protected override IEnumerable Map() { + foreach (NxsMeshMetadata mesh in NifParserHelper.nxsMeshes) { + yield return mesh; + } + } +} diff --git a/Maple2.File.Ingest/Mapper/NavMeshMapper.cs b/Maple2.File.Ingest/Mapper/NavMeshMapper.cs index 281e1c478..5e88a7d4a 100644 --- a/Maple2.File.Ingest/Mapper/NavMeshMapper.cs +++ b/Maple2.File.Ingest/Mapper/NavMeshMapper.cs @@ -1,701 +1,701 @@ -using System.Diagnostics; -using System.Numerics; -using DotRecast.Core; -using DotRecast.Core.Numerics; -using DotRecast.Detour; -using DotRecast.Detour.Extras.Jumplink; -using DotRecast.Detour.Io; -using DotRecast.Recast; -using DotRecast.Recast.Geom; -using DotRecast.Recast.Toolset; -using DotRecast.Recast.Toolset.Builder; -using DotRecast.Recast.Toolset.Tools; -using Maple2.Database.Context; -using Maple2.File.Flat; -using Maple2.File.Flat.maplestory2library; -using Maple2.File.Flat.physxmodellibrary; -using Maple2.File.Flat.standardmodellibrary; -using Maple2.File.Ingest.Helpers; -using Maple2.File.Ingest.Utils; -using Maple2.File.IO; -using Maple2.File.IO.Nif; -using Maple2.File.Parser.Flat; -using Maple2.File.Parser.MapXBlock; -using Maple2.Tools; -using Maple2.Tools.DotRecast; -using Maple2.Tools.VectorMath; - -namespace Maple2.File.Ingest.Mapper; - -public class NavMeshMapper { - private readonly HashSet xBlocks; - private readonly XBlockParser mapParser; - private readonly HashSet upsidedownFaces = []; // make top faces of block that have another block on top of them non-walkable - - private readonly List fileLines = []; // used for debugging - - private readonly List vertexBuffer = [ - new Vector3(-0.75f, 0.75f, 0.0f), - new Vector3(-0.75f, -0.75f, 0.0f), - new Vector3(-0.75f, -0.75f, 1.5f), - new Vector3(-0.75f, 0.75f, 1.5f), - new Vector3(0.75f, -0.75f, 0.0f), - new Vector3(0.75f, -0.75f, 1.5f), - new Vector3(0.75f, 0.75f, 0.0f), - new Vector3(0.75f, 0.75f, 1.5f), - // bottom face - new Vector3(-0.75f, 0.75f, 0.75f), - new Vector3(-0.75f, -0.75f, 0.75f), - new Vector3(0.75f, -0.75f, 0.75f), - new Vector3(0.75f, 0.75f, 0.75f), - - ]; - - private readonly List indexBuffer = [ - 1, - 4, - 5, - 4, - 6, - 7, - 7, - 5, - 4, - 2, - 5, - 7, - 6, - 4, - 1, - 3, - 7, - 6, - 5, - 2, - 1, - 7, - 3, - 2, - 2, - 3, - 0, - 1, - 0, - 6, - 6, - 0, - 3, - 0, - 1, - 2, - // bottom face - 11, - 10, - 9, - 9, - 8, - 11, - ]; - - public NavMeshMapper(MetadataContext db, M2dReader exportedReader) { - xBlocks = db.MapMetadata.Select(metadata => metadata.XBlock).ToHashSet(); - mapParser = new XBlockParser(exportedReader, new FlatTypeIndex(exportedReader)); - - Directory.CreateDirectory(Paths.NAVMESH_DIR); - Directory.CreateDirectory(Paths.NAVMESH_HASH_DIR); - - Map(); - } - - private void Map() { - // string xblock = "63000016_cs"; - // string xblock = "02000001_tw_tria"; - // string xblock = "82000012_survival"; - // mapParser.ParseMap(xblock, (entities) => GenerateNavMesh(xblock, entities)); - // return; - - mapParser.Parse((xblock, entities) => { - if (!xBlocks.Contains(xblock)) { - return; - } - - GenerateNavMesh(xblock, entities); - }); - } - - private void GenerateNavMesh(string xblock, IEnumerable entities) { - if (NavmeshHash.HasValidHash(xblock)) { - Console.WriteLine($"Navmesh already exists for {xblock}"); - return; - } - - Stopwatch stopwatch = Stopwatch.StartNew(); - Console.WriteLine($"Parsing {xblock}..."); - - List verts = []; - List tris = []; - List areas = []; - foreach (IMapEntity entity in entities) { - // Only consider entities with 'doesMakeTOK: true' - if (entity is not IMS2PathEngineTOK { doesMakeTOK: true }) { - continue; - } - - NifDocument? document = null; - if (entity is IMesh mesh) { - document = GetMeshNifDocument(mesh); - } - - if (entity is not IPlaceable placeable) { - continue; - } - - Transform transform = new() { - Position = placeable.Position, - RotationAnglesDegrees = placeable.Rotation, - }; - - transform.Transformation *= DotRecastHelper.MapRotation; - - bool isFluid = false; - - if (entity is IPhysXWhitebox whitebox) { - GenerateCube(whitebox, transform, verts, tris, areas); - } else if (entity is IMS2MapProperties mapProperties) { - if (mapProperties.CubeType == "Fluid") { - isFluid = true; - } - GenerateCube(mapProperties, transform, verts, tris, areas); - } - - foreach (NiPhysXProp prop in document?.PhysXProps ?? []) { - if (prop.Snapshot == null) continue; - - foreach (NiPhysXActorDesc actor in prop.Snapshot.Actors) { - foreach (NiPhysXShapeDesc shape in actor.ShapeDescriptions) { - if (shape.Mesh == null) continue; - - PhysXMesh physXMesh = new PhysXMesh(shape.Mesh.MeshData); - - Matrix4x4 scale = Matrix4x4.CreateScale(prop.PhysXToWorldScale); - Matrix4x4 matrix = shape.LocalPose * actor.Poses[0] * scale * transform.Transformation; - - AddPhysxShape(verts, tris, areas, physXMesh, matrix, isFluid); - } - } - } - } - - if (verts.Count == 0 || tris.Count == 0) { - stopwatch.Stop(); - Console.WriteLine($"No mesh data found for {xblock} in {stopwatch.ElapsedMilliseconds}ms"); - return; - } - - // Used for debugging - // CreateObjFile(xblock); - - InputGeomProvider geomProvider = new InputGeomProvider(verts, tris); - - RcNavMeshBuildSettings settings = DotRecastHelper.NavMeshBuildSettings; - - RcConfig config = CreateRcConfig(settings, SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE); - - RcBuilderConfig bcfg = new RcBuilderConfig(config, geomProvider.GetMeshBoundsMin(), geomProvider.GetMeshBoundsMax()); - - try { - RcBuilder rcBuilder = new RcBuilder(); - RcContext ctx = new RcContext(); - - RcHeightfield solid = BuildSolidHeightfield(tris, areas, geomProvider, bcfg, ctx); - - RcBuilderResult results = rcBuilder.Build(ctx, bcfg.tileX, bcfg.tileZ, geomProvider, bcfg.cfg, solid, keepInterResults: true); - - if (results.SolidHeightfiled == null) { - return; - } - - RcJumpLinkBuilderTool jumpLinkBuilder = new(); - RcJumpLinkBuilderToolConfig jumpLinkBuilderConfig = new() { - buildOffMeshConnections = true, - buildTypes = JumpLinkType.EDGE_JUMP_BIT, - groundTolerance = 1.5f, - edgeJumpEndDistance = 1.5f, - edgeJumpHeight = 2f, - edgeJumpDownMaxHeight = 1f, - edgeJumpUpMaxHeight = 2f, - }; - - // jumpLinkBuilder.Build(geomProvider, settings, [results], jumpLinkBuilderConfig); - - DtMeshData? meshData = BuildMeshData(geomProvider, config.Cs, config.Ch, config.WalkableHeightWorld, config.WalkableRadiusWorld, config.WalkableClimbWorld, results); - if (meshData == null) { - return; - } - - DtNavMesh? navMesh = BuildNavMesh(meshData, DotRecastHelper.VERTS_PER_POLY); - if (navMesh == null) { - return; - } - - string navmeshFilePath = Path.Combine(Paths.NAVMESH_DIR, $"{xblock}.navmesh"); - - using FileStream fs = new FileStream(navmeshFilePath, FileMode.Create, FileAccess.Write); - using BinaryWriter bw = new BinaryWriter(fs); - - DtMeshSetWriter writer = new(); - writer.Write(bw, navMesh, RcByteOrder.LITTLE_ENDIAN, true); - bw.Close(); - fs.Close(); - - NavmeshHash.WriteHash(xblock); - - stopwatch.Stop(); - Console.WriteLine($"Generated navmesh for {xblock} in {stopwatch.ElapsedMilliseconds}ms"); - return; - } catch (Exception ex) { - stopwatch.Stop(); - Console.WriteLine($"Failed to generate navmesh for {xblock} due to {ex.Message}"); - return; - } - } - private NifDocument? GetMeshNifDocument(IMesh mesh) { - if (string.IsNullOrEmpty(mesh.NifAsset)) { - return null; - } - - if (!mesh.NifAsset.StartsWith("urn:llid")) { - Console.WriteLine($"Invalid asset: {mesh.NifAsset} for {mesh.ModelName}"); - return null; - } - - uint llid = Convert.ToUInt32(mesh.NifAsset.Substring(mesh.NifAsset.LastIndexOf(':') + 1, 8), 16); - if (!NifParserHelper.nifDocuments.TryGetValue(llid, out NifDocument? document)) { - Console.WriteLine($"Failed to find asset: {mesh.NifAsset} for {mesh.ModelName}"); - } - - return document; - } - - private static RcConfig CreateRcConfig(RcNavMeshBuildSettings settings, RcAreaModification walkableAreaMod) { - return new RcConfig( - partitionType: (RcPartition) settings.partitioning, - cellSize: settings.cellSize, - cellHeight: settings.cellHeight, - agentMaxSlope: settings.agentMaxSlope, - agentHeight: settings.agentHeight, - agentRadius: settings.agentRadius, - agentMaxClimb: settings.agentMaxClimb, - regionMinSize: settings.minRegionSize, - regionMergeSize: settings.mergedRegionSize, - edgeMaxLen: settings.edgeMaxLen, - edgeMaxError: settings.edgeMaxError, - vertsPerPoly: settings.vertsPerPoly, - detailSampleDist: settings.detailSampleDist, - detailSampleMaxError: settings.detailSampleMaxError, - filterLowHangingObstacles: settings.filterLowHangingObstacles, - filterLedgeSpans: settings.filterLedgeSpans, - filterWalkableLowHeightSpans: settings.filterWalkableLowHeightSpans, - walkableAreaMod: walkableAreaMod, - buildMeshDetail: true - ); - } - - private static RcHeightfield BuildSolidHeightfield(List tris, List areas, InputGeomProvider geomProvider, RcBuilderConfig bcfg, RcContext ctx) { - // Allocate voxel heightfield where we rasterize our input data to. - RcHeightfield solid = new RcHeightfield(bcfg.width, bcfg.height, bcfg.bmin, bcfg.bmax, bcfg.cfg.Cs, bcfg.cfg.Ch, bcfg.cfg.BorderSize); - - foreach (RcTriMesh geom in geomProvider.Meshes()) { - float[] vertices = geom.GetVerts(); - - int[] triangles = geom.GetTris(); - - int numTriangles = triangles.Length / 3; - int[] array = CalculateAreasFlags(tris, areas, bcfg.cfg, vertices, numTriangles); - - RcRasterizations.RasterizeTriangles(ctx, vertices, triangles, array, numTriangles, solid, bcfg.cfg.WalkableClimb); - } - - return solid; - } - - // Find triangles which are walkable based on their slope and rasterize them. - // Also check if the triangle is water and mark it as non-walkable. - private static int[] CalculateAreasFlags(List tris, List areas, RcConfig cfg, float[] verts2, int ntris) { - int[] array = areas.ToArray(); - float num = MathF.Cos(cfg.WalkableSlopeAngle / 180f * MathF.PI); - RcVec3f norm = default; - for (int i = 0; i < ntris; i++) { - // Skip water triangles. - if ((array[i] & SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER) != 0) { - array[i] = 0; - continue; - } - - int num2 = i * 3; - RcRecast.CalcTriNormal(verts2, tris[num2], tris[num2 + 1], tris[num2 + 2], ref norm); - if (norm.Y > num) { - array[i] = cfg.WalkableAreaMod.Apply(array[i]); - } - } - - return array; - } - - private static DtMeshData? BuildMeshData(InputGeomProvider geom, float cellSize, float cellHeight, float agentHeight, - float agentRadius, float agentMaxClimb, RcBuilderResult result) { - int x = result.TileX; - int z = result.TileZ; - RcPolyMesh pmesh = result.Mesh; - RcPolyMeshDetail dmesh = result.MeshDetail; - DtNavMeshCreateParams option = new(); - for (int i = 0; i < pmesh.npolys; ++i) { - pmesh.flags[i] = 1; - } - - option.verts = pmesh.verts; - option.vertCount = pmesh.nverts; - option.polys = pmesh.polys; - option.polyAreas = pmesh.areas; - option.polyFlags = pmesh.flags; - option.polyCount = pmesh.npolys; - option.nvp = pmesh.nvp; - if (dmesh != null) { - option.detailMeshes = dmesh.meshes; - option.detailVerts = dmesh.verts; - option.detailVertsCount = dmesh.nverts; - option.detailTris = dmesh.tris; - option.detailTriCount = dmesh.ntris; - } - - option.walkableHeight = agentHeight; - option.walkableRadius = agentRadius; - option.walkableClimb = agentMaxClimb; - option.bmin = pmesh.bmin; - option.bmax = pmesh.bmax; - option.cs = cellSize; - option.ch = cellHeight; - option.buildBvTree = true; - - List offMeshConnections = geom.GetOffMeshConnections(); - option.offMeshConCount = offMeshConnections.Count; - option.offMeshConVerts = new float[option.offMeshConCount * 6]; - option.offMeshConRad = new float[option.offMeshConCount]; - option.offMeshConDir = new int[option.offMeshConCount]; - option.offMeshConAreas = new int[option.offMeshConCount]; - option.offMeshConFlags = new int[option.offMeshConCount]; - option.offMeshConUserID = new int[option.offMeshConCount]; - for (int i = 0; i < option.offMeshConCount; i++) { - RcOffMeshConnection offMeshCon = offMeshConnections[i]; - for (int j = 0; j < 6; j++) { - option.offMeshConVerts[6 * i + j] = offMeshCon.verts[j]; - } - - option.offMeshConRad[i] = offMeshCon.radius; - option.offMeshConDir[i] = offMeshCon.bidir ? 1 : 0; - option.offMeshConAreas[i] = offMeshCon.area; - option.offMeshConFlags[i] = offMeshCon.flags; - } - - option.tileX = x; - option.tileZ = z; - DtMeshData? dtMeshData = DtNavMeshBuilder.CreateNavMeshData(option); - if (dtMeshData != null) { - return DemoNavMeshBuilder.UpdateAreaAndFlags(dtMeshData); - } - - return null; - } - - private static DtNavMesh? BuildNavMesh(DtMeshData meshData, int vertsPerPoly) { - DtNavMesh navMesh = new(); - DtStatus status = navMesh.Init(meshData, vertsPerPoly, 0); - if (status.Failed()) { - return null; - } - return navMesh; - } - - public static DtMeshData UpdateAreaAndFlags(DtMeshData meshData) { - // Update poly flags from areas. - for (int i = 0; i < meshData.polys.Length; ++i) { - int area = meshData.polys[i].GetArea(); - if (area is SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WALKABLE) { - meshData.polys[i].SetArea(SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GROUND); - } - - if (area is SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GROUND - or SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GRASS - or SampleAreaModifications.SAMPLE_POLYAREA_TYPE_ROAD) { - meshData.polys[i].flags = SampleAreaModifications.SAMPLE_POLYFLAGS_WALK; - } else if (area is SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER) { - meshData.polys[i].flags = SampleAreaModifications.SAMPLE_POLYFLAGS_SWIM; - } else if (area is SampleAreaModifications.SAMPLE_POLYAREA_TYPE_DOOR) { - meshData.polys[i].flags = SampleAreaModifications.SAMPLE_POLYFLAGS_DOOR; - } - } - - return meshData; - } - - private void GenerateCube(IMS2MapProperties mapProperties, Transform transform, List verts, List tris, List areas) { - if (!mapProperties.GeneratePhysX) { - return; - } - - Vector3 generatePhysXDimension = mapProperties.GeneratePhysXDimension * (1 / 1.5f); - if (generatePhysXDimension == Vector3.Zero) { - generatePhysXDimension = new Vector3(100f, 100f, 100f); - } - - GenerateCube(generatePhysXDimension, Vector3.Zero, transform, verts, tris, areas); - } - - private void GenerateCube(IPhysXWhitebox physXWhitebox, Transform transform, List verts, List tris, List areas) { - Vector3 offset = new Vector3(0, 0, -0.5f * physXWhitebox.ShapeDimensions.Z); - GenerateCube(physXWhitebox.ShapeDimensions, offset, transform, verts, tris, areas); - } - - private void GenerateCube(Vector3 size, Vector3 offset, Transform transform, List verts, List tris, List areas) { - Matrix4x4 matrix = Matrix4x4.CreateScale(size) * Matrix4x4.CreateTranslation(offset) * transform.Transformation; - - int currentVerticeCount = verts.Count / 3; - - foreach (Vector3 vertex in vertexBuffer) { - Vector3 transformed = Vector3.Transform(vertex, matrix); - verts.AddRange([transformed.X, transformed.Y, transformed.Z]); - fileLines.Add($"v {transformed.X} {transformed.Y} {transformed.Z}"); - } - - for (int i = 0; i < indexBuffer.Count; i += 3) { - tris.AddRange([indexBuffer[i] + currentVerticeCount, indexBuffer[i + 1] + currentVerticeCount, indexBuffer[i + 2] + currentVerticeCount]); - fileLines.Add($"f {indexBuffer[i] + 1 + currentVerticeCount} {indexBuffer[i + 1] + 1 + currentVerticeCount} {indexBuffer[i + 2] + 1 + currentVerticeCount}"); - areas.Add(0); - } - } - - private void AddPhysxShape(List verts, List tris, List areas, PhysXMesh physXMesh, Matrix4x4 matrix, bool isFluid) { - int currentVerticeCount = verts.Count / 3; - - List vertexBuffer2 = []; - - List indexBuffer2 = []; - - Vector3 offset = new Vector3(0, 0.125f, 0.0f); - - foreach (Vector3 vertex in physXMesh.Vertices) { - Vector3 transformed = Vector3.Transform(vertex, matrix); - verts.AddRange([transformed.X, transformed.Y, transformed.Z]); - fileLines.Add($"v {transformed.X} {transformed.Y} {transformed.Z}"); - } - - foreach (PhysXMeshFace face in physXMesh.Faces) { - tris.AddRange([(int) face.Vert0 + currentVerticeCount, (int) face.Vert1 + currentVerticeCount, (int) face.Vert2 + currentVerticeCount]); - fileLines.Add($"f {face.Vert0 + 1 + currentVerticeCount} {face.Vert1 + 1 + currentVerticeCount} {face.Vert2 + 1 + currentVerticeCount}"); - - if (isFluid) { - areas.Add(SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER); - } else { - areas.Add(0); - } - - Vector3 vert0 = Vector3.Transform(physXMesh.Vertices[(int) face.Vert0], matrix); - Vector3 vert1 = Vector3.Transform(physXMesh.Vertices[(int) face.Vert1], matrix); - Vector3 vert2 = Vector3.Transform(physXMesh.Vertices[(int) face.Vert2], matrix); - - Vector3 normal = Vector3.Cross(vert1 - vert0, vert2 - vert0); - normal = Vector3.Normalize(normal); - - if (normal.Y >= -Math.Cos(Math.PI / 2)) { - continue; - } - - int faceStart = vertexBuffer2.Count + (verts.Count / 3); - upsidedownFaces.Add(faceStart); - - indexBuffer2.AddRange([faceStart, faceStart + 1, faceStart + 2]); - vertexBuffer2.AddRange([vert0 + offset, vert1 + offset, vert2 + offset]); - } - - foreach (Vector3 vertex in vertexBuffer2) { - verts.AddRange([vertex.X, vertex.Y, vertex.Z]); - fileLines.Add($"v {vertex.X} {vertex.Y} {vertex.Z}"); - } - - for (int i = 0; i < indexBuffer2.Count; i += 3) { - tris.AddRange([indexBuffer2[i], indexBuffer2[i + 1], indexBuffer2[i + 2]]); - fileLines.Add($"f {indexBuffer2[i] + 1} {indexBuffer2[i + 1] + 1} {indexBuffer2[i + 2] + 1}"); - if (isFluid) { - areas.Add(SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER); - } else { - areas.Add(0); - } - } - } - - // used for debugging - private void CreateObjFile(string xblock) { - // create a new file if it doesn't exist - if (System.IO.File.Exists($"{xblock}.obj")) { - System.IO.File.Delete($"{xblock}.obj"); - } - - var file = System.IO.File.Create($"{xblock}.obj"); - using StreamWriter streamWriter = new StreamWriter(file); - foreach (string line in fileLines) { - streamWriter.WriteLine(line); - } - streamWriter.Close(); - fileLines.Clear(); - } - - #region TileMesh configuration - // tiling configuration - // var config = new RcConfig( - // useTiles: true, - // tileSizeX: TileSize, - // tileSizeZ: TileSize, - // borderSize: RcConfig.CalcBorder(0.3f, CellSize), - // partition: RcPartition.WATERSHED, - // cellSize: CellSize, - // cellHeight: CellSize, - // agentMaxSlope: 47f, // generally 45 degrees, but we add a bit more to account for floating point errors - // agentMaxClimb: 0.7f, - // agentHeight: 1.4f, // approximation of character height - // agentRadius: 0.3f, // approximation of character radius - // minRegionArea: 8 * 8 * CellSize * CellSize, - // mergeRegionArea: 20 * 20 * CellSize * CellSize, - // edgeMaxLen: 12.0f, - // edgeMaxError: 1.3f, - // vertsPerPoly: VertsPerPoly, - // detailSampleDist: 6.0f, - // detailSampleMaxError: 1.0f, - // filterLowHangingObstacles: true, - // filterLedgeSpans: true, - // filterWalkableLowHeightSpans: true, - // walkableAreaMod: new RcAreaModification(0x3f), - // buildMeshDetail: true - // ); - - // try { - // RcBuilder rcBuilder = new(); - // List results = rcBuilder.BuildTiles(geomProvider, config, true, true, Environment.ProcessorCount + 1, Task.Factory); - - // List tileMeshData = BuildMeshData(geomProvider, config.Cs, config.Ch, config.WalkableHeightWorld, config.WalkableRadiusWorld, config.WalkableClimbWorld, results); - // DtNavMesh tileNavMesh = BuildNavMesh(geomProvider, tileMeshData, config.Cs, TileSize, VertsPerPoly); - - // string navmeshFilePath = $"navmeshes/{xblock}.navmesh"; - - // using var fs = new FileStream(navmeshFilePath, FileMode.Create, FileAccess.Write); - // using var bw = new BinaryWriter(fs); - - // DtMeshSetWriter writer = new(); - // writer.Write(bw, tileNavMesh, RcByteOrder.LITTLE_ENDIAN, true); - // } catch (Exception ex) { - // Console.WriteLine($"Failed to generate navmesh for {xblock} due to {ex.Message}"); - // return; - // } - // public static List BuildMeshData(IInputGeomProvider geom, float cellSize, float cellHeight, float agentHeight, - // float agentRadius, float agentMaxClimb, IList results) { - // List meshData = []; - // foreach (RcBuilderResult result in results) { - // int x = result.TileX; - // int z = result.TileZ; - // RcPolyMesh pmesh = result.Mesh; - // RcPolyMeshDetail dmesh = result.MeshDetail; - // DtNavMeshCreateParams option = new(); - // for (int i = 0; i < pmesh.npolys; ++i) { - // pmesh.flags[i] = 1; - // } - - // option.verts = pmesh.verts; - // option.vertCount = pmesh.nverts; - // option.polys = pmesh.polys; - // option.polyAreas = pmesh.areas; - // option.polyFlags = pmesh.flags; - // option.polyCount = pmesh.npolys; - // option.nvp = pmesh.nvp; - // if (dmesh != null) { - // option.detailMeshes = dmesh.meshes; - // option.detailVerts = dmesh.verts; - // option.detailVertsCount = dmesh.nverts; - // option.detailTris = dmesh.tris; - // option.detailTriCount = dmesh.ntris; - // } - - // option.walkableHeight = agentHeight; - // option.walkableRadius = agentRadius; - // option.walkableClimb = agentMaxClimb; - // option.bmin = pmesh.bmin; - // option.bmax = pmesh.bmax; - // option.cs = cellSize; - // option.ch = cellHeight; - // option.buildBvTree = true; - - // // TODO: Off-mesh connections - // // var offMeshConnections = geom.GetOffMeshConnections(); - // // option.offMeshConCount = offMeshConnections.Count; - // // option.offMeshConVerts = new float[option.offMeshConCount * 6]; - // // option.offMeshConRad = new float[option.offMeshConCount]; - // // option.offMeshConDir = new int[option.offMeshConCount]; - // // option.offMeshConAreas = new int[option.offMeshConCount]; - // // option.offMeshConFlags = new int[option.offMeshConCount]; - // // option.offMeshConUserID = new int[option.offMeshConCount]; - // // for (int i = 0; i < option.offMeshConCount; i++) { - // // RcOffMeshConnection offMeshCon = offMeshConnections[i]; - // // for (int j = 0; j < 6; j++) { - // // option.offMeshConVerts[6 * i + j] = offMeshCon.verts[j]; - // // } - - // // option.offMeshConRad[i] = offMeshCon.radius; - // // option.offMeshConDir[i] = offMeshCon.bidir ? 1 : 0; - // // option.offMeshConAreas[i] = offMeshCon.area; - // // option.offMeshConFlags[i] = offMeshCon.flags; - // // // option.offMeshConUserID[i] = offMeshCon.userId; - // // } - - // option.tileX = x; - // option.tileZ = z; - // var dtMeshData = DtNavMeshBuilder.CreateNavMeshData(option); - // if (dtMeshData != null) { - // meshData.Add(DemoNavMeshBuilder.UpdateAreaAndFlags(dtMeshData)); - // } - // } - - // return meshData; - // } - - // public static DtNavMesh BuildNavMesh(IInputGeomProvider geom, List meshData, float cellSize, int tileSize, int vertsPerPoly) { - // DtNavMeshParams navMeshParams = new() { - // orig = geom.GetMeshBoundsMin(), - // tileWidth = tileSize * cellSize, - // tileHeight = tileSize * cellSize, - - // maxTiles = GetMaxTiles(geom, cellSize, tileSize), - // maxPolys = GetMaxPolysPerTile(geom, cellSize, tileSize) - // }; - - // DtNavMesh navMesh = new(); - // navMesh.Init(navMeshParams, vertsPerPoly); - // meshData.ForEach(md => navMesh.AddTile(md, 0, 0, out long _)); - // return navMesh; - // } - // public static int GetMaxTiles(IInputGeomProvider geom, float cellSize, int tileSize) { - // int tileBits = GetTileBits(geom, cellSize, tileSize); - // return 1 << tileBits; - // } - - // public static int GetMaxPolysPerTile(IInputGeomProvider geom, float cellSize, int tileSize) { - // int polyBits = 22 - GetTileBits(geom, cellSize, tileSize); - // return 1 << polyBits; - // } - - // private static int GetTileBits(IInputGeomProvider geom, float cellSize, int tileSize) { - // RcRecast.CalcGridSize(geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), cellSize, out int gw, out int gh); - // int tw = (gw + tileSize - 1) / tileSize; - // int th = (gh + tileSize - 1) / tileSize; - // int tileBits = Math.Min(DtUtils.Ilog2(DtUtils.NextPow2(tw * th)), 14); - // return tileBits; - // } - #endregion -} +using System.Diagnostics; +using System.Numerics; +using DotRecast.Core; +using DotRecast.Core.Numerics; +using DotRecast.Detour; +using DotRecast.Detour.Extras.Jumplink; +using DotRecast.Detour.Io; +using DotRecast.Recast; +using DotRecast.Recast.Geom; +using DotRecast.Recast.Toolset; +using DotRecast.Recast.Toolset.Builder; +using DotRecast.Recast.Toolset.Tools; +using Maple2.Database.Context; +using Maple2.File.Flat; +using Maple2.File.Flat.maplestory2library; +using Maple2.File.Flat.physxmodellibrary; +using Maple2.File.Flat.standardmodellibrary; +using Maple2.File.Ingest.Helpers; +using Maple2.File.Ingest.Utils; +using Maple2.File.IO; +using Maple2.File.IO.Nif; +using Maple2.File.Parser.Flat; +using Maple2.File.Parser.MapXBlock; +using Maple2.Tools; +using Maple2.Tools.DotRecast; +using Maple2.Tools.VectorMath; + +namespace Maple2.File.Ingest.Mapper; + +public class NavMeshMapper { + private readonly HashSet xBlocks; + private readonly XBlockParser mapParser; + private readonly HashSet upsidedownFaces = []; // make top faces of block that have another block on top of them non-walkable + + private readonly List fileLines = []; // used for debugging + + private readonly List vertexBuffer = [ + new Vector3(-0.75f, 0.75f, 0.0f), + new Vector3(-0.75f, -0.75f, 0.0f), + new Vector3(-0.75f, -0.75f, 1.5f), + new Vector3(-0.75f, 0.75f, 1.5f), + new Vector3(0.75f, -0.75f, 0.0f), + new Vector3(0.75f, -0.75f, 1.5f), + new Vector3(0.75f, 0.75f, 0.0f), + new Vector3(0.75f, 0.75f, 1.5f), + // bottom face + new Vector3(-0.75f, 0.75f, 0.75f), + new Vector3(-0.75f, -0.75f, 0.75f), + new Vector3(0.75f, -0.75f, 0.75f), + new Vector3(0.75f, 0.75f, 0.75f), + + ]; + + private readonly List indexBuffer = [ + 1, + 4, + 5, + 4, + 6, + 7, + 7, + 5, + 4, + 2, + 5, + 7, + 6, + 4, + 1, + 3, + 7, + 6, + 5, + 2, + 1, + 7, + 3, + 2, + 2, + 3, + 0, + 1, + 0, + 6, + 6, + 0, + 3, + 0, + 1, + 2, + // bottom face + 11, + 10, + 9, + 9, + 8, + 11, + ]; + + public NavMeshMapper(MetadataContext db, M2dReader exportedReader) { + xBlocks = db.MapMetadata.Select(metadata => metadata.XBlock).ToHashSet(); + mapParser = new XBlockParser(exportedReader, new FlatTypeIndex(exportedReader)); + + Directory.CreateDirectory(Paths.NAVMESH_DIR); + Directory.CreateDirectory(Paths.NAVMESH_HASH_DIR); + + Map(); + } + + private void Map() { + // string xblock = "63000016_cs"; + // string xblock = "02000001_tw_tria"; + // string xblock = "82000012_survival"; + // mapParser.ParseMap(xblock, (entities) => GenerateNavMesh(xblock, entities)); + // return; + + mapParser.Parse((xblock, entities) => { + if (!xBlocks.Contains(xblock)) { + return; + } + + GenerateNavMesh(xblock, entities); + }); + } + + private void GenerateNavMesh(string xblock, IEnumerable entities) { + if (NavmeshHash.HasValidHash(xblock)) { + Console.WriteLine($"Navmesh already exists for {xblock}"); + return; + } + + Stopwatch stopwatch = Stopwatch.StartNew(); + Console.WriteLine($"Parsing {xblock}..."); + + List verts = []; + List tris = []; + List areas = []; + foreach (IMapEntity entity in entities) { + // Only consider entities with 'doesMakeTOK: true' + if (entity is not IMS2PathEngineTOK { doesMakeTOK: true }) { + continue; + } + + NifDocument? document = null; + if (entity is IMesh mesh) { + document = GetMeshNifDocument(mesh); + } + + if (entity is not IPlaceable placeable) { + continue; + } + + Transform transform = new() { + Position = placeable.Position, + RotationAnglesDegrees = placeable.Rotation, + }; + + transform.Transformation *= DotRecastHelper.MapRotation; + + bool isFluid = false; + + if (entity is IPhysXWhitebox whitebox) { + GenerateCube(whitebox, transform, verts, tris, areas); + } else if (entity is IMS2MapProperties mapProperties) { + if (mapProperties.CubeType == "Fluid") { + isFluid = true; + } + GenerateCube(mapProperties, transform, verts, tris, areas); + } + + foreach (NiPhysXProp prop in document?.PhysXProps ?? []) { + if (prop.Snapshot == null) continue; + + foreach (NiPhysXActorDesc actor in prop.Snapshot.Actors) { + foreach (NiPhysXShapeDesc shape in actor.ShapeDescriptions) { + if (shape.Mesh == null) continue; + + PhysXMesh physXMesh = new PhysXMesh(shape.Mesh.MeshData); + + Matrix4x4 scale = Matrix4x4.CreateScale(prop.PhysXToWorldScale); + Matrix4x4 matrix = shape.LocalPose * actor.Poses[0] * scale * transform.Transformation; + + AddPhysxShape(verts, tris, areas, physXMesh, matrix, isFluid); + } + } + } + } + + if (verts.Count == 0 || tris.Count == 0) { + stopwatch.Stop(); + Console.WriteLine($"No mesh data found for {xblock} in {stopwatch.ElapsedMilliseconds}ms"); + return; + } + + // Used for debugging + // CreateObjFile(xblock); + + InputGeomProvider geomProvider = new InputGeomProvider(verts, tris); + + RcNavMeshBuildSettings settings = DotRecastHelper.NavMeshBuildSettings; + + RcConfig config = CreateRcConfig(settings, SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE); + + RcBuilderConfig bcfg = new RcBuilderConfig(config, geomProvider.GetMeshBoundsMin(), geomProvider.GetMeshBoundsMax()); + + try { + RcBuilder rcBuilder = new RcBuilder(); + RcContext ctx = new RcContext(); + + RcHeightfield solid = BuildSolidHeightfield(tris, areas, geomProvider, bcfg, ctx); + + RcBuilderResult results = rcBuilder.Build(ctx, bcfg.tileX, bcfg.tileZ, geomProvider, bcfg.cfg, solid, keepInterResults: true); + + if (results.SolidHeightfiled == null) { + return; + } + + RcJumpLinkBuilderTool jumpLinkBuilder = new(); + RcJumpLinkBuilderToolConfig jumpLinkBuilderConfig = new() { + buildOffMeshConnections = true, + buildTypes = JumpLinkType.EDGE_JUMP_BIT, + groundTolerance = 1.5f, + edgeJumpEndDistance = 1.5f, + edgeJumpHeight = 2f, + edgeJumpDownMaxHeight = 1f, + edgeJumpUpMaxHeight = 2f, + }; + + // jumpLinkBuilder.Build(geomProvider, settings, [results], jumpLinkBuilderConfig); + + DtMeshData? meshData = BuildMeshData(geomProvider, config.Cs, config.Ch, config.WalkableHeightWorld, config.WalkableRadiusWorld, config.WalkableClimbWorld, results); + if (meshData == null) { + return; + } + + DtNavMesh? navMesh = BuildNavMesh(meshData, DotRecastHelper.VERTS_PER_POLY); + if (navMesh == null) { + return; + } + + string navmeshFilePath = Path.Combine(Paths.NAVMESH_DIR, $"{xblock}.navmesh"); + + using FileStream fs = new FileStream(navmeshFilePath, FileMode.Create, FileAccess.Write); + using BinaryWriter bw = new BinaryWriter(fs); + + DtMeshSetWriter writer = new(); + writer.Write(bw, navMesh, RcByteOrder.LITTLE_ENDIAN, true); + bw.Close(); + fs.Close(); + + NavmeshHash.WriteHash(xblock); + + stopwatch.Stop(); + Console.WriteLine($"Generated navmesh for {xblock} in {stopwatch.ElapsedMilliseconds}ms"); + return; + } catch (Exception ex) { + stopwatch.Stop(); + Console.WriteLine($"Failed to generate navmesh for {xblock} due to {ex.Message}"); + return; + } + } + private NifDocument? GetMeshNifDocument(IMesh mesh) { + if (string.IsNullOrEmpty(mesh.NifAsset)) { + return null; + } + + if (!mesh.NifAsset.StartsWith("urn:llid")) { + Console.WriteLine($"Invalid asset: {mesh.NifAsset} for {mesh.ModelName}"); + return null; + } + + uint llid = Convert.ToUInt32(mesh.NifAsset.Substring(mesh.NifAsset.LastIndexOf(':') + 1, 8), 16); + if (!NifParserHelper.nifDocuments.TryGetValue(llid, out NifDocument? document)) { + Console.WriteLine($"Failed to find asset: {mesh.NifAsset} for {mesh.ModelName}"); + } + + return document; + } + + private static RcConfig CreateRcConfig(RcNavMeshBuildSettings settings, RcAreaModification walkableAreaMod) { + return new RcConfig( + partitionType: (RcPartition) settings.partitioning, + cellSize: settings.cellSize, + cellHeight: settings.cellHeight, + agentMaxSlope: settings.agentMaxSlope, + agentHeight: settings.agentHeight, + agentRadius: settings.agentRadius, + agentMaxClimb: settings.agentMaxClimb, + regionMinSize: settings.minRegionSize, + regionMergeSize: settings.mergedRegionSize, + edgeMaxLen: settings.edgeMaxLen, + edgeMaxError: settings.edgeMaxError, + vertsPerPoly: settings.vertsPerPoly, + detailSampleDist: settings.detailSampleDist, + detailSampleMaxError: settings.detailSampleMaxError, + filterLowHangingObstacles: settings.filterLowHangingObstacles, + filterLedgeSpans: settings.filterLedgeSpans, + filterWalkableLowHeightSpans: settings.filterWalkableLowHeightSpans, + walkableAreaMod: walkableAreaMod, + buildMeshDetail: true + ); + } + + private static RcHeightfield BuildSolidHeightfield(List tris, List areas, InputGeomProvider geomProvider, RcBuilderConfig bcfg, RcContext ctx) { + // Allocate voxel heightfield where we rasterize our input data to. + RcHeightfield solid = new RcHeightfield(bcfg.width, bcfg.height, bcfg.bmin, bcfg.bmax, bcfg.cfg.Cs, bcfg.cfg.Ch, bcfg.cfg.BorderSize); + + foreach (RcTriMesh geom in geomProvider.Meshes()) { + float[] vertices = geom.GetVerts(); + + int[] triangles = geom.GetTris(); + + int numTriangles = triangles.Length / 3; + int[] array = CalculateAreasFlags(tris, areas, bcfg.cfg, vertices, numTriangles); + + RcRasterizations.RasterizeTriangles(ctx, vertices, triangles, array, numTriangles, solid, bcfg.cfg.WalkableClimb); + } + + return solid; + } + + // Find triangles which are walkable based on their slope and rasterize them. + // Also check if the triangle is water and mark it as non-walkable. + private static int[] CalculateAreasFlags(List tris, List areas, RcConfig cfg, float[] verts2, int ntris) { + int[] array = areas.ToArray(); + float num = MathF.Cos(cfg.WalkableSlopeAngle / 180f * MathF.PI); + RcVec3f norm = default; + for (int i = 0; i < ntris; i++) { + // Skip water triangles. + if ((array[i] & SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER) != 0) { + array[i] = 0; + continue; + } + + int num2 = i * 3; + RcRecast.CalcTriNormal(verts2, tris[num2], tris[num2 + 1], tris[num2 + 2], ref norm); + if (norm.Y > num) { + array[i] = cfg.WalkableAreaMod.Apply(array[i]); + } + } + + return array; + } + + private static DtMeshData? BuildMeshData(InputGeomProvider geom, float cellSize, float cellHeight, float agentHeight, + float agentRadius, float agentMaxClimb, RcBuilderResult result) { + int x = result.TileX; + int z = result.TileZ; + RcPolyMesh pmesh = result.Mesh; + RcPolyMeshDetail dmesh = result.MeshDetail; + DtNavMeshCreateParams option = new(); + for (int i = 0; i < pmesh.npolys; ++i) { + pmesh.flags[i] = 1; + } + + option.verts = pmesh.verts; + option.vertCount = pmesh.nverts; + option.polys = pmesh.polys; + option.polyAreas = pmesh.areas; + option.polyFlags = pmesh.flags; + option.polyCount = pmesh.npolys; + option.nvp = pmesh.nvp; + if (dmesh != null) { + option.detailMeshes = dmesh.meshes; + option.detailVerts = dmesh.verts; + option.detailVertsCount = dmesh.nverts; + option.detailTris = dmesh.tris; + option.detailTriCount = dmesh.ntris; + } + + option.walkableHeight = agentHeight; + option.walkableRadius = agentRadius; + option.walkableClimb = agentMaxClimb; + option.bmin = pmesh.bmin; + option.bmax = pmesh.bmax; + option.cs = cellSize; + option.ch = cellHeight; + option.buildBvTree = true; + + List offMeshConnections = geom.GetOffMeshConnections(); + option.offMeshConCount = offMeshConnections.Count; + option.offMeshConVerts = new float[option.offMeshConCount * 6]; + option.offMeshConRad = new float[option.offMeshConCount]; + option.offMeshConDir = new int[option.offMeshConCount]; + option.offMeshConAreas = new int[option.offMeshConCount]; + option.offMeshConFlags = new int[option.offMeshConCount]; + option.offMeshConUserID = new int[option.offMeshConCount]; + for (int i = 0; i < option.offMeshConCount; i++) { + RcOffMeshConnection offMeshCon = offMeshConnections[i]; + for (int j = 0; j < 6; j++) { + option.offMeshConVerts[6 * i + j] = offMeshCon.verts[j]; + } + + option.offMeshConRad[i] = offMeshCon.radius; + option.offMeshConDir[i] = offMeshCon.bidir ? 1 : 0; + option.offMeshConAreas[i] = offMeshCon.area; + option.offMeshConFlags[i] = offMeshCon.flags; + } + + option.tileX = x; + option.tileZ = z; + DtMeshData? dtMeshData = DtNavMeshBuilder.CreateNavMeshData(option); + if (dtMeshData != null) { + return DemoNavMeshBuilder.UpdateAreaAndFlags(dtMeshData); + } + + return null; + } + + private static DtNavMesh? BuildNavMesh(DtMeshData meshData, int vertsPerPoly) { + DtNavMesh navMesh = new(); + DtStatus status = navMesh.Init(meshData, vertsPerPoly, 0); + if (status.Failed()) { + return null; + } + return navMesh; + } + + public static DtMeshData UpdateAreaAndFlags(DtMeshData meshData) { + // Update poly flags from areas. + for (int i = 0; i < meshData.polys.Length; ++i) { + int area = meshData.polys[i].GetArea(); + if (area is SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WALKABLE) { + meshData.polys[i].SetArea(SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GROUND); + } + + if (area is SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GROUND + or SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GRASS + or SampleAreaModifications.SAMPLE_POLYAREA_TYPE_ROAD) { + meshData.polys[i].flags = SampleAreaModifications.SAMPLE_POLYFLAGS_WALK; + } else if (area is SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER) { + meshData.polys[i].flags = SampleAreaModifications.SAMPLE_POLYFLAGS_SWIM; + } else if (area is SampleAreaModifications.SAMPLE_POLYAREA_TYPE_DOOR) { + meshData.polys[i].flags = SampleAreaModifications.SAMPLE_POLYFLAGS_DOOR; + } + } + + return meshData; + } + + private void GenerateCube(IMS2MapProperties mapProperties, Transform transform, List verts, List tris, List areas) { + if (!mapProperties.GeneratePhysX) { + return; + } + + Vector3 generatePhysXDimension = mapProperties.GeneratePhysXDimension * (1 / 1.5f); + if (generatePhysXDimension == Vector3.Zero) { + generatePhysXDimension = new Vector3(100f, 100f, 100f); + } + + GenerateCube(generatePhysXDimension, Vector3.Zero, transform, verts, tris, areas); + } + + private void GenerateCube(IPhysXWhitebox physXWhitebox, Transform transform, List verts, List tris, List areas) { + Vector3 offset = new Vector3(0, 0, -0.5f * physXWhitebox.ShapeDimensions.Z); + GenerateCube(physXWhitebox.ShapeDimensions, offset, transform, verts, tris, areas); + } + + private void GenerateCube(Vector3 size, Vector3 offset, Transform transform, List verts, List tris, List areas) { + Matrix4x4 matrix = Matrix4x4.CreateScale(size) * Matrix4x4.CreateTranslation(offset) * transform.Transformation; + + int currentVerticeCount = verts.Count / 3; + + foreach (Vector3 vertex in vertexBuffer) { + Vector3 transformed = Vector3.Transform(vertex, matrix); + verts.AddRange([transformed.X, transformed.Y, transformed.Z]); + fileLines.Add($"v {transformed.X} {transformed.Y} {transformed.Z}"); + } + + for (int i = 0; i < indexBuffer.Count; i += 3) { + tris.AddRange([indexBuffer[i] + currentVerticeCount, indexBuffer[i + 1] + currentVerticeCount, indexBuffer[i + 2] + currentVerticeCount]); + fileLines.Add($"f {indexBuffer[i] + 1 + currentVerticeCount} {indexBuffer[i + 1] + 1 + currentVerticeCount} {indexBuffer[i + 2] + 1 + currentVerticeCount}"); + areas.Add(0); + } + } + + private void AddPhysxShape(List verts, List tris, List areas, PhysXMesh physXMesh, Matrix4x4 matrix, bool isFluid) { + int currentVerticeCount = verts.Count / 3; + + List vertexBuffer2 = []; + + List indexBuffer2 = []; + + Vector3 offset = new Vector3(0, 0.125f, 0.0f); + + foreach (Vector3 vertex in physXMesh.Vertices) { + Vector3 transformed = Vector3.Transform(vertex, matrix); + verts.AddRange([transformed.X, transformed.Y, transformed.Z]); + fileLines.Add($"v {transformed.X} {transformed.Y} {transformed.Z}"); + } + + foreach (PhysXMeshFace face in physXMesh.Faces) { + tris.AddRange([(int) face.Vert0 + currentVerticeCount, (int) face.Vert1 + currentVerticeCount, (int) face.Vert2 + currentVerticeCount]); + fileLines.Add($"f {face.Vert0 + 1 + currentVerticeCount} {face.Vert1 + 1 + currentVerticeCount} {face.Vert2 + 1 + currentVerticeCount}"); + + if (isFluid) { + areas.Add(SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER); + } else { + areas.Add(0); + } + + Vector3 vert0 = Vector3.Transform(physXMesh.Vertices[(int) face.Vert0], matrix); + Vector3 vert1 = Vector3.Transform(physXMesh.Vertices[(int) face.Vert1], matrix); + Vector3 vert2 = Vector3.Transform(physXMesh.Vertices[(int) face.Vert2], matrix); + + Vector3 normal = Vector3.Cross(vert1 - vert0, vert2 - vert0); + normal = Vector3.Normalize(normal); + + if (normal.Y >= -Math.Cos(Math.PI / 2)) { + continue; + } + + int faceStart = vertexBuffer2.Count + (verts.Count / 3); + upsidedownFaces.Add(faceStart); + + indexBuffer2.AddRange([faceStart, faceStart + 1, faceStart + 2]); + vertexBuffer2.AddRange([vert0 + offset, vert1 + offset, vert2 + offset]); + } + + foreach (Vector3 vertex in vertexBuffer2) { + verts.AddRange([vertex.X, vertex.Y, vertex.Z]); + fileLines.Add($"v {vertex.X} {vertex.Y} {vertex.Z}"); + } + + for (int i = 0; i < indexBuffer2.Count; i += 3) { + tris.AddRange([indexBuffer2[i], indexBuffer2[i + 1], indexBuffer2[i + 2]]); + fileLines.Add($"f {indexBuffer2[i] + 1} {indexBuffer2[i + 1] + 1} {indexBuffer2[i + 2] + 1}"); + if (isFluid) { + areas.Add(SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER); + } else { + areas.Add(0); + } + } + } + + // used for debugging + private void CreateObjFile(string xblock) { + // create a new file if it doesn't exist + if (System.IO.File.Exists($"{xblock}.obj")) { + System.IO.File.Delete($"{xblock}.obj"); + } + + var file = System.IO.File.Create($"{xblock}.obj"); + using StreamWriter streamWriter = new StreamWriter(file); + foreach (string line in fileLines) { + streamWriter.WriteLine(line); + } + streamWriter.Close(); + fileLines.Clear(); + } + + #region TileMesh configuration + // tiling configuration + // var config = new RcConfig( + // useTiles: true, + // tileSizeX: TileSize, + // tileSizeZ: TileSize, + // borderSize: RcConfig.CalcBorder(0.3f, CellSize), + // partition: RcPartition.WATERSHED, + // cellSize: CellSize, + // cellHeight: CellSize, + // agentMaxSlope: 47f, // generally 45 degrees, but we add a bit more to account for floating point errors + // agentMaxClimb: 0.7f, + // agentHeight: 1.4f, // approximation of character height + // agentRadius: 0.3f, // approximation of character radius + // minRegionArea: 8 * 8 * CellSize * CellSize, + // mergeRegionArea: 20 * 20 * CellSize * CellSize, + // edgeMaxLen: 12.0f, + // edgeMaxError: 1.3f, + // vertsPerPoly: VertsPerPoly, + // detailSampleDist: 6.0f, + // detailSampleMaxError: 1.0f, + // filterLowHangingObstacles: true, + // filterLedgeSpans: true, + // filterWalkableLowHeightSpans: true, + // walkableAreaMod: new RcAreaModification(0x3f), + // buildMeshDetail: true + // ); + + // try { + // RcBuilder rcBuilder = new(); + // List results = rcBuilder.BuildTiles(geomProvider, config, true, true, Environment.ProcessorCount + 1, Task.Factory); + + // List tileMeshData = BuildMeshData(geomProvider, config.Cs, config.Ch, config.WalkableHeightWorld, config.WalkableRadiusWorld, config.WalkableClimbWorld, results); + // DtNavMesh tileNavMesh = BuildNavMesh(geomProvider, tileMeshData, config.Cs, TileSize, VertsPerPoly); + + // string navmeshFilePath = $"navmeshes/{xblock}.navmesh"; + + // using var fs = new FileStream(navmeshFilePath, FileMode.Create, FileAccess.Write); + // using var bw = new BinaryWriter(fs); + + // DtMeshSetWriter writer = new(); + // writer.Write(bw, tileNavMesh, RcByteOrder.LITTLE_ENDIAN, true); + // } catch (Exception ex) { + // Console.WriteLine($"Failed to generate navmesh for {xblock} due to {ex.Message}"); + // return; + // } + // public static List BuildMeshData(IInputGeomProvider geom, float cellSize, float cellHeight, float agentHeight, + // float agentRadius, float agentMaxClimb, IList results) { + // List meshData = []; + // foreach (RcBuilderResult result in results) { + // int x = result.TileX; + // int z = result.TileZ; + // RcPolyMesh pmesh = result.Mesh; + // RcPolyMeshDetail dmesh = result.MeshDetail; + // DtNavMeshCreateParams option = new(); + // for (int i = 0; i < pmesh.npolys; ++i) { + // pmesh.flags[i] = 1; + // } + + // option.verts = pmesh.verts; + // option.vertCount = pmesh.nverts; + // option.polys = pmesh.polys; + // option.polyAreas = pmesh.areas; + // option.polyFlags = pmesh.flags; + // option.polyCount = pmesh.npolys; + // option.nvp = pmesh.nvp; + // if (dmesh != null) { + // option.detailMeshes = dmesh.meshes; + // option.detailVerts = dmesh.verts; + // option.detailVertsCount = dmesh.nverts; + // option.detailTris = dmesh.tris; + // option.detailTriCount = dmesh.ntris; + // } + + // option.walkableHeight = agentHeight; + // option.walkableRadius = agentRadius; + // option.walkableClimb = agentMaxClimb; + // option.bmin = pmesh.bmin; + // option.bmax = pmesh.bmax; + // option.cs = cellSize; + // option.ch = cellHeight; + // option.buildBvTree = true; + + // // TODO: Off-mesh connections + // // var offMeshConnections = geom.GetOffMeshConnections(); + // // option.offMeshConCount = offMeshConnections.Count; + // // option.offMeshConVerts = new float[option.offMeshConCount * 6]; + // // option.offMeshConRad = new float[option.offMeshConCount]; + // // option.offMeshConDir = new int[option.offMeshConCount]; + // // option.offMeshConAreas = new int[option.offMeshConCount]; + // // option.offMeshConFlags = new int[option.offMeshConCount]; + // // option.offMeshConUserID = new int[option.offMeshConCount]; + // // for (int i = 0; i < option.offMeshConCount; i++) { + // // RcOffMeshConnection offMeshCon = offMeshConnections[i]; + // // for (int j = 0; j < 6; j++) { + // // option.offMeshConVerts[6 * i + j] = offMeshCon.verts[j]; + // // } + + // // option.offMeshConRad[i] = offMeshCon.radius; + // // option.offMeshConDir[i] = offMeshCon.bidir ? 1 : 0; + // // option.offMeshConAreas[i] = offMeshCon.area; + // // option.offMeshConFlags[i] = offMeshCon.flags; + // // // option.offMeshConUserID[i] = offMeshCon.userId; + // // } + + // option.tileX = x; + // option.tileZ = z; + // var dtMeshData = DtNavMeshBuilder.CreateNavMeshData(option); + // if (dtMeshData != null) { + // meshData.Add(DemoNavMeshBuilder.UpdateAreaAndFlags(dtMeshData)); + // } + // } + + // return meshData; + // } + + // public static DtNavMesh BuildNavMesh(IInputGeomProvider geom, List meshData, float cellSize, int tileSize, int vertsPerPoly) { + // DtNavMeshParams navMeshParams = new() { + // orig = geom.GetMeshBoundsMin(), + // tileWidth = tileSize * cellSize, + // tileHeight = tileSize * cellSize, + + // maxTiles = GetMaxTiles(geom, cellSize, tileSize), + // maxPolys = GetMaxPolysPerTile(geom, cellSize, tileSize) + // }; + + // DtNavMesh navMesh = new(); + // navMesh.Init(navMeshParams, vertsPerPoly); + // meshData.ForEach(md => navMesh.AddTile(md, 0, 0, out long _)); + // return navMesh; + // } + // public static int GetMaxTiles(IInputGeomProvider geom, float cellSize, int tileSize) { + // int tileBits = GetTileBits(geom, cellSize, tileSize); + // return 1 << tileBits; + // } + + // public static int GetMaxPolysPerTile(IInputGeomProvider geom, float cellSize, int tileSize) { + // int polyBits = 22 - GetTileBits(geom, cellSize, tileSize); + // return 1 << polyBits; + // } + + // private static int GetTileBits(IInputGeomProvider geom, float cellSize, int tileSize) { + // RcRecast.CalcGridSize(geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), cellSize, out int gw, out int gh); + // int tw = (gw + tileSize - 1) / tileSize; + // int th = (gh + tileSize - 1) / tileSize; + // int tileBits = Math.Min(DtUtils.Ilog2(DtUtils.NextPow2(tw * th)), 14); + // return tileBits; + // } + #endregion +} diff --git a/Maple2.File.Ingest/Mapper/NifMapper.cs b/Maple2.File.Ingest/Mapper/NifMapper.cs index e6b7df7cf..669427f37 100644 --- a/Maple2.File.Ingest/Mapper/NifMapper.cs +++ b/Maple2.File.Ingest/Mapper/NifMapper.cs @@ -1,48 +1,48 @@ -using Maple2.File.Ingest.Helpers; -using Maple2.File.IO.Nif; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.Tools.VectorMath; - -namespace Maple2.File.Ingest.Mapper; - -public class NifMapper : TypeMapper { - protected override IEnumerable Map() { - foreach ((uint llid, NifDocument document) in NifParserHelper.nifDocuments) { - BoundingBox3 bounds = NifParserHelper.nifBounds[llid]; - - yield return new NifMetadata( - Llid: llid, - PhysXBounds: bounds, - Blocks: MapBlockMetadata(document).ToArray() - ); - } - } - - private static IEnumerable MapBlockMetadata(NifDocument document) { - foreach (NifBlock item in document.Blocks) { - int nxsMeshIndex = -1; - if (item is NiPhysXMeshDesc meshDesc) { - string meshDataString = Convert.ToBase64String(meshDesc.MeshData); - if (NifParserHelper.nxsMeshIndexMap.TryGetValue(meshDataString, out int value)) { - nxsMeshIndex = value; - } - } - - NifMetadata.NifBlockMetadata nifBlockMetadata = item switch { - NiPhysXActorDesc actorDesc => new NifMetadata.NiPhysXActorDescMetadata( - item.BlockIndex, - actorDesc.Name, - ActorName: actorDesc.ActorName, - Poses: actorDesc.Poses, - ShapeDescriptions: actorDesc.ShapeDescriptions.Select(shapeDesc => shapeDesc.BlockIndex).ToList()), - NiPhysXMeshDesc meshDescBlock => new NifMetadata.NiPhysXMeshDescMetadata(item.BlockIndex, meshDescBlock.Name, MeshName: meshDescBlock.Name, MeshDataIndex: nxsMeshIndex), - NiPhysXProp prop => new NifMetadata.NiPhysXPropMetadata(item.BlockIndex, prop.Name, PhysXToWorldScale: prop.PhysXToWorldScale, Snapshot: prop.Snapshot?.BlockIndex ?? -1), - NiPhysXPropDesc propDesc => new NifMetadata.NiPhysXPropDescMetadata(item.BlockIndex, propDesc.Name, Actors: propDesc.Actors.Select(actor => actor.BlockIndex).ToList()), - NiPhysXShapeDesc shapeDesc => new NifMetadata.NiPhysXShapeDescMetadata(item.BlockIndex, shapeDesc.Name, LocalPose: shapeDesc.LocalPose, ShapeType: (NxShapeType) shapeDesc.ShapeType, BoxHalfExtents: shapeDesc.BoxHalfExtents, Mesh: shapeDesc.Mesh?.BlockIndex ?? -1), - _ => new NifMetadata.NifBlockMetadata(item.BlockIndex, item.Name), - }; - yield return nifBlockMetadata; - } - } -} +using Maple2.File.Ingest.Helpers; +using Maple2.File.IO.Nif; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.Tools.VectorMath; + +namespace Maple2.File.Ingest.Mapper; + +public class NifMapper : TypeMapper { + protected override IEnumerable Map() { + foreach ((uint llid, NifDocument document) in NifParserHelper.nifDocuments) { + BoundingBox3 bounds = NifParserHelper.nifBounds[llid]; + + yield return new NifMetadata( + Llid: llid, + PhysXBounds: bounds, + Blocks: MapBlockMetadata(document).ToArray() + ); + } + } + + private static IEnumerable MapBlockMetadata(NifDocument document) { + foreach (NifBlock item in document.Blocks) { + int nxsMeshIndex = -1; + if (item is NiPhysXMeshDesc meshDesc) { + string meshDataString = Convert.ToBase64String(meshDesc.MeshData); + if (NifParserHelper.nxsMeshIndexMap.TryGetValue(meshDataString, out int value)) { + nxsMeshIndex = value; + } + } + + NifMetadata.NifBlockMetadata nifBlockMetadata = item switch { + NiPhysXActorDesc actorDesc => new NifMetadata.NiPhysXActorDescMetadata( + item.BlockIndex, + actorDesc.Name, + ActorName: actorDesc.ActorName, + Poses: actorDesc.Poses, + ShapeDescriptions: actorDesc.ShapeDescriptions.Select(shapeDesc => shapeDesc.BlockIndex).ToList()), + NiPhysXMeshDesc meshDescBlock => new NifMetadata.NiPhysXMeshDescMetadata(item.BlockIndex, meshDescBlock.Name, MeshName: meshDescBlock.Name, MeshDataIndex: nxsMeshIndex), + NiPhysXProp prop => new NifMetadata.NiPhysXPropMetadata(item.BlockIndex, prop.Name, PhysXToWorldScale: prop.PhysXToWorldScale, Snapshot: prop.Snapshot?.BlockIndex ?? -1), + NiPhysXPropDesc propDesc => new NifMetadata.NiPhysXPropDescMetadata(item.BlockIndex, propDesc.Name, Actors: propDesc.Actors.Select(actor => actor.BlockIndex).ToList()), + NiPhysXShapeDesc shapeDesc => new NifMetadata.NiPhysXShapeDescMetadata(item.BlockIndex, shapeDesc.Name, LocalPose: shapeDesc.LocalPose, ShapeType: (NxShapeType) shapeDesc.ShapeType, BoxHalfExtents: shapeDesc.BoxHalfExtents, Mesh: shapeDesc.Mesh?.BlockIndex ?? -1), + _ => new NifMetadata.NifBlockMetadata(item.BlockIndex, item.Name), + }; + yield return nifBlockMetadata; + } + } +} diff --git a/Maple2.File.Ingest/Mapper/NpcMapper.cs b/Maple2.File.Ingest/Mapper/NpcMapper.cs index 9b7d7014f..0a068d53a 100644 --- a/Maple2.File.Ingest/Mapper/NpcMapper.cs +++ b/Maple2.File.Ingest/Mapper/NpcMapper.cs @@ -1,155 +1,155 @@ -using System.Diagnostics; -using System.Numerics; -using M2dXmlGenerator; -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Xml.Npc; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; - -namespace Maple2.File.Ingest.Mapper; - -public class NpcMapper : TypeMapper { - private readonly NpcParser parser; - - public static Dictionary NpcMetadataById { get; } = new(); - - public NpcMapper(M2dReader xmlReader, string language) { - parser = new NpcParser(xmlReader, language); - } - - protected override IEnumerable Map() { - foreach ((int id, string name, NpcData data, List _) in parser.Parse()) { - Debug.Assert(data.collision.shape == "box" || string.IsNullOrWhiteSpace(data.collision.shape)); - - var metadata = new NpcMetadata(Id: id, - Name: name, - AiPath: data.aiInfo.path, - Model: new NpcMetadataModel( - Name: data.model.kfm, - Scale: data.model.scale, - AniSpeed: data.model.anispeed - ), - Distance: new NpcMetadataDistance( - Avoid: data.distance.avoid, - Sight: data.distance.sight, - SightHeightUp: data.distance.sightHeightUP, - SightHeightDown: data.distance.sightHeightDown, - LastSightRadius: data.distance.customLastSightRadius == 0 ? Constant.NpcLastSightRadius : data.distance.customLastSightRadius, - LastSightHeightUp: data.distance.customLastSightHeightUp == 0 ? Constant.NpcLastSightHeightUp : data.distance.customLastSightHeightUp, - LastSightHeightDown: data.distance.customLastSightHeightDown == 0 ? Constant.NpcLastSightHeightDown : data.distance.customLastSightHeightDown - ), - Skill: new NpcMetadataSkill( - Entries: data.skill.ids.Select((skillId, i) => - new NpcMetadataSkill.Entry(skillId, data.skill.levels[i])).ToArray(), - Cooldown: data.skill.coolDown - ), - Stat: new NpcMetadataStat(Stats: MapStats(data.stat), - ScaleStatRate: [ - data.stat.scaleStatRate_1, - data.stat.scaleStatRate_2, - data.stat.scaleStatRate_3, - data.stat.scaleStatRate_4, - ], - ScaleBaseTap: [ - data.stat.scaleBaseTap_1, - data.stat.scaleBaseTap_2, - data.stat.scaleBaseTap_3, - data.stat.scaleBaseTap_4, - ], - ScaleBaseDef: [ - data.stat.scaleBaseDef_1, - data.stat.scaleBaseDef_2, - data.stat.scaleBaseDef_3, - data.stat.scaleBaseDef_4, - ], - ScaleBaseSpaRate: [ - data.stat.scaleBaseSpaRate_1, - data.stat.scaleBaseSpaRate_2, - data.stat.scaleBaseSpaRate_3, - data.stat.scaleBaseSpaRate_4, - ]), - Basic: new NpcMetadataBasic(Friendly: data.basic.friendly, - AttackGroup: data.basic.npcAttackGroup, - DefenseGroup: data.basic.npcDefenseGroup, - Kind: data.basic.kind, - ShopId: data.basic.shopId, - HitImmune: data.basic.hitImmune, - AbnormalImmune: data.basic.abnormalImmune, - Level: data.basic.level, - Class: data.basic.@class, - RotationDisabled: data.basic.rotationDisabled, - MaxSpawnCount: data.basic.maxSpawnCount, - GroupSpawnCount: data.basic.groupSpawnCount, - RareDegree: data.basic.rareDegree, - MainTags: data.basic.mainTags, - SubTags: data.basic.subTags, - Difficulty: data.basic.difficulty, - CustomExp: data.exp.customExp), - Property: new NpcMetadataProperty( - Buffs: data.additionalEffect.codes.Select((buffId, i) => new NpcMetadataBuff(buffId, data.additionalEffect.levels[i])).ToArray(), - Capsule: new NpcMetadataCapsule(data.capsule.radius, data.capsule.height), - Collision: data.collision.shape == "box" ? new NpcMetadataCollision( - Dimensions: new Vector3(data.collision.width, data.collision.depth, data.collision.height), - Offset: new Vector3(data.collision.widthOffset, data.collision.depthOffset, data.collision.heightOffset) - ) : null), - DropInfo: new NpcMetadataDropInfo( - DropDistanceBase: data.dropiteminfo.dropDistanceBase, - DropDistanceRandom: data.dropiteminfo.dropDistanceRandom, - GlobalDropBoxIds: data.dropiteminfo.globalDropBoxId, - DeadGlobalDropBoxIds: data.dropiteminfo.globalDeadDropBoxId, - IndividualDropBoxIds: data.dropiteminfo.individualDropBoxId, - GlobalHitDropBoxIds: data.dropiteminfo.globalHitDropBoxId, - IndividualHitDropBoxIds: data.dropiteminfo.globalHitDropBoxId), - Action: new NpcMetadataAction( - RotateSpeed: data.speed.rotation, - WalkSpeed: data.speed.walk, - RunSpeed: data.speed.run, - Actions: data.normal.action.Zip(data.normal.prob, (action, prob) => new NpcAction(action, prob)).ToArray(), - MoveArea: data.normal.movearea, - MaidExpired: data.normal.maidExpired), - Dead: new NpcMetadataDead( - Time: data.dead.time, - Revival: data.dead.revival, - Count: data.dead.count, - LifeTime: data.dead.lifeTime, - ExtendRoomTime: data.dead.extendRoomTime), - LookAtTarget: new NpcMetadataLookAtTarget( - data.lookattarget.targetdummy, - data.lookattarget.lookAtMyPCWhenTalking == 1, - data.lookattarget.useTalkMotion == 1) - ); - - NpcMetadataById[id] = metadata; - - yield return metadata; - } - } - - private static IReadOnlyDictionary MapStats(Stat stat) { - Dictionary stats = stat.ToDictionary(); - - if (FeatureLocaleFilter.FeatureEnabled("HiddenStatAdd01")) { - stats[BasicAttribute.Health] = stats.GetValueOrDefault(BasicAttribute.Health) + stat.hiddenhpadd; - stats[BasicAttribute.Defense] = stats.GetValueOrDefault(BasicAttribute.Defense) + stat.hiddennddadd; - } - if (FeatureLocaleFilter.FeatureEnabled("HiddenStatAdd02")) { - stats[BasicAttribute.PhysicalAtk] = stats.GetValueOrDefault(BasicAttribute.PhysicalAtk) + stat.hiddenwapadd; - stats[BasicAttribute.MagicalAtk] = stats.GetValueOrDefault(BasicAttribute.MagicalAtk) + stat.hiddenwapadd; - } - if (FeatureLocaleFilter.FeatureEnabled("HiddenStatAdd03")) { - stats[BasicAttribute.Health] = stats.GetValueOrDefault(BasicAttribute.Health) + stat.hiddenhpadd03; - stats[BasicAttribute.Defense] = stats.GetValueOrDefault(BasicAttribute.Defense) + stat.hiddennddadd03; - stats[BasicAttribute.PhysicalAtk] = stats.GetValueOrDefault(BasicAttribute.PhysicalAtk) + stat.hiddenwapadd03; - stats[BasicAttribute.MagicalAtk] = stats.GetValueOrDefault(BasicAttribute.MagicalAtk) + stat.hiddenwapadd03; - } - if (FeatureLocaleFilter.FeatureEnabled("HiddenStatAdd04")) { - stats[BasicAttribute.Health] = stats.GetValueOrDefault(BasicAttribute.Health) + stat.hiddenhpadd04; - stats[BasicAttribute.Defense] = stats.GetValueOrDefault(BasicAttribute.Defense) + stat.hiddennddadd04; - stats[BasicAttribute.PhysicalAtk] = stats.GetValueOrDefault(BasicAttribute.PhysicalAtk) + stat.hiddenwapadd04; - stats[BasicAttribute.MagicalAtk] = stats.GetValueOrDefault(BasicAttribute.MagicalAtk) + stat.hiddenwapadd04; - } - - return stats; - } -} +using System.Diagnostics; +using System.Numerics; +using M2dXmlGenerator; +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Xml.Npc; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; + +namespace Maple2.File.Ingest.Mapper; + +public class NpcMapper : TypeMapper { + private readonly NpcParser parser; + + public static Dictionary NpcMetadataById { get; } = new(); + + public NpcMapper(M2dReader xmlReader, string language) { + parser = new NpcParser(xmlReader, language); + } + + protected override IEnumerable Map() { + foreach ((int id, string name, NpcData data, List _) in parser.Parse()) { + Debug.Assert(data.collision.shape == "box" || string.IsNullOrWhiteSpace(data.collision.shape)); + + var metadata = new NpcMetadata(Id: id, + Name: name, + AiPath: data.aiInfo.path, + Model: new NpcMetadataModel( + Name: data.model.kfm, + Scale: data.model.scale, + AniSpeed: data.model.anispeed + ), + Distance: new NpcMetadataDistance( + Avoid: data.distance.avoid, + Sight: data.distance.sight, + SightHeightUp: data.distance.sightHeightUP, + SightHeightDown: data.distance.sightHeightDown, + LastSightRadius: data.distance.customLastSightRadius == 0 ? Constant.NpcLastSightRadius : data.distance.customLastSightRadius, + LastSightHeightUp: data.distance.customLastSightHeightUp == 0 ? Constant.NpcLastSightHeightUp : data.distance.customLastSightHeightUp, + LastSightHeightDown: data.distance.customLastSightHeightDown == 0 ? Constant.NpcLastSightHeightDown : data.distance.customLastSightHeightDown + ), + Skill: new NpcMetadataSkill( + Entries: data.skill.ids.Select((skillId, i) => + new NpcMetadataSkill.Entry(skillId, data.skill.levels[i])).ToArray(), + Cooldown: data.skill.coolDown + ), + Stat: new NpcMetadataStat(Stats: MapStats(data.stat), + ScaleStatRate: [ + data.stat.scaleStatRate_1, + data.stat.scaleStatRate_2, + data.stat.scaleStatRate_3, + data.stat.scaleStatRate_4, + ], + ScaleBaseTap: [ + data.stat.scaleBaseTap_1, + data.stat.scaleBaseTap_2, + data.stat.scaleBaseTap_3, + data.stat.scaleBaseTap_4, + ], + ScaleBaseDef: [ + data.stat.scaleBaseDef_1, + data.stat.scaleBaseDef_2, + data.stat.scaleBaseDef_3, + data.stat.scaleBaseDef_4, + ], + ScaleBaseSpaRate: [ + data.stat.scaleBaseSpaRate_1, + data.stat.scaleBaseSpaRate_2, + data.stat.scaleBaseSpaRate_3, + data.stat.scaleBaseSpaRate_4, + ]), + Basic: new NpcMetadataBasic(Friendly: data.basic.friendly, + AttackGroup: data.basic.npcAttackGroup, + DefenseGroup: data.basic.npcDefenseGroup, + Kind: data.basic.kind, + ShopId: data.basic.shopId, + HitImmune: data.basic.hitImmune, + AbnormalImmune: data.basic.abnormalImmune, + Level: data.basic.level, + Class: data.basic.@class, + RotationDisabled: data.basic.rotationDisabled, + MaxSpawnCount: data.basic.maxSpawnCount, + GroupSpawnCount: data.basic.groupSpawnCount, + RareDegree: data.basic.rareDegree, + MainTags: data.basic.mainTags, + SubTags: data.basic.subTags, + Difficulty: data.basic.difficulty, + CustomExp: data.exp.customExp), + Property: new NpcMetadataProperty( + Buffs: data.additionalEffect.codes.Select((buffId, i) => new NpcMetadataBuff(buffId, data.additionalEffect.levels[i])).ToArray(), + Capsule: new NpcMetadataCapsule(data.capsule.radius, data.capsule.height), + Collision: data.collision.shape == "box" ? new NpcMetadataCollision( + Dimensions: new Vector3(data.collision.width, data.collision.depth, data.collision.height), + Offset: new Vector3(data.collision.widthOffset, data.collision.depthOffset, data.collision.heightOffset) + ) : null), + DropInfo: new NpcMetadataDropInfo( + DropDistanceBase: data.dropiteminfo.dropDistanceBase, + DropDistanceRandom: data.dropiteminfo.dropDistanceRandom, + GlobalDropBoxIds: data.dropiteminfo.globalDropBoxId, + DeadGlobalDropBoxIds: data.dropiteminfo.globalDeadDropBoxId, + IndividualDropBoxIds: data.dropiteminfo.individualDropBoxId, + GlobalHitDropBoxIds: data.dropiteminfo.globalHitDropBoxId, + IndividualHitDropBoxIds: data.dropiteminfo.globalHitDropBoxId), + Action: new NpcMetadataAction( + RotateSpeed: data.speed.rotation, + WalkSpeed: data.speed.walk, + RunSpeed: data.speed.run, + Actions: data.normal.action.Zip(data.normal.prob, (action, prob) => new NpcAction(action, prob)).ToArray(), + MoveArea: data.normal.movearea, + MaidExpired: data.normal.maidExpired), + Dead: new NpcMetadataDead( + Time: data.dead.time, + Revival: data.dead.revival, + Count: data.dead.count, + LifeTime: data.dead.lifeTime, + ExtendRoomTime: data.dead.extendRoomTime), + LookAtTarget: new NpcMetadataLookAtTarget( + data.lookattarget.targetdummy, + data.lookattarget.lookAtMyPCWhenTalking == 1, + data.lookattarget.useTalkMotion == 1) + ); + + NpcMetadataById[id] = metadata; + + yield return metadata; + } + } + + private static IReadOnlyDictionary MapStats(Stat stat) { + Dictionary stats = stat.ToDictionary(); + + if (FeatureLocaleFilter.FeatureEnabled("HiddenStatAdd01")) { + stats[BasicAttribute.Health] = stats.GetValueOrDefault(BasicAttribute.Health) + stat.hiddenhpadd; + stats[BasicAttribute.Defense] = stats.GetValueOrDefault(BasicAttribute.Defense) + stat.hiddennddadd; + } + if (FeatureLocaleFilter.FeatureEnabled("HiddenStatAdd02")) { + stats[BasicAttribute.PhysicalAtk] = stats.GetValueOrDefault(BasicAttribute.PhysicalAtk) + stat.hiddenwapadd; + stats[BasicAttribute.MagicalAtk] = stats.GetValueOrDefault(BasicAttribute.MagicalAtk) + stat.hiddenwapadd; + } + if (FeatureLocaleFilter.FeatureEnabled("HiddenStatAdd03")) { + stats[BasicAttribute.Health] = stats.GetValueOrDefault(BasicAttribute.Health) + stat.hiddenhpadd03; + stats[BasicAttribute.Defense] = stats.GetValueOrDefault(BasicAttribute.Defense) + stat.hiddennddadd03; + stats[BasicAttribute.PhysicalAtk] = stats.GetValueOrDefault(BasicAttribute.PhysicalAtk) + stat.hiddenwapadd03; + stats[BasicAttribute.MagicalAtk] = stats.GetValueOrDefault(BasicAttribute.MagicalAtk) + stat.hiddenwapadd03; + } + if (FeatureLocaleFilter.FeatureEnabled("HiddenStatAdd04")) { + stats[BasicAttribute.Health] = stats.GetValueOrDefault(BasicAttribute.Health) + stat.hiddenhpadd04; + stats[BasicAttribute.Defense] = stats.GetValueOrDefault(BasicAttribute.Defense) + stat.hiddennddadd04; + stats[BasicAttribute.PhysicalAtk] = stats.GetValueOrDefault(BasicAttribute.PhysicalAtk) + stat.hiddenwapadd04; + stats[BasicAttribute.MagicalAtk] = stats.GetValueOrDefault(BasicAttribute.MagicalAtk) + stat.hiddenwapadd04; + } + + return stats; + } +} diff --git a/Maple2.File.Ingest/Mapper/PetMapper.cs b/Maple2.File.Ingest/Mapper/PetMapper.cs index 3bb3ab3e9..9ebf346e8 100644 --- a/Maple2.File.Ingest/Mapper/PetMapper.cs +++ b/Maple2.File.Ingest/Mapper/PetMapper.cs @@ -1,75 +1,75 @@ -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Xml.Pet; -using Maple2.File.Parser.Xml.Table; -using Maple2.Model.Metadata; - -namespace Maple2.File.Ingest.Mapper; - -public class PetMapper : TypeMapper { - private readonly PetParser parser; - - public PetMapper(M2dReader xmlReader) { - parser = new PetParser(xmlReader); - } - - protected override IEnumerable Map() { - var petNames = new Dictionary(); - var petData = new Dictionary(); - foreach ((int id, string name, PetData data) in parser.Parse()) { - petNames[id] = name; - petData[id] = data; - } - - foreach (PetProperty property in parser.ParseProperty()) { - if (!petData.TryGetValue(property.code, out PetData? data)) { - // Defaults - data = new PetData { - code = property.code, - slotNum = property.slotNum, - skill = new Skill(), - distance = new Distance { - pick = 1050, - warp = property.warpDistance, - trace = property.traceDistance, - battleTrace = property.battleTraceDistance, - }, - time = new Time { - bore = 120000, - idle = 70000, - skill = 13000, - tired = 10000, - summonCast = 700, - }, - }; - } - //Debug.Assert(property.slotNum == data.slotNum, $"{id} inventory slots mismatch: {property.slotNum} != {data.slotNum}"); - // 60000026 - yield return new PetMetadata( - Id: property.code, - Name: petNames.GetValueOrDefault(property.code), - Type: property.type, - AiPresets: property.tamingAiPresets.ToArray(), - NpcId: property.npcID, - ItemSlots: property.slotNum, - EnableExtraction: property.enablePetExtraction, - OptionLevel: property.optionLevel, - OptionFactor: property.constantOptionFactor, - Skill: data.skill.id == 0 ? null : new PetMetadataSkill(data.skill.id, data.skill.level), - Effect: property.additionalEffectID == null ? [] - : property.additionalEffectID.Zip(property.additionalEffectLevel, - (effectId, level) => new PetMetadataEffect(effectId, level)).ToArray(), - Distance: new PetMetadataDistance( - Warp: property.warpDistance, - Trace: property.traceDistance, - BattleTrace: property.battleTraceDistance), - Time: new PetMetadataTime( - Idle: data.time.idle, - Bore: data.time.bore, - Summon: data.time.summonCast, - Tired: data.time.tired, - Skill: data.time.skill) - ); - } - } -} +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Xml.Pet; +using Maple2.File.Parser.Xml.Table; +using Maple2.Model.Metadata; + +namespace Maple2.File.Ingest.Mapper; + +public class PetMapper : TypeMapper { + private readonly PetParser parser; + + public PetMapper(M2dReader xmlReader) { + parser = new PetParser(xmlReader); + } + + protected override IEnumerable Map() { + var petNames = new Dictionary(); + var petData = new Dictionary(); + foreach ((int id, string name, PetData data) in parser.Parse()) { + petNames[id] = name; + petData[id] = data; + } + + foreach (PetProperty property in parser.ParseProperty()) { + if (!petData.TryGetValue(property.code, out PetData? data)) { + // Defaults + data = new PetData { + code = property.code, + slotNum = property.slotNum, + skill = new Skill(), + distance = new Distance { + pick = 1050, + warp = property.warpDistance, + trace = property.traceDistance, + battleTrace = property.battleTraceDistance, + }, + time = new Time { + bore = 120000, + idle = 70000, + skill = 13000, + tired = 10000, + summonCast = 700, + }, + }; + } + //Debug.Assert(property.slotNum == data.slotNum, $"{id} inventory slots mismatch: {property.slotNum} != {data.slotNum}"); + // 60000026 + yield return new PetMetadata( + Id: property.code, + Name: petNames.GetValueOrDefault(property.code), + Type: property.type, + AiPresets: property.tamingAiPresets.ToArray(), + NpcId: property.npcID, + ItemSlots: property.slotNum, + EnableExtraction: property.enablePetExtraction, + OptionLevel: property.optionLevel, + OptionFactor: property.constantOptionFactor, + Skill: data.skill.id == 0 ? null : new PetMetadataSkill(data.skill.id, data.skill.level), + Effect: property.additionalEffectID == null ? [] + : property.additionalEffectID.Zip(property.additionalEffectLevel, + (effectId, level) => new PetMetadataEffect(effectId, level)).ToArray(), + Distance: new PetMetadataDistance( + Warp: property.warpDistance, + Trace: property.traceDistance, + BattleTrace: property.battleTraceDistance), + Time: new PetMetadataTime( + Idle: data.time.idle, + Bore: data.time.bore, + Summon: data.time.summonCast, + Tired: data.time.tired, + Skill: data.time.skill) + ); + } + } +} diff --git a/Maple2.File.Ingest/Mapper/QuestMapper.cs b/Maple2.File.Ingest/Mapper/QuestMapper.cs index cea97ec6a..df55842e4 100644 --- a/Maple2.File.Ingest/Mapper/QuestMapper.cs +++ b/Maple2.File.Ingest/Mapper/QuestMapper.cs @@ -1,136 +1,136 @@ -using System.Diagnostics; -using M2dXmlGenerator; -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Enum; -using Maple2.File.Parser.Xml.Quest; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using ConditionType = Maple2.Model.Enum.ConditionType; -using ExpType = Maple2.Model.Enum.ExpType; - -namespace Maple2.File.Ingest.Mapper; - -public class QuestMapper : TypeMapper { - private readonly QuestParser parser; - - public QuestMapper(M2dReader xmlReader, string language) { - parser = new QuestParser(xmlReader, language); - } - - protected override IEnumerable Map() { - foreach ((int id, string name, QuestData data) in parser.Parse()) { - Debug.Assert(Enum.IsDefined((QuestType) data.basic.questType), $"Invalid QuestType: {data.basic.questType}"); - var unrequiredAchievement = (0, 0); - if (data.require.unreqAchievement.Length == 2 && - int.TryParse(data.require.unreqAchievement[0], out int achievementId) && - int.TryParse(data.require.unreqAchievement[1], out int grade)) { - unrequiredAchievement = (achievementId, grade); - } - yield return new QuestMetadata( - Id: id, - Name: name, - Basic: new QuestMetadataBasic( - ChapterId: data.basic.chapterID, - Type: (QuestType) data.basic.questType, - Account: data.basic.account, - StandardLevel: data.basic.standardLevel, - Forfeitable: !data.basic.disableGiveup, - EventTag: data.basic.eventTag, - AutoStart: data.basic.autoStart, - Disabled: data.basic.locking, - UsePostbox: data.basic.usePostbox, - StartNpc: data.start?.npc ?? 0, - CompleteNpc: data.complete?.npc ?? 0, - CompleteMaps: data.complete?.map, - ProgressMaps: data.progressMap.progressMap - ), - Require: new QuestMetadataRequire( - Level: data.require.level, - MaxLevel: data.require.maxLevel, - Job: data.require.job.Select(job => (JobCode) job).ToArray(), - Quest: data.require.quest, - SelectableQuest: data.require.selectableQuest, - Achievement: data.require.achievement, - UnrequiredAchievement: unrequiredAchievement, - GearScore: data.require.gearScore - ), - AcceptReward: Convert(data.acceptReward), - CompleteReward: Convert(data.completeReward), - RemoteAccept: new QuestRemoteAccept( - Type: (QuestRemoteType) data.remoteAccept.useRemote, - MapId: data.remoteAccept.requireField - ), - RemoteComplete: new QuestRemoteComplete( - Type: (QuestRemoteType) data.remoteComplete.useRemote, - MapId: data.remoteComplete.requireField, - RequireDungeonClear: data.remoteComplete.requireDungeonClear > 0 - ), - GoToNpc: new QuestMetadataGoToNpc( - Enabled: data.gotoNpc.enable, - MapId: data.gotoNpc.gotoField, - PortalId: data.gotoNpc.gotoPortal), - GoToDungeon: new QuestMetadataGoToDungeon( - State: (QuestState) data.gotoDungeon.state, - MapId: data.gotoDungeon.gotoDungeon, - InstanceId: data.gotoDungeon.gotoInstanceID), - Dispatch: data.dispatch == null ? null : new QuestDispatch( - Type: Enum.TryParse(data.dispatch.type, true, out QuestDispatchType dispatchType) ? dispatchType : QuestDispatchType.None, - MapId: data.dispatch.field, - PortalId: data.dispatch.portal, - Script: data.dispatch.script - ), - Mentoring: data.mentoringMission == null || string.IsNullOrEmpty(data.mentoringMission.mentoringIcon) ? null : new QuestMentoringMission( - OpeningDay: data.mentoringMission.openingDay, - Season: data.mentoringMission.mentoringSeason - ), - SummonPortal: data.summonPortal is { fieldID: 0, portalID: 0 } ? null : new QuestSummonPortal( - MapId: data.summonPortal.fieldID, - PortalId: data.summonPortal.portalID - ), - EventMissionType: Enum.TryParse(data.eventMission.@event, true, out QuestEventMissionType eventMissionType) ? eventMissionType : QuestEventMissionType.none, - Conditions: data.condition.Select(condition => new ConditionMetadata( - Type: (ConditionType) condition.type, - Value: condition.value == 0 ? 1 : condition.value, - Codes: condition.code.ConvertCodes(), - Target: condition.target.ConvertCodes(), - PartyCount: condition.partyCount, - GuildPartyCount: condition.guildPartyCount - )).ToArray() - ); - } - } - - private static QuestMetadataReward Convert(Reward reward) { - List essentialItem = reward.essentialItem; - List essentialJobItem = reward.essentialJobItem; - if (FeatureLocaleFilter.FeatureEnabled("GlobalQuestRewardItem")) { - essentialItem = reward.globalEssentialItem.Count > 0 ? reward.globalEssentialItem : essentialItem; - essentialJobItem = reward.globalEssentialJobItem.Count > 0 ? reward.globalEssentialJobItem : essentialJobItem; - } - - return new QuestMetadataReward( - Meso: reward.money, - Exp: reward.exp, - RelativeExp: ToExpType(reward.relativeExp), - GuildFund: reward.guildFund, - GuildExp: reward.guildExp, - GuildCoin: reward.guildCoin, - Treva: reward.karma, - Rue: reward.lu, - MenteeCoin: reward.menteeCoin, - MissionPoint: reward.missionPoint, - EssentialItem: essentialItem.Select(item => - new QuestMetadataReward.Item(item.code, item.rank, item.count)).ToList(), - EssentialJobItem: essentialJobItem.Select(item => - new QuestMetadataReward.Item(item.code, item.rank, item.count)).ToList() - ); - } - - private static ExpType ToExpType(RelativeExp commonExpType) { - if (Enum.TryParse(commonExpType.ToString(), out ExpType expType)) { - return expType; - } - return ExpType.none; - } -} +using System.Diagnostics; +using M2dXmlGenerator; +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Enum; +using Maple2.File.Parser.Xml.Quest; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using ConditionType = Maple2.Model.Enum.ConditionType; +using ExpType = Maple2.Model.Enum.ExpType; + +namespace Maple2.File.Ingest.Mapper; + +public class QuestMapper : TypeMapper { + private readonly QuestParser parser; + + public QuestMapper(M2dReader xmlReader, string language) { + parser = new QuestParser(xmlReader, language); + } + + protected override IEnumerable Map() { + foreach ((int id, string name, QuestData data) in parser.Parse()) { + Debug.Assert(Enum.IsDefined((QuestType) data.basic.questType), $"Invalid QuestType: {data.basic.questType}"); + var unrequiredAchievement = (0, 0); + if (data.require.unreqAchievement.Length == 2 && + int.TryParse(data.require.unreqAchievement[0], out int achievementId) && + int.TryParse(data.require.unreqAchievement[1], out int grade)) { + unrequiredAchievement = (achievementId, grade); + } + yield return new QuestMetadata( + Id: id, + Name: name, + Basic: new QuestMetadataBasic( + ChapterId: data.basic.chapterID, + Type: (QuestType) data.basic.questType, + Account: data.basic.account, + StandardLevel: data.basic.standardLevel, + Forfeitable: !data.basic.disableGiveup, + EventTag: data.basic.eventTag, + AutoStart: data.basic.autoStart, + Disabled: data.basic.locking, + UsePostbox: data.basic.usePostbox, + StartNpc: data.start?.npc ?? 0, + CompleteNpc: data.complete?.npc ?? 0, + CompleteMaps: data.complete?.map, + ProgressMaps: data.progressMap.progressMap + ), + Require: new QuestMetadataRequire( + Level: data.require.level, + MaxLevel: data.require.maxLevel, + Job: data.require.job.Select(job => (JobCode) job).ToArray(), + Quest: data.require.quest, + SelectableQuest: data.require.selectableQuest, + Achievement: data.require.achievement, + UnrequiredAchievement: unrequiredAchievement, + GearScore: data.require.gearScore + ), + AcceptReward: Convert(data.acceptReward), + CompleteReward: Convert(data.completeReward), + RemoteAccept: new QuestRemoteAccept( + Type: (QuestRemoteType) data.remoteAccept.useRemote, + MapId: data.remoteAccept.requireField + ), + RemoteComplete: new QuestRemoteComplete( + Type: (QuestRemoteType) data.remoteComplete.useRemote, + MapId: data.remoteComplete.requireField, + RequireDungeonClear: data.remoteComplete.requireDungeonClear > 0 + ), + GoToNpc: new QuestMetadataGoToNpc( + Enabled: data.gotoNpc.enable, + MapId: data.gotoNpc.gotoField, + PortalId: data.gotoNpc.gotoPortal), + GoToDungeon: new QuestMetadataGoToDungeon( + State: (QuestState) data.gotoDungeon.state, + MapId: data.gotoDungeon.gotoDungeon, + InstanceId: data.gotoDungeon.gotoInstanceID), + Dispatch: data.dispatch == null ? null : new QuestDispatch( + Type: Enum.TryParse(data.dispatch.type, true, out QuestDispatchType dispatchType) ? dispatchType : QuestDispatchType.None, + MapId: data.dispatch.field, + PortalId: data.dispatch.portal, + Script: data.dispatch.script + ), + Mentoring: data.mentoringMission == null || string.IsNullOrEmpty(data.mentoringMission.mentoringIcon) ? null : new QuestMentoringMission( + OpeningDay: data.mentoringMission.openingDay, + Season: data.mentoringMission.mentoringSeason + ), + SummonPortal: data.summonPortal is { fieldID: 0, portalID: 0 } ? null : new QuestSummonPortal( + MapId: data.summonPortal.fieldID, + PortalId: data.summonPortal.portalID + ), + EventMissionType: Enum.TryParse(data.eventMission.@event, true, out QuestEventMissionType eventMissionType) ? eventMissionType : QuestEventMissionType.none, + Conditions: data.condition.Select(condition => new ConditionMetadata( + Type: (ConditionType) condition.type, + Value: condition.value == 0 ? 1 : condition.value, + Codes: condition.code.ConvertCodes(), + Target: condition.target.ConvertCodes(), + PartyCount: condition.partyCount, + GuildPartyCount: condition.guildPartyCount + )).ToArray() + ); + } + } + + private static QuestMetadataReward Convert(Reward reward) { + List essentialItem = reward.essentialItem; + List essentialJobItem = reward.essentialJobItem; + if (FeatureLocaleFilter.FeatureEnabled("GlobalQuestRewardItem")) { + essentialItem = reward.globalEssentialItem.Count > 0 ? reward.globalEssentialItem : essentialItem; + essentialJobItem = reward.globalEssentialJobItem.Count > 0 ? reward.globalEssentialJobItem : essentialJobItem; + } + + return new QuestMetadataReward( + Meso: reward.money, + Exp: reward.exp, + RelativeExp: ToExpType(reward.relativeExp), + GuildFund: reward.guildFund, + GuildExp: reward.guildExp, + GuildCoin: reward.guildCoin, + Treva: reward.karma, + Rue: reward.lu, + MenteeCoin: reward.menteeCoin, + MissionPoint: reward.missionPoint, + EssentialItem: essentialItem.Select(item => + new QuestMetadataReward.Item(item.code, item.rank, item.count)).ToList(), + EssentialJobItem: essentialJobItem.Select(item => + new QuestMetadataReward.Item(item.code, item.rank, item.count)).ToList() + ); + } + + private static ExpType ToExpType(RelativeExp commonExpType) { + if (Enum.TryParse(commonExpType.ToString(), out ExpType expType)) { + return expType; + } + return ExpType.none; + } +} diff --git a/Maple2.File.Ingest/Mapper/RideMapper.cs b/Maple2.File.Ingest/Mapper/RideMapper.cs index 19d7c7073..917c7d3d6 100644 --- a/Maple2.File.Ingest/Mapper/RideMapper.cs +++ b/Maple2.File.Ingest/Mapper/RideMapper.cs @@ -1,43 +1,43 @@ -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Xml.Riding; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; - -namespace Maple2.File.Ingest.Mapper; - -public class RideMapper : TypeMapper { - private readonly RidingParser parser; - - public RideMapper(M2dReader xmlReader) { - parser = new RidingParser(xmlReader); - } - - protected override IEnumerable Map() { - var passengers = new Dictionary(); - foreach ((int id, IList data) in parser.ParsePassenger()) { - passengers[id] = data.Count; - } - - foreach ((int id, Riding data) in parser.Parse()) { - yield return new RideMetadata( - Id: id, - Model: data.basic.kfm, - Basic: new RideMetadataBasic( - Type: Enum.TryParse(data.basic.type.ToString(), true, out RideOnType type) ? type : RideOnType.Default, - SkillSetId: data.basic.skillSetID, - SummonTime: data.basic.rideSummonCastTime, - RunXStamina: data.basic.runXConsumeEp, - EnableSwim: data.basic.enableSwim, - FallDamageDown: data.basic.fallDamageDown, - Passengers: passengers.GetValueOrDefault(id)), - Speed: new RideMetadataSpeed( - WalkSpeed: data.basic.walkSpeed, - RunSpeed: data.basic.runSpeed, - RunXSpeed: data.basic.runXSpeed, - SwimSpeed: data.basic.swimSpeed), - Stats: data.stat.ToDictionary() - ); - } - } -} +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Xml.Riding; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; + +namespace Maple2.File.Ingest.Mapper; + +public class RideMapper : TypeMapper { + private readonly RidingParser parser; + + public RideMapper(M2dReader xmlReader) { + parser = new RidingParser(xmlReader); + } + + protected override IEnumerable Map() { + var passengers = new Dictionary(); + foreach ((int id, IList data) in parser.ParsePassenger()) { + passengers[id] = data.Count; + } + + foreach ((int id, Riding data) in parser.Parse()) { + yield return new RideMetadata( + Id: id, + Model: data.basic.kfm, + Basic: new RideMetadataBasic( + Type: Enum.TryParse(data.basic.type.ToString(), true, out RideOnType type) ? type : RideOnType.Default, + SkillSetId: data.basic.skillSetID, + SummonTime: data.basic.rideSummonCastTime, + RunXStamina: data.basic.runXConsumeEp, + EnableSwim: data.basic.enableSwim, + FallDamageDown: data.basic.fallDamageDown, + Passengers: passengers.GetValueOrDefault(id)), + Speed: new RideMetadataSpeed( + WalkSpeed: data.basic.walkSpeed, + RunSpeed: data.basic.runSpeed, + RunXSpeed: data.basic.runXSpeed, + SwimSpeed: data.basic.swimSpeed), + Stats: data.stat.ToDictionary() + ); + } + } +} diff --git a/Maple2.File.Ingest/Mapper/ScriptMapper.cs b/Maple2.File.Ingest/Mapper/ScriptMapper.cs index e885fde18..42922489a 100644 --- a/Maple2.File.Ingest/Mapper/ScriptMapper.cs +++ b/Maple2.File.Ingest/Mapper/ScriptMapper.cs @@ -1,103 +1,103 @@ -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Xml.Script; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using CinematicContent = Maple2.Model.Metadata.CinematicContent; -using CinematicDistractor = Maple2.Model.Metadata.CinematicDistractor; -using CinematicEventScript = Maple2.Model.Metadata.CinematicEventScript; -using ScriptContent = Maple2.Model.Metadata.ScriptContent; - -namespace Maple2.File.Ingest.Mapper; - -public class ScriptMapper : TypeMapper { - private readonly ScriptParser parser; - - public ScriptMapper(M2dReader xmlReader, string language) { - parser = new ScriptParser(xmlReader, language); - } - - protected override IEnumerable Map() { - foreach ((int id, NpcScript script) in parser.ParseNpc()) { - var states = new Dictionary(); - if (script.job != null) { - states.Add(script.job.id, new ScriptState( - Id: script.job.id, - Type: ScriptStateType.Job, - Pick: script.job.randomPick, - JobCondition: null, - Contents: ParseCinematicContents(script.job.content))); - } - foreach (TalkScript select in script.select) { - states.Add(select.id, new ScriptState( - Id: select.id, - Type: ScriptStateType.Select, - Pick: select.randomPick, - JobCondition: null, - Contents: ParseCinematicContents(select.content))); - } - foreach (ConditionTalkScript select in script.script) { - int[] conditions = select.gotoConditionTalkID; // TODO: - states.Add(select.id, new ScriptState( - Id: select.id, - Type: ScriptStateType.Script, - Pick: select.randomPick, - JobCondition: null, - Contents: ParseCinematicContents(select.content))); - } - if (states.Count == 0) { - continue; - } - - yield return new ScriptMetadata(Id: id, Type: ScriptType.Npc, States: states); - } - - foreach ((int id, QuestScript script) in parser.ParseQuest()) { - var states = new Dictionary(); - foreach (QuestTalkScript talk in script.script) { - states.Add(talk.id, new ScriptState( - Id: talk.id, - Type: ScriptStateType.Quest, - Pick: talk.randomPick, - JobCondition: (JobCode) talk.jobCondition, - Contents: ParseCinematicContents(talk.content))); - } - if (states.Count == 0) { - continue; - } - - yield return new ScriptMetadata(Id: id, Type: ScriptType.Quest, States: states); - } - } - - private static CinematicContent[] ParseCinematicContents(IList contents) { - var result = new List(); - foreach (Parser.Xml.Script.CinematicContent content in contents) { - var distractors = new List(); - foreach (Parser.Xml.Script.CinematicDistractor distractor in content.distractor) { - distractors.Add(new CinematicDistractor(Goto: distractor.@goto, GotoFail: distractor.gotoFail)); - } - - var events = new List(); - foreach (Parser.Xml.Script.CinematicEventScript @event in content.@event) { - var eventContents = new List(); - foreach (Parser.Xml.Script.ScriptContent eventContent in @event.content) { - eventContents.Add(new ScriptContent( - Text: eventContent.text, - VoiceId: eventContent.voiceID, - Illustration: eventContent.illust)); - } - events.Add(new CinematicEventScript(@event.id, eventContents.ToArray())); - } - - result.Add(new CinematicContent( - Text: content.text, - ButtonType: (NpcTalkButton) content.buttonSet, - FunctionId: content.functionID, - Distractors: distractors.ToArray(), - Events: events.ToArray())); - } - - return result.ToArray(); - } -} +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Xml.Script; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using CinematicContent = Maple2.Model.Metadata.CinematicContent; +using CinematicDistractor = Maple2.Model.Metadata.CinematicDistractor; +using CinematicEventScript = Maple2.Model.Metadata.CinematicEventScript; +using ScriptContent = Maple2.Model.Metadata.ScriptContent; + +namespace Maple2.File.Ingest.Mapper; + +public class ScriptMapper : TypeMapper { + private readonly ScriptParser parser; + + public ScriptMapper(M2dReader xmlReader, string language) { + parser = new ScriptParser(xmlReader, language); + } + + protected override IEnumerable Map() { + foreach ((int id, NpcScript script) in parser.ParseNpc()) { + var states = new Dictionary(); + if (script.job != null) { + states.Add(script.job.id, new ScriptState( + Id: script.job.id, + Type: ScriptStateType.Job, + Pick: script.job.randomPick, + JobCondition: null, + Contents: ParseCinematicContents(script.job.content))); + } + foreach (TalkScript select in script.select) { + states.Add(select.id, new ScriptState( + Id: select.id, + Type: ScriptStateType.Select, + Pick: select.randomPick, + JobCondition: null, + Contents: ParseCinematicContents(select.content))); + } + foreach (ConditionTalkScript select in script.script) { + int[] conditions = select.gotoConditionTalkID; // TODO: + states.Add(select.id, new ScriptState( + Id: select.id, + Type: ScriptStateType.Script, + Pick: select.randomPick, + JobCondition: null, + Contents: ParseCinematicContents(select.content))); + } + if (states.Count == 0) { + continue; + } + + yield return new ScriptMetadata(Id: id, Type: ScriptType.Npc, States: states); + } + + foreach ((int id, QuestScript script) in parser.ParseQuest()) { + var states = new Dictionary(); + foreach (QuestTalkScript talk in script.script) { + states.Add(talk.id, new ScriptState( + Id: talk.id, + Type: ScriptStateType.Quest, + Pick: talk.randomPick, + JobCondition: (JobCode) talk.jobCondition, + Contents: ParseCinematicContents(talk.content))); + } + if (states.Count == 0) { + continue; + } + + yield return new ScriptMetadata(Id: id, Type: ScriptType.Quest, States: states); + } + } + + private static CinematicContent[] ParseCinematicContents(IList contents) { + var result = new List(); + foreach (Parser.Xml.Script.CinematicContent content in contents) { + var distractors = new List(); + foreach (Parser.Xml.Script.CinematicDistractor distractor in content.distractor) { + distractors.Add(new CinematicDistractor(Goto: distractor.@goto, GotoFail: distractor.gotoFail)); + } + + var events = new List(); + foreach (Parser.Xml.Script.CinematicEventScript @event in content.@event) { + var eventContents = new List(); + foreach (Parser.Xml.Script.ScriptContent eventContent in @event.content) { + eventContents.Add(new ScriptContent( + Text: eventContent.text, + VoiceId: eventContent.voiceID, + Illustration: eventContent.illust)); + } + events.Add(new CinematicEventScript(@event.id, eventContents.ToArray())); + } + + result.Add(new CinematicContent( + Text: content.text, + ButtonType: (NpcTalkButton) content.buttonSet, + FunctionId: content.functionID, + Distractors: distractors.ToArray(), + Events: events.ToArray())); + } + + return result.ToArray(); + } +} diff --git a/Maple2.File.Ingest/Mapper/ServerTableMapper.cs b/Maple2.File.Ingest/Mapper/ServerTableMapper.cs index 55f7d2d71..4428456d2 100644 --- a/Maple2.File.Ingest/Mapper/ServerTableMapper.cs +++ b/Maple2.File.Ingest/Mapper/ServerTableMapper.cs @@ -1,2109 +1,2109 @@ -using System.Globalization; -using System.Xml; -using Maple2.Database.Extensions; -using Maple2.File.Ingest.Utils; -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Enum; -using Maple2.File.Parser.Xml.Table.Server; -using Maple2.Model; -using Maple2.Model.Common; -using Maple2.Model.Enum; -using Maple2.Model.Error; -using Maple2.Model.Game; -using Maple2.Model.Game.Shop; -using Maple2.Model.Metadata; -using DayOfWeek = System.DayOfWeek; -using ExpType = Maple2.Model.Enum.ExpType; -using Fish = Maple2.File.Parser.Xml.Table.Server.Fish; -using FishingSpot = Maple2.File.Parser.Xml.Table.Server.FishingSpot; -using GuildNpcType = Maple2.Model.Enum.GuildNpcType; -using IndividualItemDrop = Maple2.File.Parser.Xml.Table.Server.IndividualItemDrop; -using InstanceType = Maple2.Model.Enum.InstanceType; -using JobConditionTable = Maple2.Model.Metadata.JobConditionTable; -using MergeOption = Maple2.File.Parser.Xml.Table.Server.MergeOption; -using ScriptEventType = Maple2.Model.Enum.ScriptEventType; -using ScriptType = Maple2.Model.Enum.ScriptType; -using TimeEventType = Maple2.File.Parser.Enum.TimeEventType; - -namespace Maple2.File.Ingest.Mapper; - -public class ServerTableMapper : TypeMapper { - private readonly ServerTableParser parser; - - public ServerTableMapper(M2dReader xmlReader) { - parser = new ServerTableParser(xmlReader); - } - - protected override IEnumerable Map() { - yield return new ServerTableMetadata { - Name = ServerTableNames.INSTANCE_FIELD, - Table = ParseInstanceField(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.SCRIPT_CONDITION, - Table = ParseScriptCondition(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.SCRIPT_FUNCTION, - Table = ParseScriptFunction(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.SCRIPT_EVENT, - Table = ParseScriptEventConditionTable(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.JOB_CONDITION, - Table = ParseJobCondition(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.BONUS_GAME, - Table = ParseBonusGameTable(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.GLOBAL_DROP_ITEM_BOX, - Table = ParseGlobalItemDropTable(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.USER_STAT, - Table = ParseUserStat(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.INDIVIDUAL_DROP_ITEM, - Table = ParseIndividualItemDropTable(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.PRESTIGE_EXP, - Table = ParsePrestigeExpTable(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.PRESTIGE_ID_EXP, - Table = ParsePrestigeIdExpTable(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.TIME_EVENT, - Table = ParseTimeEventTable(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.GAME_EVENT, - Table = ParseGameEventTable(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.OX_QUIZ, - Table = ParseOxQuizTable(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.ITEM_MERGE, - Table = ParseItemMergeOptionTable(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.SHOP, - Table = ParseShop(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.SHOP_ITEM, - Table = ParseShopItems(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.BEAUTY_SHOP, - Table = ParseBeautyShops(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.MERET_MARKET, - Table = ParseMeretCustomShop(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.FISH, - Table = ParseFish(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.COMBINE_SPAWN, - Table = ParseCombineSpawn(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.ENCHANT_OPTION, - Table = ParseEnchantOption(), - }; - yield return new ServerTableMetadata { - Name = ServerTableNames.UNLIMITED_ENCHANT_OPTION, - Table = ParseUnlimitedEnchantOption(), - }; - - } - - private InstanceFieldTable ParseInstanceField() { - var results = new Dictionary(); - foreach ((int instanceId, InstanceField instanceField) in parser.ParseInstanceField()) { - foreach (int fieldId in instanceField.fieldIDs) { - - InstanceFieldMetadata instanceFieldMetadata = new( - MapId: fieldId, - Type: Enum.TryParse(instanceField.instanceType.ToString(), out InstanceType instanceType) ? instanceType : InstanceType.none, - InstanceId: instanceId, - BackupSourcePortal: instanceField.backupSourcePortal, - PoolCount: instanceField.poolCount, - SaveField: instanceField.isSaveField, - NpcStatFactorId: instanceField.npcStatFactorID, - MaxCount: instanceField.maxCount, - OpenType: instanceField.openType, - OpenValue: instanceField.openValue - ); - - results.Add(fieldId, instanceFieldMetadata); - } - } - - return new InstanceFieldTable(results); - } - - private ScriptConditionTable ParseScriptCondition() { - var results = new Dictionary>(); - results = MergeNpcScriptConditions(results, parser.ParseNpcScriptCondition()); - results = MergeQuestScriptConditions(results, parser.ParseQuestScriptCondition()); - - return new ScriptConditionTable(results); - } - - private Dictionary> MergeNpcScriptConditions(Dictionary> results, IEnumerable<(int NpcId, IDictionary ScriptConditions)> parser) { - foreach ((int npcId, IDictionary scripts) in parser) { - var scriptConditions = new Dictionary(); - foreach ((int scriptId, NpcScriptCondition scriptCondition) in scripts) { - var questStarted = new Dictionary(); - foreach (string quest in scriptCondition.quest_start) { - KeyValuePair parsedQuest = ParseToIntKeyValuePair(quest); - questStarted.Add(parsedQuest.Key, parsedQuest.Value); - } - - var questsCompleted = new Dictionary(); - foreach (string quest in scriptCondition.quest_complete) { - KeyValuePair parsedQuest = ParseToIntKeyValuePair(quest); - questsCompleted.Add(parsedQuest.Key, parsedQuest.Value); - } - - var items = new List>(); - for (int i = 0; i < scriptCondition.item.Length; i++) { - KeyValuePair parsedItem = ParseToIntKeyValuePair(scriptCondition.item[i]); - string itemCount = scriptCondition.itemCount.ElementAtOrDefault(i) ?? "1"; - if (!int.TryParse(itemCount, out int itemAmount)) { - itemAmount = 1; - } - var item = new ItemComponent(parsedItem.Key, -1, itemAmount, ItemTag.None); - items.Add(new KeyValuePair(item, parsedItem.Value)); - } - - scriptConditions.Add(scriptId, new ScriptConditionMetadata( - Id: npcId, - ScriptId: scriptId, - Type: ScriptType.Npc, - Maid: new ScriptConditionMetadata.MaidData( - Authority: scriptCondition.maid_auth, - Expired: scriptCondition.maid_expired != "!1", - ReadyToPay: scriptCondition.maid_ready_to_pay != "!1", - ClosenessRank: scriptCondition.maid_affinity_grade, - ClosenessTime: ParseToIntKeyValuePair(scriptCondition.maid_affinity_time), - MoodTime: ParseToIntKeyValuePair(scriptCondition.maid_mood_time), - DaysBeforeExpired: ParseToIntKeyValuePair(scriptCondition.maid_day_before_expired) - ), - Wedding: new ScriptConditionMetadata.WeddingData( - HasReservation: scriptCondition.weddingHallBooking < 0 ? null : scriptCondition.weddingHallBooking == 1, - MarriageDays: scriptCondition.marriageDate, - UserState: scriptCondition.weddingState < 0 ? null : (MaritalStatus) scriptCondition.weddingState, - HallState: ParseToStringKeyValuePair(scriptCondition.weddingHallState), - CoolingOff: scriptCondition.coolingOff), - JobCode: scriptCondition.job?.Select(job => (JobCode) job).ToList() ?? [], - QuestStarted: questStarted, - QuestCompleted: questsCompleted, - Items: items, - Buff: ParseToIntKeyValuePair(scriptCondition.buff), - Meso: ParseToIntKeyValuePair(scriptCondition.meso), - Level: ParseToIntKeyValuePair(scriptCondition.level), - AchieveCompleted: ParseToIntKeyValuePair(scriptCondition.achieve_complete), - DeathPenalty: scriptCondition.panelty == 1, - InGuild: scriptCondition.guild - )); - } - results.Add(npcId, scriptConditions); - } - return results; - } - - private Dictionary> MergeQuestScriptConditions(Dictionary> results, IEnumerable<(int NpcId, IDictionary ScriptConditions)> parser) { - foreach ((int questId, IDictionary scripts) in parser) { - var scriptConditions = new Dictionary(); - foreach ((int scriptId, QuestScriptCondition scriptCondition) in scripts) { - var questStarted = new Dictionary(); - foreach (string quest in scriptCondition.quest_start) { - KeyValuePair parsedQuest = ParseToIntKeyValuePair(quest); - questStarted.Add(parsedQuest.Key, parsedQuest.Value); - } - - var questsCompleted = new Dictionary(); - foreach (string quest in scriptCondition.quest_complete) { - KeyValuePair parsedQuest = ParseToIntKeyValuePair(quest); - questsCompleted.Add(parsedQuest.Key, parsedQuest.Value); - } - - var items = new List>(); - for (int i = 0; i < scriptCondition.item.Length; i++) { - KeyValuePair parsedItem = ParseToIntKeyValuePair(scriptCondition.item[i]); - string itemCount = scriptCondition.itemCount.ElementAtOrDefault(i) ?? "1"; - if (!int.TryParse(itemCount, out int itemAmount)) { - itemAmount = 1; - } - var item = new ItemComponent(parsedItem.Key, -1, itemAmount, ItemTag.None); - items.Add(new KeyValuePair(item, parsedItem.Value)); - } - - scriptConditions.Add(scriptId, new ScriptConditionMetadata( - Id: questId, - ScriptId: scriptId, - Type: ScriptType.Quest, - Maid: new ScriptConditionMetadata.MaidData( - Authority: scriptCondition.maid_auth, - Expired: scriptCondition.maid_expired != "!1", - ReadyToPay: scriptCondition.maid_ready_to_pay != "!1", - ClosenessRank: scriptCondition.maid_affinity_grade, - ClosenessTime: ParseToIntKeyValuePair(scriptCondition.maid_affinity_time), - MoodTime: ParseToIntKeyValuePair(scriptCondition.maid_mood_time), - DaysBeforeExpired: ParseToIntKeyValuePair(scriptCondition.maid_day_before_expired) - ), - Wedding: new ScriptConditionMetadata.WeddingData( - HasReservation: scriptCondition.weddingHallBooking < 0 ? null : scriptCondition.weddingHallBooking == 1, - MarriageDays: scriptCondition.marriageDate, - UserState: scriptCondition.weddingState < 0 ? null : (MaritalStatus) scriptCondition.weddingState, - HallState: ParseToStringKeyValuePair(scriptCondition.weddingHallState), - CoolingOff: scriptCondition.coolingOff), - JobCode: scriptCondition.job?.Select(job => (JobCode) job).ToList() ?? [], - QuestStarted: questStarted, - QuestCompleted: questsCompleted, - Items: items, - Buff: ParseToIntKeyValuePair(scriptCondition.buff), - Meso: ParseToIntKeyValuePair(scriptCondition.meso), - Level: ParseToIntKeyValuePair(scriptCondition.level), - AchieveCompleted: ParseToIntKeyValuePair(scriptCondition.achieve_complete), - InGuild: scriptCondition.guild, - DeathPenalty: scriptCondition.panelty == 1 - )); - } - results.Add(questId, scriptConditions); - } - return results; - } - - private static KeyValuePair ParseToIntKeyValuePair(string input) { - bool value = !input.StartsWith("!"); - - if (!value) { - input = input.Replace("!", ""); - } - - if (!int.TryParse(input, out int key)) { - key = 0; - } - return new KeyValuePair(key, value); - } - - private static KeyValuePair ParseToStringKeyValuePair(string input) { - bool value = !input.StartsWith("!"); - - if (!value) { - input = input.Substring(1); - } - return new KeyValuePair(input, value); - } - - private ScriptFunctionTable ParseScriptFunction() { - var results = new Dictionary>>(); - results = MergeNpcScriptFunctions(results, parser.ParseNpcScriptFunction()); - results = MergeQuestScriptFunctions(results, parser.ParseQuestScriptFunction()); - - return new ScriptFunctionTable(results); - } - - private static Dictionary>> MergeNpcScriptFunctions(Dictionary>> results, IEnumerable<(int NpcId, IDictionary ScriptFunctions)> parser) { - foreach ((int npcId, IDictionary scripts) in parser) { - var scriptDict = new Dictionary>(); // scriptIds, functionDict - foreach ((int scriptId, NpcScriptFunction scriptFunction) in scripts) { - var presentItems = new List(); - for (int i = 0; i < scriptFunction.presentItemID.Length; i++) { - short itemRarity = scriptFunction.presentItemRank.ElementAtOrDefault(i) != default(short) ? scriptFunction.presentItemRank.ElementAtOrDefault(i) : (short) -1; - int itemAmount = scriptFunction.presentItemAmount.ElementAtOrDefault(i) != default ? scriptFunction.presentItemAmount.ElementAtOrDefault(i) : 1; - presentItems.Add(new ItemComponent(scriptFunction.presentItemID[i], itemRarity, itemAmount, ItemTag.None)); - } - - var collectItems = new List(); - for (int i = 0; i < scriptFunction.collectItemID.Length; i++) { - int itemAmount = scriptFunction.collectItemAmount.ElementAtOrDefault(i) != default ? scriptFunction.collectItemAmount.ElementAtOrDefault(i) : 1; - collectItems.Add(new ItemComponent(scriptFunction.collectItemID[i], -1, itemAmount, ItemTag.None)); - } - - var metadata = new ScriptFunctionMetadata( - Id: npcId, // NpcId or QuestId - ScriptId: scriptId, - Type: ScriptType.Npc, - FunctionId: scriptFunction.functionID, - EndFunction: scriptFunction.endFunction, - PortalId: scriptFunction.portal, - UiName: scriptFunction.uiName, - UiArg: scriptFunction.uiArg, - UiArg2: scriptFunction.uiArg2, - MoveMapId: scriptFunction.moveFieldID, - MovePortalId: scriptFunction.moveFieldPortalID, - MoveMapMovie: scriptFunction.moveFieldMovie, - Emoticon: scriptFunction.emoticon, - PresentItems: presentItems, - CollectItems: collectItems, - SetTriggerValueTriggerId: scriptFunction.setTriggerValueTriggerID, - SetTriggerValueKey: scriptFunction.setTriggerValueKey, - SetTriggerValue: scriptFunction.setTriggerValue, - Divorce: scriptFunction.divorce, - PresentExp: scriptFunction.presentExp, - CollectMeso: scriptFunction.collectMeso, - MaidMoodIncrease: scriptFunction.maidMoodUp, - MaidClosenessIncrease: scriptFunction.maidAffinityUp, - MaidPay: scriptFunction.maidPay - ); - if (!scriptDict.TryGetValue(scriptId, out Dictionary? functionDict)) { - functionDict = new Dictionary { - { scriptFunction.functionID, metadata }, - }; - scriptDict.Add(scriptId, functionDict); - } else { - functionDict.Add(scriptFunction.functionID, metadata); - } - } - results.Add(npcId, scriptDict); - } - return results; - } - - private static Dictionary>> MergeQuestScriptFunctions(Dictionary>> results, IEnumerable<(int NpcId, IDictionary ScriptFunctions)> parser) { - foreach ((int questId, IDictionary scripts) in parser) { - var scriptDict = new Dictionary>(); // scriptIds, functionDict - foreach ((int scriptId, QuestScriptFunction scriptFunction) in scripts) { - var presentItems = new List(); - for (int i = 0; i < scriptFunction.presentItemID.Length; i++) { - short itemRarity = scriptFunction.presentItemRank.ElementAtOrDefault(i) != default(short) ? scriptFunction.presentItemRank.ElementAtOrDefault(i) : (short) -1; - int itemAmount = scriptFunction.presentItemAmount.ElementAtOrDefault(i) != default ? scriptFunction.presentItemAmount.ElementAtOrDefault(i) : 1; - presentItems.Add(new ItemComponent(scriptFunction.presentItemID[i], itemRarity, itemAmount, ItemTag.None)); - } - - var collectItems = new List(); - for (int i = 0; i < scriptFunction.collectItemID.Length; i++) { - int itemAmount = scriptFunction.collectItemAmount.ElementAtOrDefault(i) != default ? scriptFunction.collectItemAmount.ElementAtOrDefault(i) : 1; - collectItems.Add(new ItemComponent(scriptFunction.collectItemID[i], -1, itemAmount, ItemTag.None)); - } - - var metadata = new ScriptFunctionMetadata( - Id: questId, - ScriptId: scriptId, - Type: ScriptType.Quest, - FunctionId: scriptFunction.functionID, - EndFunction: scriptFunction.endFunction, - PortalId: scriptFunction.portal, - UiName: scriptFunction.uiName, - UiArg: scriptFunction.uiArg, - UiArg2: scriptFunction.uiArg2, - MoveMapId: scriptFunction.moveFieldID, - MovePortalId: scriptFunction.moveFieldPortalID, - MoveMapMovie: scriptFunction.moveFieldMovie, - Emoticon: scriptFunction.emoticon, - PresentItems: presentItems, - CollectItems: collectItems, - SetTriggerValueTriggerId: scriptFunction.setTriggerValueTriggerID, - SetTriggerValueKey: scriptFunction.setTriggerValueKey, - SetTriggerValue: scriptFunction.setTriggerValue, - Divorce: scriptFunction.divorce, - PresentExp: scriptFunction.presentExp, - CollectMeso: scriptFunction.collectMeso, - MaidMoodIncrease: scriptFunction.maidMoodUp, - MaidClosenessIncrease: scriptFunction.maidAffinityUp, - MaidPay: scriptFunction.maidPay - ); - if (!scriptDict.TryGetValue(scriptId, out Dictionary? functionDict)) { - functionDict = new Dictionary { - { scriptFunction.functionID, metadata }, - }; - scriptDict.Add(scriptId, functionDict); - } else { - functionDict.Add(scriptFunction.functionID, metadata); - } - } - results.Add(questId, scriptDict); - } - return results; - } - - private JobConditionTable ParseJobCondition() { - var results = new Dictionary(); - foreach ((int npcId, Parser.Xml.Table.Server.JobConditionTable jobCondition) in parser.ParseJobConditionTable()) { - DateTime date = DateTime.TryParseExact(jobCondition.date, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out date) ? date : DateTime.MinValue; - results.Add(npcId, new JobConditionMetadata( - NpcId: npcId, - ScriptId: jobCondition.scriptID, - StartedQuestId: jobCondition.quest_start, - CompletedQuestId: jobCondition.quest_complete, - JobCode: (JobCode) jobCondition.job, - MaidAuthority: jobCondition.maid_auth, - MaidClosenessTime: jobCondition.maid_affinity_time, - MaidCosenessRank: jobCondition.maid_affinity_grade, - Date: date.ToEpochSeconds(), - BuffId: jobCondition.buff, - Mesos: jobCondition.meso, - Level: jobCondition.level, - Home: jobCondition.home, - Roulette: jobCondition.roulette, - Guild: jobCondition.guild, - CompletedAchievement: jobCondition.achieve_complete, - IsBirthday: jobCondition.birthday, - ChangeToJobCode: (JobCode) jobCondition.jobCode, - MapId: jobCondition.map, - MoveMapId: jobCondition.moveFieldID, - MovePortalId: jobCondition.movePortalID, - DeathPenalty: jobCondition.panelty - )); - } - - return new JobConditionTable(results); - } - - private BonusGameTable ParseBonusGameTable() { - var bonusGames = new Dictionary(); - foreach ((int type, int id, BonusGame bonusGame) in parser.ParseBonusGame()) { - List slots = []; - foreach (BonusGame.Slot slot in bonusGame.slot) { - slots.Add(new BonusGameTable.Game.Slot( - MinProp: slot.minProp, - MaxProp: slot.maxProp)); - } - bonusGames.Add(id, new BonusGameTable.Game( - Id: id, - ConsumeItem: new ItemComponent( - ItemId: bonusGame.consumeItemID, - Rarity: 1, - Amount: bonusGame.consumeItemCount, - Tag: ItemTag.None), - Slots: slots.ToArray())); - } - - var drops = new Dictionary(); - foreach ((int type, int id, BonusGameDrop gameDrop) in parser.ParseBonusGameDrop()) { - List items = []; - foreach (BonusGameDrop.Item item in gameDrop.item) { - items.Add(new BonusGameTable.Drop.Item( - ItemComponent: new ItemComponent( - ItemId: item.id, - Rarity: item.rank, - Amount: item.count, - Tag: ItemTag.None), - Probability: item.prop, - Notice: item.notice)); - } - drops.Add(id, new BonusGameTable.Drop( - Id: id, - Items: items.ToArray())); - } - - return new BonusGameTable(bonusGames, drops); - } - - private GlobalDropItemBoxTable ParseGlobalItemDropTable() { - var dropGroups = new Dictionary>>(); - - foreach ((int id, GlobalDropItemBox itemDrop) in parser.ParseGlobalDropItemBox()) { - var groups = new List(); - foreach (GlobalDropItemBox.Group group in itemDrop.v) { - List dropCounts = []; - for (int i = 0; i < group.dropCount.Length; i++) { - dropCounts.Add(new GlobalDropItemBoxTable.Group.DropCount( - Amount: group.dropCount[i], - Probability: group.dropCountProbability[i])); - } - groups.Add(new GlobalDropItemBoxTable.Group( - GroupId: group.dropGroupIDs, - MinLevel: group.minLevel, - MaxLevel: group.maxLevel, - DropCounts: dropCounts, - OwnerDrop: group.isOwnerDrop, - MapTypeCondition: (MapType) group.mapTypeCondition, - ContinentCondition: (Continent) group.continentCondition)); - } - - if (!dropGroups.TryGetValue(id, out Dictionary>? groupDict)) { - groupDict = new Dictionary> { - { id, groups }, - }; - dropGroups.Add(id, groupDict); - } else { - groupDict.Add(id, groups); - } - } - - var dropItems = new Dictionary>(); - foreach ((int id, GlobalDropItemSet itemBox) in parser.ParseGlobalDropItemSet()) { - var items = new List(); - - foreach (GlobalDropItemSet.Item item in itemBox.v) { - int minCount = item.minCount <= 0 ? 1 : item.minCount; - int maxCount = item.maxCount < item.minCount ? item.minCount : item.maxCount; - items.Add(new GlobalDropItemBoxTable.Item( - Id: item.itemID, - MinLevel: item.minLevel, - MaxLevel: item.maxLevel, - DropCount: new GlobalDropItemBoxTable.Range(minCount, maxCount), - Rarity: item.grade, - Weight: item.weight, - MapIds: item.mapDependency, - QuestConstraint: item.constraintsQuest)); - } - - dropItems.Add(id, items); - } - return new GlobalDropItemBoxTable(dropGroups, dropItems); - } - - private UserStatTable ParseUserStat() { - static IReadOnlyDictionary UserStatMetadataMapper(UserStat userStat) { - Dictionary stats = new() { - { BasicAttribute.Strength, (long) userStat.str }, - { BasicAttribute.Dexterity, (long) userStat.dex }, - { BasicAttribute.Intelligence, (long) userStat.@int }, - { BasicAttribute.Luck, (long) userStat.luk }, - { BasicAttribute.Health, (long) userStat.hp }, - { BasicAttribute.HpRegen, (long) userStat.hp_rgp }, - { BasicAttribute.HpRegenInterval, (long) userStat.hp_inv }, - { BasicAttribute.Spirit, (long) userStat.sp }, - { BasicAttribute.SpRegen, (long) userStat.sp_rgp }, - { BasicAttribute.SpRegenInterval, (long) userStat.sp_inv }, - { BasicAttribute.Stamina, (long) userStat.ep }, - { BasicAttribute.StaminaRegen, (long) userStat.ep_rgp }, - { BasicAttribute.StaminaRegenInterval, (long) userStat.ep_inv }, - { BasicAttribute.AttackSpeed, (long) userStat.asp }, - { BasicAttribute.MovementSpeed, (long) userStat.msp }, - { BasicAttribute.Accuracy, (long) userStat.atp }, - { BasicAttribute.Evasion, (long) userStat.evp }, - { BasicAttribute.CriticalRate, (long) userStat.cap }, - { BasicAttribute.CriticalDamage, (long) userStat.cad }, - { BasicAttribute.CriticalEvasion, (long) userStat.car }, - { BasicAttribute.Defense, (long) userStat.ndd }, - { BasicAttribute.PerfectGuard, (long) userStat.abp }, - { BasicAttribute.JumpHeight, (long) userStat.jmp }, - { BasicAttribute.PhysicalAtk, (long) userStat.pap }, - { BasicAttribute.MagicalAtk, (long) userStat.map }, - { BasicAttribute.PhysicalRes, (long) userStat.par }, - { BasicAttribute.MagicalRes, (long) userStat.mar }, - { BasicAttribute.MinWeaponAtk, (long) userStat.wapmin }, - { BasicAttribute.MaxWeaponAtk, (long) userStat.wapmax }, - { BasicAttribute.Damage, (long) userStat.dmg }, - { BasicAttribute.Piercing, (long) userStat.pen }, - { BasicAttribute.BonusAtk, (long) userStat.base_atk }, - { BasicAttribute.PetBonusAtk, (long) userStat.sp_value }, - }; - - return stats; - } - - return new UserStatTable( - new Dictionary>> { - { JobCode.Newbie, parser.ParseUserStat1().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, - { JobCode.Knight, parser.ParseUserStat10().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, - { JobCode.Berserker, parser.ParseUserStat20().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, - { JobCode.Wizard, parser.ParseUserStat30().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, - { JobCode.Priest, parser.ParseUserStat40().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, - { JobCode.Archer, parser.ParseUserStat50().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, - { JobCode.HeavyGunner, parser.ParseUserStat60().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, - { JobCode.Thief, parser.ParseUserStat70().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, - { JobCode.Assassin, parser.ParseUserStat80().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, - { JobCode.RuneBlader, parser.ParseUserStat90().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, - { JobCode.Striker, parser.ParseUserStat100().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, - { JobCode.SoulBinder, parser.ParseUserStat110().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, - } - ); - } - - private IndividualDropItemTable ParseIndividualItemDropTable() { - var results = new Dictionary>(); - - foreach ((int id, IndividualItemDrop dropBox) in parser.ParseIndividualItemDrop()) { - var entries = new Dictionary(); - - foreach (IndividualItemDrop.Group group in dropBox.group) { - List items = []; - foreach (IndividualItemDrop.Group.Item item in group.v) { - int minCount = item.minCount <= 0 ? 1 : item.minCount; - int maxCount = item.maxCount < item.minCount ? item.minCount : item.maxCount; - List rarities = item.gradeProbability - .Select((probability, i) => new IndividualDropItemTable.Item.Rarity(probability, item.grade[i])) - .ToList(); - - if (rarities.Count == 0) { - if (item.grade.Length > 0) { - foreach (short grade in item.grade) { - rarities.Add(new IndividualDropItemTable.Item.Rarity(100, grade)); - } - } else if (item.uiItemRank != 0) { - rarities.Add(new IndividualDropItemTable.Item.Rarity(100, item.uiItemRank)); - } - } - items.Add(new IndividualDropItemTable.Item( - Ids: [item.itemID, item.itemID2], - Announce: item.isAnnounce, - ProperJobWeight: item.properJobWeight, - ImproperJobWeight: item.imProperJobWeight, - Weight: item.weight, - DropCount: new IndividualDropItemTable.Range(minCount, maxCount), - Rarities: rarities, - EnchantLevel: item.enchantLevel, - SocketDataId: item.socketDataID, - DeductTradeCount: item.tradableCountDeduction, - DeductRepackLimit: item.rePackingLimitCountDeduction, - Bind: item.isBindCharacter, - DisableBreak: item.disableBreak, - MapIds: item.mapDependency, - QuestId: item.constraintsQuest ? GetQuestId(dropBox.comment, item.reference1) : 0 - )); - } - - IList dropCounts = group.dropCount.Zip(group.dropCountProbability, (count, probability) => new IndividualDropItemTable.Entry.DropCount(count, probability)).ToList(); - if (dropCounts.Count == 0) { - dropCounts.Add(new IndividualDropItemTable.Entry.DropCount(1, 100)); - } - - var entry = new IndividualDropItemTable.Entry( - GroupId: group.dropGroupID, - SmartDropRate: group.smartDropRate, - DropCounts: dropCounts, - MinLevel: group.dropGroupMinLevel, - ServerDrop: group.serverDrop, - SmartGender: group.isApplySmartGenderDrop, - Items: items - ); - - entries.Add(group.dropGroupID, entry); - - } - - results.Add(id, entries); - } - return new IndividualDropItemTable(results); - - int GetQuestId(string comment, string reference1) { - if (reference1.Contains("Quest")) { - string[] referenceArray = reference1.Split("/"); - int referenceQuestIndex = Array.IndexOf(referenceArray, "Quest"); - - if (!int.TryParse(referenceArray[referenceQuestIndex - 2], out int questId) && comment.Contains("Quest")) { - string[] commentArray = comment.Split("/"); - int commentQuestIndex = Array.IndexOf(commentArray, "Quest"); - if (string.IsNullOrEmpty(commentArray[commentQuestIndex - 2])) { - return 0; - } - return !int.TryParse(commentArray[commentQuestIndex - 2], out questId) ? 0 : questId; - } - } - return 0; - } - } - - private PrestigeExpTable ParsePrestigeExpTable() { - var results = new Dictionary(); - - foreach ((AdventureExpType type, AdventureExpTable table) in parser.ParseAdventureExp()) { - ExpType expType = ToExpType(type); - results.Add(expType, table.value); - } - - return new PrestigeExpTable(results); - } - - private PrestigeIdExpTable ParsePrestigeIdExpTable() { - var results = new Dictionary(); - foreach ((int id, AdventureIdExpTable table) in parser.ParseAdventureIdExp()) { - results.Add(id, new PrestigeIdExpTable.Entry( - Id: id, - Value: table.value, - Type: ToExpType(table.expType))); - } - - return new PrestigeIdExpTable(results); - } - - private static ExpType ToExpType(AdventureExpType type) { - return type switch { - AdventureExpType.Exp_MapCommon => ExpType.mapCommon, - AdventureExpType.Exp_MapHidden => ExpType.mapHidden, - AdventureExpType.Exp_TaxiStation => ExpType.taxi, - AdventureExpType.Exp_Telescope => ExpType.telescope, - AdventureExpType.Exp_RareChest => ExpType.rareChest, - AdventureExpType.Exp_RareChestFirst => ExpType.rareChestFirst, - AdventureExpType.Exp_NormalChest => ExpType.normalChest, - AdventureExpType.Exp_DropItem => ExpType.dropItem, - AdventureExpType.Exp_DungeonBoss => ExpType.dungeonBoss, - AdventureExpType.Exp_MusicMasteryLv1 => ExpType.musicMastery1, - AdventureExpType.Exp_MusicMasteryLv2 => ExpType.musicMastery2, - AdventureExpType.Exp_MusicMasteryLv3 => ExpType.musicMastery3, - AdventureExpType.Exp_MusicMasteryLv4 => ExpType.musicMastery4, - AdventureExpType.Exp_Arcade => ExpType.arcade, - AdventureExpType.Exp_Fishing => ExpType.fishing, - AdventureExpType.Exp_Rest => ExpType.rest, - AdventureExpType.Exp_Quest => ExpType.quest, - AdventureExpType.Exp_PvpBloodMineRank1 => ExpType.bloodMineRank1, - AdventureExpType.Exp_PvpBloodMineRank2 => ExpType.bloodMineRank2, - AdventureExpType.Exp_PvpBloodMineRank3 => ExpType.bloodMineRank3, - AdventureExpType.Exp_PvpBloodMineRankOther => ExpType.bloodMineRankOther, - AdventureExpType.Exp_PvpRedDuelWin => ExpType.redDuelWin, - AdventureExpType.Exp_PvpRedDuelLose => ExpType.redDuelLose, - AdventureExpType.Exp_PvpBtiTeamWin => ExpType.btiTeamWin, - AdventureExpType.Exp_PvpBtiTeamLose => ExpType.btiTeamLose, - AdventureExpType.Exp_PvpRankDuelWin => ExpType.rankDuelWin, - AdventureExpType.Exp_PvpRankDuelLose => ExpType.rankDuelLose, - AdventureExpType.Exp_Gathering => ExpType.gathering, - AdventureExpType.Exp_Manufacturing => ExpType.manufacturing, - AdventureExpType.Exp_RandomDungeonBonus => ExpType.randomDungeonBonus, - AdventureExpType.Exp_MiniGame => ExpType.miniGame, - AdventureExpType.Exp_UserMiniGame => ExpType.userMiniGame, - AdventureExpType.Exp_UserMiniGameExtra => ExpType.userMiniGameExtra, - AdventureExpType.Exp_Mission => ExpType.mission, - AdventureExpType.Exp_DungeonRelative => ExpType.dungeonRelative, - AdventureExpType.Exp_GuildUserExp => ExpType.guildUserExp, - AdventureExpType.Exp_DailyGuildQuest => ExpType.dailyGuildQuest, - AdventureExpType.Exp_WeeklyGuildQuest => ExpType.weeklyGuildQuest, - AdventureExpType.Exp_PetTaming => ExpType.petTaming, - AdventureExpType.Exp_DailyMission => ExpType.dailymission, - AdventureExpType.Exp_DailyMissionLevelUp => ExpType.dailymissionLevelUp, - AdventureExpType.Exp_mapleSurvival => ExpType.mapleSurvival, - AdventureExpType.Exp_DarkStream => ExpType.darkStream, - AdventureExpType.Exp_DungeonClear => ExpType.dungeonClear, - AdventureExpType.Exp_KillMonster => ExpType.monster, - AdventureExpType.Exp_QuestETC => ExpType.questEtc, - AdventureExpType.Exp_EpicQuest => ExpType.epicQuest, - AdventureExpType.Exp_KillMonsterBoss => ExpType.monsterBoss, - AdventureExpType.Exp_KillMonsterElite => ExpType.monsterElite, - _ => ExpType.none, - }; - } - - private TimeEventTable ParseTimeEventTable() { - var results = new Dictionary(); - foreach ((int id, TimeEventData data) in parser.ParseTimeEventData()) { - // TODO: Handle other event types - if (data.type == TimeEventType.GlobalEvent) { - var entries = new GlobalPortalMetadata.Field[3]; // UI only supports 3 fields - entries[0] = new GlobalPortalMetadata.Field( - Name: data.eventName1, - MapId: data.eventField1.Length == 0 ? 0 : data.eventField1[0], - PortalId: data.eventField1.Length == 0 ? 0 : data.eventField1[1]); - entries[1] = new GlobalPortalMetadata.Field( - Name: data.eventName2, - MapId: data.eventField2.Length == 0 ? 0 : data.eventField2[0], - PortalId: data.eventField2.Length == 0 ? 0 : data.eventField2[1]); - entries[2] = new GlobalPortalMetadata.Field( - Name: data.eventName3, - MapId: data.eventField3.Length == 0 ? 0 : data.eventField3[0], - PortalId: data.eventField3.Length == 0 ? 0 : data.eventField3[1]); - int[] startTimeArray = ParseTimeToArray(data.startTime); - int[] endTimeArray = ParseTimeToArray(data.endTime); - int[] cycleArray = ParseTimeToArray(data.cycleTime); - int[] randomArray = ParseTimeToArray(data.randomTime); - int[] lifeArray = ParseTimeToArray(data.lifeTime); - results.Add(id, new GlobalPortalMetadata( - Id: id, - Probability: data.prob, - StartTime: new DateTime(startTimeArray[0], startTimeArray[1], startTimeArray[2], startTimeArray[3], startTimeArray[4], startTimeArray[5]), - EndTime: new DateTime(endTimeArray[0], endTimeArray[1], endTimeArray[2], endTimeArray[3], endTimeArray[4], endTimeArray[5]), - CycleTime: new TimeSpan(cycleArray[2], cycleArray[3], cycleArray[4], cycleArray[5]), - RandomTime: new TimeSpan(randomArray[2], randomArray[3], randomArray[4], randomArray[5]), - LifeTime: new TimeSpan(lifeArray[2], lifeArray[3], lifeArray[4], lifeArray[5]), - PopupMessage: data.popupMessage, - SoundId: data.soundID, - Entries: entries)); - } - } - return new TimeEventTable(results); - - int[] ParseTimeToArray(string time) { - string[] timeArray = time.Split('-'); - int[] timeInt = new int[timeArray.Length]; - for (int i = 0; i < timeArray.Length; i++) { - timeInt[i] = int.Parse(timeArray[i]); - } - return timeInt; - } - } - - private GameEventTable ParseGameEventTable() { - var results = new Dictionary(); - foreach ((int id, GameEvent data) in parser.ParseGameEvent()) { - if (!Enum.TryParse(data.eventType, out GameEventType eventType)) { - Console.WriteLine($"Unknown GameEventType: {data.eventType}"); - } - - GameEventData? eventData = ParseGameEventData(eventType, data.value1, data.value2, data.value3, data.value4); - if (eventData == null) { - continue; - } - - DateTime startTime = DateTime.TryParseExact(data.eventStart, "yyyy-MM-dd-HH-mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out startTime) ? startTime : DateTime.MinValue; - DateTime endTime = DateTime.TryParseExact(data.eventEnd, "yyyy-MM-dd-HH-mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out endTime) ? endTime : DateTime.MinValue; - - // Only add events that are not expired - if (endTime < DateTime.UtcNow) { - continue; - } - - (TimeSpan partTimeStart, TimeSpan partTimeEnd) = ParsePartTime(data.partTime); - results.Add(id, new GameEventMetadata( - Id: id, - Type: eventType, - StartTime: startTime, - EndTime: endTime, - StartPartTime: partTimeStart, - EndPartTime: partTimeEnd, - ActiveDays: data.dayOfWeek.Length == 0 ? [] : data.dayOfWeek.Select(ParseDayOfWeek).ToArray(), - Data: eventData, - Value1: data.value1, - Value2: data.value2, - Value3: data.value3, - Value4: data.value4)); - } - return new GameEventTable(results); - - DayOfWeek ParseDayOfWeek(Maple2.File.Parser.Enum.DayOfWeek dayofWeek) { - return dayofWeek switch { - Maple2.File.Parser.Enum.DayOfWeek.sun => DayOfWeek.Sunday, - Maple2.File.Parser.Enum.DayOfWeek.mon => DayOfWeek.Monday, - Maple2.File.Parser.Enum.DayOfWeek.tue => DayOfWeek.Tuesday, - Maple2.File.Parser.Enum.DayOfWeek.wed => DayOfWeek.Wednesday, - Maple2.File.Parser.Enum.DayOfWeek.thu => DayOfWeek.Thursday, - Maple2.File.Parser.Enum.DayOfWeek.fri => DayOfWeek.Friday, - Maple2.File.Parser.Enum.DayOfWeek.sat => DayOfWeek.Saturday, - _ => DayOfWeek.Sunday, - }; - } - } - - private (TimeSpan, TimeSpan) ParsePartTime(string partTimeString) { - string[] partTimeStringArray = partTimeString.Split("-").ToArray(); - if (partTimeStringArray.Length != 2) { - return (TimeSpan.Zero, TimeSpan.Zero); - } - TimeSpan startTime = TimeSpan.Parse(partTimeStringArray[0]); - TimeSpan endTime = TimeSpan.Parse(partTimeStringArray[1]); - return (startTime, endTime); - } - - private GameEventData? ParseGameEventData(GameEventType type, string value1, string value2, string value3, string value4) { - var value1Xml = new XmlDocument(); - var value2Xml = new XmlDocument(); - var value3Xml = new XmlDocument(); - var value4Xml = new XmlDocument(); - - switch (type) { - case GameEventType.BlueMarble: - if (!string.IsNullOrEmpty(value1)) { - value1Xml.LoadXml(value1); - } - - value2Xml.LoadXml(value2); - var rounds = new List(); - var requiredItem = new ItemComponent(0, 0, 0, ItemTag.None); - - XmlNode? roundNode = value1Xml.FirstChild; - if (roundNode != null) { - if (roundNode.Attributes?["consumeItemID"] != null) { - if (!int.TryParse(roundNode.Attributes?["consumeItemID"]?.Value, out int itemId)) { - itemId = 0; - } - if (!int.TryParse(roundNode.Attributes?["consumeItemCount"]?.Value, out int itemCount)) { - itemCount = 1; - } - requiredItem = new ItemComponent(itemId, -1, itemCount, ItemTag.None); - } - - foreach (XmlNode vNode in roundNode.ChildNodes) { - if (!int.TryParse(vNode.Attributes?["round"]?.Value, out int round)) { - round = 0; - } - - if (!int.TryParse(vNode.Attributes?["itemID"]?.Value, out int itemId)) { - itemId = 0; - } - - if (!int.TryParse(vNode.Attributes?["itemCount"]?.Value, out int itemCount)) { - itemCount = 1; - } - - rounds.Add(new BlueMarble.Round( - RoundCount: round, - Item: new ItemComponent( - ItemId: itemId, - Rarity: 1, - Amount: itemCount, - Tag: ItemTag.None))); - } - } - - XmlNode? slotsNode = value2Xml.FirstChild; - if (slotsNode == null) { - return null; - } - - var slots = new List(); - foreach (XmlNode vNode in slotsNode.ChildNodes) { - if (!Enum.TryParse(vNode.Attributes?["type"]?.Value, true, out BlueMarbleSlotType slotType)) { - slotType = BlueMarbleSlotType.Item; - } - - if (!int.TryParse(vNode.Attributes?["arg1"]?.Value, out int arg1)) { - arg1 = 0; - } - - if (!int.TryParse(vNode.Attributes?["arg2"]?.Value, out int arg2)) { - arg2 = 0; - } - - int moveAmount = 0; - if (slotType is BlueMarbleSlotType.Backward or BlueMarbleSlotType.Forward) { - moveAmount = arg1; - } - - var blueMarbleSlotItem = new ItemComponent(0, 0, 0, ItemTag.None); - if (slotType is BlueMarbleSlotType.Item or BlueMarbleSlotType.Paradise) { - // TODO: Get rarity from item xmls - blueMarbleSlotItem = new ItemComponent(arg1, -1, arg2, ItemTag.None); - } - - slots.Add(new BlueMarble.Slot( - Type: slotType, - MoveAmount: moveAmount, - Item: blueMarbleSlotItem)); - } - return new BlueMarble( - RequiredItem: requiredItem, - Rounds: rounds.ToArray(), - Slots: slots.ToArray()); - case GameEventType.StringBoard: - return new StringBoard( - Text: value4, - StringId: int.TryParse(value1, out int stringId) ? stringId : 0); - case GameEventType.StringBoardLink: - return new StringBoardLink( - Link: value1); - case GameEventType.TrafficOptimizer: - // values are hardcoded seeing as these are not shown in the table. - return new TrafficOptimizer( - RideSyncInterval: 100, - UserSyncInterval: 100, - LinearMovementInterval: 100, - GuideObjectSyncInterval: 100); - case GameEventType.LobbyMap: - return new LobbyMap( - MapId: int.TryParse(value1, out int lobbyMapId) ? lobbyMapId : 0); - case GameEventType.EventFieldPopup: - return new EventFieldPopup( - MapId: int.TryParse(value1, out int fieldPopupMapId) ? fieldPopupMapId : 0); - case GameEventType.SaleChat: - return new SaleChat( - WorldChatDiscount: int.TryParse(value1, out int worldChatDiscount) ? worldChatDiscount : 0, - ChannelChatDiscount: int.TryParse(value2, out int channelChatDiscount) ? channelChatDiscount : 0); - case GameEventType.AttendGift: - value1Xml = new XmlDocument(); - value1Xml.LoadXml(value1); - - var rewards = new List(); - if (value1Xml.FirstChild == null) { - return null; - } - foreach (XmlNode node in value1Xml.FirstChild.ChildNodes) { - if (!int.TryParse(node.Attributes?["itemID"]?.Value, out int itemId)) { - itemId = 0; - } - if (!int.TryParse(node.Attributes?["count"]?.Value, out int itemCount)) { - itemCount = 1; - } - if (!short.TryParse(node.Attributes?["grade"]?.Value, out short grade)) { - grade = -1; - } - rewards.Add(new RewardItem(itemId, grade, itemCount)); - } - - value2Xml = new XmlDocument(); - value2Xml.LoadXml(value2); - if (value2Xml.FirstChild is not { Name: "ms2" }) { - return null; - } - - XmlNode? stringNode = value2Xml.FirstChild.SelectSingleNode("string"); - XmlNode? configNode = value2Xml.FirstChild.SelectSingleNode("Config"); - if (stringNode == null || configNode == null) { - return null; - } - - string name = stringNode.Attributes?["name"]?.Value ?? string.Empty; - string mailTitle = stringNode.Attributes?["mailTitle"]?.Value ?? string.Empty; - string mailContent = stringNode.Attributes?["mailContents"]?.Value ?? string.Empty; - string link = stringNode.Attributes?["detailUrl"]?.Value ?? string.Empty; - - if (!int.TryParse(configNode.Attributes?["requirePlaySeconds"]?.Value, out int requiredPlaySeconds)) { - requiredPlaySeconds = 0; - } - - AttendGift.Require? giftRequirement = null; - if (!string.IsNullOrEmpty(value3)) { - value3Xml.LoadXml(value3); - if (value3Xml.FirstChild is { Name: "ms2" }) { - XmlNode? requirementNode = value3Xml.FirstChild.SelectSingleNode("require"); - if (requirementNode != null) { - if (!Enum.TryParse(requirementNode.Attributes?["type"]?.Value, true, out AttendGiftRequirement requirement)) { - requirement = AttendGiftRequirement.None; - } - - if (!int.TryParse(requirementNode.Attributes?["value1"]?.Value, out int requirementValue1)) { - requirementValue1 = 0; - } - - if (!int.TryParse(requirementNode.Attributes?["value2"]?.Value, out int requirementValue2)) { - requirementValue2 = 0; - } - - giftRequirement = new AttendGift.Require( - Type: requirement, - Value1: requirementValue1, - Value2: requirementValue2); - } - } - } - - return new AttendGift( - Items: rewards.ToArray(), - Name: name, - MailTitle: mailTitle, - MailContent: mailContent, - Link: link, - RequiredPlaySeconds: requiredPlaySeconds, - Requirement: giftRequirement); - case GameEventType.ReturnUser: - var requiredTime = DateTimeOffset.MinValue; - int daysInactive = 0; - if (DateTime.TryParseExact(value1, "yyyy-MM-dd-HH-mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime returnUserTime)) { - requiredTime = new DateTimeOffset(returnUserTime); - } else if (int.TryParse(value1, out daysInactive)) { - } - - return new ReturnUser( - SeasonId: int.TryParse(value3, out int season) ? season : 0, - DateInactiveSince: requiredTime, - DaysInactive: daysInactive, - QuestIds: string.IsNullOrEmpty(value4) ? [] : value4.Split(',').Select(int.Parse).ToArray(), - RequiredLevel: int.TryParse(value1, out int levelRequirement) ? levelRequirement : 0, - RequiredUserValue: int.TryParse(value2, out int userValue) ? userValue : 0); - case GameEventType.NewUser: - var requiredNewUserTime = DateTimeOffset.MinValue; - if (DateTime.TryParseExact(value1, "yyyy-MM-dd-HH-mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime newUserTime)) { - requiredNewUserTime = new DateTimeOffset(newUserTime); - } - - return new NewUser( - SeasonId: int.TryParse(value2, out int newUserSeason) ? newUserSeason : 0, - DateCreatedBy: requiredNewUserTime); - case GameEventType.ReturnUserCandidate: - var unknownTime = DateTimeOffset.MinValue; - if (DateTime.TryParseExact(value4, "yyyy-MM-dd-HH-mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime requiredDateTime)) { - unknownTime = new DateTimeOffset(requiredDateTime); - } - return new ReturnUserCandidate( - Season: int.TryParse(value3, out int returnUserCandidateSeason) ? returnUserCandidateSeason : 0, - SeasonId: int.TryParse(value2, out int returnUserCandidateSeasonId) ? returnUserCandidateSeasonId : 0, - MinLevel: int.TryParse(value1, out int returnUserCandidateMinLevel) ? returnUserCandidateMinLevel : 0, - UnknownDate: unknownTime); - case GameEventType.ActiveUser: - int[] value1Values = value1.Split(',').Select(int.Parse).ToArray(); - - value2Xml.LoadXml(value2); - int meret = 0; - if (value2Xml.FirstChild is { Name: "money" }) { - foreach (XmlNode childNode in value2Xml.FirstChild.ChildNodes) { - if (childNode.Name == "v") { - string valueType = childNode.Attributes?["type"]?.Value ?? string.Empty; - if (valueType == "merat_e") { - string? amountStr = childNode.Attributes?["amount"]?.Value; - if (!int.TryParse(amountStr, out int meratEAmount)) { - Console.WriteLine($"Failed to parse merat_e amount: {amountStr} for event type {type}"); - } else { - meret = meratEAmount; - } - } - } - } - } - return new ActiveUser( - MailId: value1Values[0], - MailDaysExpire: value1Values[1], - Meret: meret, - MinLevel: int.TryParse(value3, out int minLevel) ? minLevel : 0); - case GameEventType.RPS: - value1Xml = new XmlDocument(); - value1Xml.LoadXml(value1); - if (value1Xml.FirstChild is not { Name: "ms2" }) { - return null; - } - - XmlNode? rpseventNode = value1Xml.FirstChild.SelectSingleNode("rps_event"); - if (rpseventNode == null) { - return null; - } - - int ticketId = 0; - var rpsRewards = new List(); - foreach (XmlNode childNode in rpseventNode) { - if (childNode.Name == "gameTicket" && int.TryParse(childNode.Attributes?["itemID"]?.Value, out int itemId)) { - ticketId = itemId; - } - - if (childNode.Name == "rewardData") { - if (!int.TryParse(childNode.Attributes?["condPlayCount"]?.Value, out int playCount)) { - playCount = 1; - } - - // items - var rpsItems = new List(); - foreach (XmlNode itemNode in childNode.ChildNodes) { - foreach (XmlNode valueNode in itemNode.ChildNodes) { - if (!int.TryParse(valueNode.Attributes?["itemID"]?.Value, out int rpsRewardItemId)) { - rpsRewardItemId = 0; - } - - if (!short.TryParse(valueNode.Attributes?["grade"]?.Value, out short rpsRewardGrade)) { - rpsRewardGrade = 1; - } - - if (!int.TryParse(valueNode.Attributes?["count"]?.Value, out int rpsRewardCount)) { - rpsRewardCount = 1; - } - - rpsItems.Add(new RewardItem(rpsRewardItemId, rpsRewardGrade, rpsRewardCount)); - } - } - - rpsRewards.Add(new Rps.RewardData( - PlayCount: playCount, - Rewards: rpsItems.ToArray())); - } - } - - return new Rps( - GameTicketId: ticketId, - Rewards: rpsRewards.ToArray(), - ActionsHtml: value2); - case GameEventType.LoginNotice: - return new LoginNotice(); - case GameEventType.FieldEffect: - return new FieldEffect( - MapIds: value1.Split(',').Select(int.Parse).ToArray(), - Effect: value2); - case GameEventType.DTReward: - string[] itemStrings = value1.Split(';'); - - List items = []; - foreach (string itemString in itemStrings) { - int[] itemData = itemString.Split(',').Select(int.Parse).ToArray(); - items.Add(new DTReward.Entry( - StartDuration: itemData[0], - EndDuration: itemData[1], - MailContentId: itemData[2], - Item: new RewardItem( - itemId: itemData[3], - amount: itemData[4], - rarity: (short) itemData[5]))); - } - - return new DTReward( - Entries: items.ToArray()); - case GameEventType.ConstructShowItem: - return new ConstructShowItem( - CategoryId: int.TryParse(value1, out int categoryId) ? categoryId : 0, - CategoryName: value2, - ItemIds: value4.Split(',').Select(int.Parse).ToArray()); - case GameEventType.MassiveConstructionEvent: - return new MassiveConstructionEvent( - MapIds: value1.Split(',').Select(int.Parse).ToArray()); - case GameEventType.UGCMapContractSale: - return new UGCMapContractSale( - DiscountAmount: int.TryParse(value1, out int contractSaleAmount) ? contractSaleAmount : 0); - case GameEventType.UGCMapExtensionSale: - return new UGCMapExtensionSale( - DiscountAmount: int.TryParse(value1, out int extensionSaleAmount) ? extensionSaleAmount : 0); - case GameEventType.Gallery: - value1Xml.LoadXml(value1); - if (value1Xml.FirstChild is not { Name: "cards" }) { - return null; - } - - var questIds = new List(); - foreach (XmlNode valueNode in value1Xml.FirstChild) { - if (!int.TryParse(valueNode.Attributes?["quest"]?.Value, out int questId)) { - continue; - } - questIds.Add(questId); - } - - value2Xml.LoadXml(value2); - if (value2Xml.FirstChild is not { Name: "items" }) { - return null; - } - - var galleryRewards = new List(); - foreach (XmlNode itemNode in value2Xml.FirstChild) { - if (!int.TryParse(itemNode.Attributes?["itemID"]?.Value, out int itemId)) { - continue; - } - - if (!short.TryParse(itemNode.Attributes?["grade"]?.Value, out short grade)) { - grade = 1; - } - - if (!int.TryParse(itemNode.Attributes?["count"]?.Value, out int count)) { - count = 1; - } - - galleryRewards.Add(new RewardItem(itemId, grade, count)); - } - return new Gallery( - QuestIds: questIds.ToArray(), - RewardItems: galleryRewards.ToArray(), - RevealDayLimit: int.TryParse(value3, out int revealDayLimit) ? revealDayLimit : 1, - Image: value4); - case GameEventType.BingoEvent: - value1Xml.LoadXml(value1); - if (value1Xml.FirstChild is not { Name: "ms2" }) { - return null; - } - - var numbers = new List(); - foreach (XmlNode childNode in value1Xml.FirstChild.ChildNodes) { - if (childNode.Name == "number") { - int[] dayNumbers = childNode.Attributes?["value"]?.Value.Split(',').Select(int.Parse).ToArray() ?? []; - numbers.Add(dayNumbers); - } - } - - - value2Xml.LoadXml(value2); - if (value2Xml.FirstChild is not { Name: "ms2" }) { - return null; - } - - - var bingoRewards = new List(); - foreach (XmlNode childNode in value2Xml.FirstChild.ChildNodes) { - if (childNode.Name == "reward") { - var bingoItems = new List(); - foreach (XmlNode itemNode in childNode.ChildNodes) { - if (!int.TryParse(itemNode.Attributes?["itemID"]?.Value, out int itemId)) { - continue; - } - - if (!short.TryParse(itemNode.Attributes?["grade"]?.Value, out short grade)) { - grade = 1; - } - - if (!int.TryParse(itemNode.Attributes?["count"]?.Value, out int count)) { - count = 1; - } - - bingoItems.Add(new RewardItem(itemId, grade, count)); - } - bingoRewards.Add(new BingoEvent.BingoReward( - Items: bingoItems.ToArray())); - } - } - - int pencilItemId = int.TryParse(value3, out int pencilId) ? pencilId : 0; - int pencilPlusItemId = int.TryParse(value4, out int plusPencilId) ? plusPencilId : 0; - return new BingoEvent( - Numbers: numbers.ToArray(), - Rewards: bingoRewards.ToArray(), - PencilItemId: pencilItemId, - PencilPlusItemId: pencilPlusItemId); - case GameEventType.TimeRunEvent: - value1Xml.LoadXml(value1); - if (value1Xml.FirstChild is not { Name: "ms" }) { - return null; - } - - var quests = new List(); - foreach (XmlNode childNode in value1Xml.FirstChild.ChildNodes) { - if (childNode.Name == "quest") { - if (!int.TryParse(childNode.Attributes?["questID"]?.Value, out int questId)) { - continue; - } - if (!int.TryParse(childNode.Attributes?["distance"]?.Value, out int distance)) { - continue; - } - if (!int.TryParse(childNode.Attributes?["openingDay"]?.Value, out int openingDay)) { - continue; - } - quests.Add(new TimeRunEvent.Quest( - Id: questId, - Distance: distance, - OpeningDay: openingDay)); - } - } - - value2Xml.LoadXml(value2); - if (value2Xml.FirstChild is not { Name: "ms" }) { - return null; - } - XmlNode? rewardNode = value2Xml.FirstChild.FirstChild; - if (rewardNode == null) { - return null; - } - - if (!int.TryParse(rewardNode.Attributes?["itemID"]?.Value, out int timerunEventItemId)) { - return null; - } - if (!int.TryParse(rewardNode.Attributes?["count"]?.Value, out int timerunEventItemCount)) { - return null; - } - if (!short.TryParse(rewardNode.Attributes?["grade"]?.Value, out short timerunEventItemGrade)) { - return null; - } - - if (!int.TryParse(value3, out int startTimeRunEventItemId)) { - return null; - } - return new TimeRunEvent( - StartItemId: startTimeRunEventItemId, - Quests: quests.ToArray(), - StepRewards: new Dictionary(), // No step rewards were added in the metadata. - FinalReward: new RewardItem( - itemId: timerunEventItemId, - amount: timerunEventItemCount, - rarity: timerunEventItemGrade)); - case GameEventType.MapleSurvivalOpenPeriod: - return new MapleSurvivalOpenPeriod(); - case GameEventType.ShutdownMapleSurvival: - return new ShutdownMapleSurvival(); - case GameEventType.SaleAutoPlayInstrument: - if (!int.TryParse(value1, out int performanceDiscount) && string.IsNullOrEmpty(value2)) { - return null; - } - return new SaleAutoPlayInstrument( - Discount: performanceDiscount, - ContentType: value2); - case GameEventType.SaleAutoFishing: - if (!int.TryParse(value1, out int fishingDiscount) && string.IsNullOrEmpty(value2)) { - return null; - } - return new SaleAutoFishing( - Discount: fishingDiscount, - ContentType: value2); - default: - return null; - } - } - - private OxQuizTable ParseOxQuizTable() { - var results = new Dictionary(); - foreach ((int id, OxQuiz quiz) in parser.ParseOxQuiz()) { - results.Add(id, new OxQuizTable.Entry( - Id: quiz.quizID, - CategoryId: quiz.categoryID, - Category: quiz.categoryStr, - Question: quiz.quizStr, - Level: quiz.level, - IsTrue: quiz.answer, - Answer: quiz.answerStr)); - } - return new OxQuizTable(results); - } - - private ItemMergeTable ParseItemMergeOptionTable() { - var results = new Dictionary>(); - foreach ((int id, MergeOption mergeOption) in parser.ParseItemMergeOption()) { - var slots = new Dictionary(); - foreach (MergeOption.Slot slotEntry in mergeOption.slot) { - var ingredients = new List(); - - ItemComponent? ingredient1 = ParseItemMaterial(slotEntry.itemMaterial1); - if (ingredient1 != null) { - ingredients.Add(ingredient1); - } - ItemComponent? ingredient2 = ParseItemMaterial(slotEntry.itemMaterial2); - if (ingredient2 != null) { - ingredients.Add(ingredient2); - } - - var basicOptions = new Dictionary(); - var specialOptions = new Dictionary(); - - foreach (MergeOption.Option mergeOptionEntry in slotEntry.option) { - if (mergeOptionEntry.optionName is "str" or "dex" or "int" or "luk" or "hp" or "hp_rgp" or "hp_inv" or "sp" or "sp_rgp" or "sp_inv" or "ep" or "ep_rgp" or "ep_inv" or "asp" or "msp" or "atp" or "evp" or - "cap" or "cad" or "car" or "ndd" or "abp" or "jmp" or "pap" or "map" or "par" or "mar" or "wapmin" or "wapmax" or "dmg" or "pen" or "rmsp" or "bap" or "bap_pet") { - var basicAttribute = mergeOptionEntry.optionName.ToBasicAttribute(); - List> values = []; - List> rates = []; - List weights = []; - int min = mergeOptionEntry.min; - if (basicAttribute is BasicAttribute.Piercing or BasicAttribute.PerfectGuard or - BasicAttribute.JumpHeight) { - // Looping by 10 because that's the max amount of values in the xml - for (int i = 0; i < 10; i++) { - (int value, int weight) = mergeOptionEntry[i]; - if (value == 0) { - continue; - } - rates.Add(new ItemMergeTable.Range(min + 1, value)); - values.Add(new ItemMergeTable.Range(0, 0)); - weights.Add(weight); - min = value; - } - } else { - for (int i = 0; i < 10; i++) { - (int value, int weight) = mergeOptionEntry[i]; - if (value == 0) { - continue; - } - values.Add(new ItemMergeTable.Range(min + 1, value)); - rates.Add(new ItemMergeTable.Range(0, 0)); - weights.Add(weight); - min = value; - } - } - - basicOptions[basicAttribute] = new ItemMergeTable.Option( - Values: values.ToArray(), - Rates: rates.ToArray(), - Weights: weights.ToArray()); - } else { - var specialAttribute = mergeOptionEntry.optionName.ToSpecialAttribute(); - List> values = []; - List> rates = []; - List weights = []; - int min = mergeOptionEntry.min; - if (specialAttribute is SpecialAttribute.HpOnKill or SpecialAttribute.ReduceCooldown or SpecialAttribute.ReduceKnockBack or SpecialAttribute.MassiveOxSpeed or SpecialAttribute.MassiveTrapMasterSpeed or - SpecialAttribute.MassiveFinalSurvivalSpeed or SpecialAttribute.MassiveCrazyRunnerSpeed or SpecialAttribute.MassiveShCrazyRunnerSpeed or SpecialAttribute.MassiveEscapeSpeed or SpecialAttribute.MassiveSpringBeachSpeed or - SpecialAttribute.MassiveDanceDanceSpeed or SpecialAttribute.DarkStreamEvp or SpecialAttribute.CompleteFieldMissionSpeed or SpecialAttribute.AdditionalEffect95000018 or SpecialAttribute.AdditionalEffect95000012 or - SpecialAttribute.AdditionalEffect95000014 or SpecialAttribute.AdditionalEffect95000020 or SpecialAttribute.AdditionalEffect95000021 or SpecialAttribute.AdditionalEffect95000022 or SpecialAttribute.AdditionalEffect95000023 - or SpecialAttribute.AdditionalEffect95000024 or SpecialAttribute.AdditionalEffect95000025 or SpecialAttribute.AdditionalEffect95000026 or SpecialAttribute.AdditionalEffect95000027 or SpecialAttribute.AdditionalEffect95000028 or - SpecialAttribute.AdditionalEffect95000029 or SpecialAttribute.DashDistance or SpecialAttribute.SpiritOnKill or SpecialAttribute.StaminaOnKill or SpecialAttribute.PvpDamage or SpecialAttribute.ReducePvpDamage or SpecialAttribute.SkillLevelUpTier1 - or SpecialAttribute.SkillLevelUpTier2 or SpecialAttribute.SkillLevelUpTier3 or SpecialAttribute.SkillLevelUpTier4 or SpecialAttribute.SkillLevelUpTier5 or SpecialAttribute.SkillLevelUpTier6 or SpecialAttribute.SkillLevelUpTier7 or SpecialAttribute.SkillLevelUpTier8 - or SpecialAttribute.SkillLevelUpTier9 or SpecialAttribute.SkillLevelUpTier10 or SpecialAttribute.SkillLevelUpTier11 or SpecialAttribute.SkillLevelUpTier12 or SpecialAttribute.SkillLevelUpTier13 or SpecialAttribute.SkillLevelUpTier14 or SpecialAttribute.ChaosRaidAttackSpeed - or SpecialAttribute.ChaosRaidAccuracy or SpecialAttribute.ChaosRaidHp or SpecialAttribute.PetTrapReward) { - for (int i = 0; i < 10; i++) { - (int value, int weight) = mergeOptionEntry[i]; - if (value == 0) { - continue; - } - values.Add(new ItemMergeTable.Range(min + 1, value)); - rates.Add(new ItemMergeTable.Range(0, 0)); - weights.Add(weight); - min = value; - } - } else { - for (int i = 0; i < 10; i++) { - (int value, int weight) = mergeOptionEntry[i]; - if (value == 0) { - continue; - } - rates.Add(new ItemMergeTable.Range(min + 1, value)); - values.Add(new ItemMergeTable.Range(0, 0)); - weights.Add(weight); - min = value; - } - } - - specialOptions[specialAttribute] = new ItemMergeTable.Option( - Values: values.ToArray(), - Rates: rates.ToArray(), - Weights: weights.ToArray()); - } - } - var slot = new ItemMergeTable.Entry( - Slot: slotEntry.part, - MesoCost: slotEntry.consumeMeso, - Materials: ingredients.ToArray(), - BasicOptions: basicOptions, - SpecialOptions: specialOptions - ); - - slots.Add(slotEntry.part, slot); - } - results.Add(id, slots); - } - // Hardcoding values seeing as the missing ids here are utilizing table id 37000055 - for (int i = 37000056; i < 37000064; i++) { - if (results.TryGetValue(37000055, out Dictionary? dictionary)) { - results.Add(i, dictionary); - } - } - return new ItemMergeTable(results); - - ItemComponent? ParseItemMaterial(string[] itemMaterial) { - var tag = ItemTag.None; - if (itemMaterial.Length > 0) { - string[] item = itemMaterial[0].Split(':'); - if (item.Length == 2) { - tag = Enum.TryParse(item[1], out ItemTag itemTag) ? itemTag : ItemTag.None; - } - int itemId = int.TryParse(item[0], out int id) ? id : 0; - int rarity = int.TryParse(itemMaterial[1], out int r) ? r : 1; - int amount = int.TryParse(itemMaterial[2], out int a) ? a : 1; - if (tag != ItemTag.None || itemId > 0) { - return new ItemComponent(itemId, rarity, amount, tag); - } - } - return null; - } - } - - private ShopTable ParseShop() { - var results = new Dictionary(); - foreach ((int shopId, ShopGameInfo shopInfo) in parser.ParseShopGameInfo()) { - var entry = new ShopMetadata( - Id: shopInfo.shopID, - CategoryId: shopInfo.categoryID, - Name: shopInfo.iconName, - FrameType: (ShopFrameType) shopInfo.uiFrameType, - DisplayOnlyUsable: shopInfo.showOnlyUsableItem, - HideStats: shopInfo.hideOptionInfo, - DisplayProbability: shopInfo.showProbInfo, - IsOnlySell: shopInfo.isOnlySell, - OpenWallet: shopInfo.isOpenTokenPocket, - DisplayNew: false, // this isn't present in the table - DisableDisplayOrderSort: shopInfo.disableDisplayOrderSort, - RestockTime: string.IsNullOrEmpty(shopInfo.resetFixedTime) ? 0 : DateTime.ParseExact(shopInfo.resetFixedTime, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds(), - EnableReset: shopInfo.resetEnable, - RestockData: new ShopRestockData( - ResetType: (ResetType) shopInfo.resetType, - CurrencyType: (ShopCurrencyType) shopInfo.resetPaymentType, - ExcessCurrencyType: (ShopCurrencyType) shopInfo.resetPaymentType, // not present in the table, using resetPaymentType for now - MinItemCount: shopInfo.resetListMin, - MaxItemCount: shopInfo.resetListMax, - Price: shopInfo.resetPrice, - EnablePriceMultiplier: false, // not present in the table - DisableInstantRestock: shopInfo.resetButtonHide, - AccountWide: shopInfo.resetByAccount) - ); - results.Add(shopId, entry); - } - return new ShopTable(results); - } - - private ShopItemTable ParseShopItems() { - var results = new Dictionary>(); - foreach ((int shopId, ShopGame data) in parser.ParseShopGame()) { - var shopResults = new Dictionary(); - foreach (ShopGame.Item item in data.item) { - // Check if item exists in ItemMetadataById - if (!ItemMapper.ItemMetadataById.TryGetValue(item.id, out ItemMetadata? itemMeta)) { - continue; - } - - string[] achievementArray = string.IsNullOrEmpty(item.requireAchieve) ? [] : item.requireAchieve.Split(","); - int achievementId = 0; - int achievementRank = 0; - if (achievementArray.Length == 2) { - if (!int.TryParse(achievementArray[0], out achievementId)) { - achievementId = 0; - } - if (!int.TryParse(achievementArray[1], out achievementRank)) { - achievementRank = 1; - } - } - - byte championshipRank = 0; - short championShipJoinCount = 0; - if (item.requireChampionshipInfo.Length == 2) { - championshipRank = (byte) item.requireChampionshipInfo[0]; - championShipJoinCount = (short) item.requireChampionshipInfo[1]; - } - - var npcType = GuildNpcType.Unknown; - short guildNpcLevel = 0; - if (item.requireGuildNpc.Length == 2) { - npcType = item.requireGuildNpc[0] switch { - "goods" => GuildNpcType.Goods, - "equip" => GuildNpcType.Equip, - "gemstone" => GuildNpcType.Gemstone, - "itemMerge" => GuildNpcType.ItemMerge, - "music" => GuildNpcType.Music, - "quest" => GuildNpcType.Quest, - _ => GuildNpcType.Unknown, - }; - if (short.TryParse(item.requireGuildNpc[1], out short level)) { - guildNpcLevel = level; - } - } - - RestrictedBuyData? restrictedBuyData = null; - if (!string.IsNullOrEmpty(item.startDate) && !string.IsNullOrEmpty(item.endDate)) { - var buyTimeOfDays = new List(); - foreach (string partTime in item.partTime) { - (TimeSpan startPartTime, TimeSpan endPartTime) = ParsePartTime(partTime); - buyTimeOfDays.Add(new BuyTimeOfDay(startPartTime.Seconds, endPartTime.Seconds)); - } - restrictedBuyData = new RestrictedBuyData { - Days = item.dayOfWeek.Length == 0 ? [] : Array.ConvertAll(item.dayOfWeek, day => (ShopBuyDay) day), - TimeRanges = buyTimeOfDays, - StartTime = string.IsNullOrEmpty(item.startDate) ? 0 : DateTime.ParseExact(item.startDate, "yyyy-MM-dd-HH-mm", CultureInfo.InvariantCulture).ToEpochSeconds(), - EndTime = string.IsNullOrEmpty(item.endDate) ? 0 : DateTime.ParseExact(item.endDate, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds(), - }; - } - - var entry = new ShopItemMetadata( - Id: item.sn, - ShopId: shopId, - ItemId: item.id, - Rarity: (byte) item.grade, - Cost: new ShopCost { - Amount = (int) item.price, - ItemId = item.paymentItemID, - SaleAmount = 0, // ? - Type = (ShopCurrencyType) item.paymentType, - }, - SellCount: item.sellCount, - Category: item.category, - Requirements: new ShopItemMetadata.Requirement( - GuildTrophy: item.requireGuildTrophy, - Achievement: new ShopItemMetadata.Achievement( - Id: achievementId, - Rank: achievementRank), - Championship: new ShopItemMetadata.Championship( - Rank: championshipRank, - JoinCount: championShipJoinCount), - GuildNpc: new ShopItemMetadata.GuildNpc( - Type: npcType, - Level: guildNpcLevel), - QuestAlliance: new ShopItemMetadata.QuestAlliance( - Type: item.requireAlliance switch { - "MapleUnion" => ReputationType.MapleAlliance, - "TriaRoyalGuard" => ReputationType.RoyalGuard, - "DarkWind" => ReputationType.DarkWind, - "GreenHood" => ReputationType.GreenHood, - "LumiKnight" => ReputationType.Lumiknight, - "MapleUnion_KritiasExped" => ReputationType.KritiasMapleAlliance, - "GreenHood_KritiasExped" => ReputationType.KritiasGreenHood, - "Lumiknight_KritiasExped" => ReputationType.KritiasLumiknight, - "Georg" => ReputationType.Humanitas, - _ => ReputationType.None, - }, - Grade: item.requireAllianceGrade)), - RestrictedBuyData: restrictedBuyData, - SellUnit: (short) item.sellUnit, - Label: (ShopItemLabel) item.frameType, - IconTag: item.paymentIconTag, - WearForPreview: item.wearForPreview, - RandomOption: item.randomOption, - Probability: item.prob, - IsPremiumItem: item.premiumItem); - shopResults.Add(item.sn, entry); - } - results.Add(shopId, shopResults); - } - return new ShopItemTable(results); - } - - private BeautyShopTable ParseBeautyShops() { - var results = new Dictionary(); - results = MergeBeautyShopData(results, parser.ParseShopBeauty()); - results = MergeBeautyShopData(results, parser.ParseShopBeautyCoupon()); - results = MergeBeautyShopData(results, parser.ParseShopBeautySpecialHair()); - return new BeautyShopTable(results); - } - - private Dictionary MergeBeautyShopData(Dictionary entries, IEnumerable<(int, ShopBeauty)> beautyParser) { - foreach ((int shopId, ShopBeauty shop) in beautyParser) { - List itemGroups = []; - foreach (ShopBeauty.ItemGroup group in shop.itemGroup) { - itemGroups.Add(new BeautyShopItemGroup( - StartTime: string.IsNullOrEmpty(group.saleStartTime) ? 0 : DateTime.ParseExact(group.saleStartTime, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds(), - Items: ParseBeautyShopItems(group.item).ToArray())); - } - - entries.Add(shopId, new BeautyShopMetadata( - Id: shop.shopID, - Category: (BeautyShopCategory) shop.categoryID, - SubType: shop.shopID switch { - // Hardcoding this because I'm not sure where this information is located in the xmls - 500 => 16, - 501 => 19, - 504 => 17, - 505 => 28, - 506 => 18, - 508 => 21, - 509 => 0, - 510 => 20, - _ => 0, - }, - StyleCostMetadata: new BeautyShopCostMetadata( - CurrencyType: (ShopCurrencyType) shop.stylePaymentType, - Price: shop.stylePrice, - Icon: shop.stylePaymentIconTag, - PaymentItemId: shop.stylePaymentItemID), - ColorCostMetadata: new BeautyShopCostMetadata( - CurrencyType: (ShopCurrencyType) shop.colorPaymentType, - Price: shop.colorPrice, - Icon: shop.colorPaymentIconTag, - PaymentItemId: shop.colorPaymentItemID), - IsRandom: shop.random, - IsByItem: shop.byItem, - ReturnCouponId: shop.returnCouponID, - CouponId: shop.displayCouponID, - CouponTag: Enum.TryParse(shop.couponTag, out ItemTag tag) ? tag : ItemTag.None, - Items: ParseBeautyShopItems(shop.item).ToArray(), - ItemGroups: itemGroups.ToArray())); - } - return entries; - - IEnumerable ParseBeautyShopItems(IList items) { - foreach (ShopBeauty.Item item in items) { - yield return new BeautyShopItem( - Id: item.id, - Cost: new BeautyShopCostMetadata( - CurrencyType: (ShopCurrencyType) item.paymentType, - Price: item.price, - Icon: item.paymentIconTag, - PaymentItemId: item.paymentItemID), - Weight: item.weight, - AchievementId: item.achieveID, - AchievementRank: (byte) item.achieveGrade, - RequiredLevel: item.requireLevel, - SaleTag: (ShopItemLabel) item.saleTag); - } - } - } - - private MeretMarketTable ParseMeretCustomShop() { - var results = new Dictionary(); - foreach ((int id, ShopMeretCustom entry) in parser.ParseShopMeretCustom()) { - foreach (ShopMeretCustom addQuantity in entry.additionalQuantity) { - results.Add(addQuantity.id, ParseMarketItemMetadata(addQuantity, entry)); - } - results.Add(id, ParseMarketItemMetadata(entry)); - } - return new MeretMarketTable(results); - - MeretMarketItemMetadata ParseMarketItemMetadata(ShopMeretCustom item, ShopMeretCustom? parent = null) { - string saleStartTime = string.IsNullOrEmpty(parent?.saleStartTime) ? item.saleStartTime : parent.saleStartTime; - string saleEndTime = string.IsNullOrEmpty(parent?.saleEndTime) ? item.saleEndTime : parent.saleEndTime; - string promoStartTime = string.IsNullOrEmpty(parent?.promoSaleStartTime) ? item.promoSaleStartTime : parent.promoSaleStartTime; - string promoEndTime = string.IsNullOrEmpty(parent?.promoSaleEndTime) ? item.promoSaleEndTime : parent.promoSaleEndTime; - int[] jobRequirement = parent?.jobRequire ?? item.jobRequire; - return new MeretMarketItemMetadata( - Id: item.id, - ParentId: parent?.id ?? 0, - TabId: parent?.tabID ?? item.tabID, - Banner: item.banner, - BannerTag: (MeretMarketBannerTag) item.bannerTag, - ItemId: parent?.itemID ?? item.itemID, - Rarity: (byte) (parent?.grade ?? item.grade), - Quantity: item.quantity, - BonusQuantity: item.bonusQuantity, - DurationInDays: item.durationDay, - SaleTag: (MeretMarketItemSaleTag) item.saleTag, - CurrencyType: (MeretMarketCurrencyType) (parent?.paymentType ?? item.paymentType), - Price: item.price, - SalePrice: item.salePrice == 0 ? item.price : item.salePrice, - SaleStartTime: string.IsNullOrEmpty(saleStartTime) ? 0 : DateTime.ParseExact(saleStartTime, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds(), - SaleEndTime: string.IsNullOrEmpty(saleEndTime) ? 0 : DateTime.ParseExact(saleEndTime, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds(), - JobRequirement: jobRequirement.Select(job => (JobCode) job).AsEnumerable().FilterFlags(), - RestockUnavailable: parent?.noRestock ?? item.noRestock, - RequireMinLevel: parent?.minLevel ?? item.minLevel, - RequireMaxLevel: parent?.maxLevel ?? item.maxLevel, - RequireAchievementId: parent?.achieveID ?? item.achieveID, - RequireAchievementRank: parent?.achieveGrade ?? item.achieveGrade, - PcCafe: parent?.pcCafe ?? item.pcCafe, - Giftable: parent?.giftable ?? item.giftable, - ShowSaleTime: item.showSaleTime, - PromoName: item.promoName, - PromoStartTime: string.IsNullOrEmpty(promoStartTime) ? 0 : DateTime.ParseExact(promoStartTime, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds(), - PromoEndTime: string.IsNullOrEmpty(promoEndTime) ? 0 : DateTime.ParseExact(promoEndTime, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds()); - } - } - - private FishTable ParseFish() { - // Parse Fish - var fishes = new Dictionary(); - foreach ((int id, Fish fish) in parser.ParseFish()) { - if (!Enum.TryParse(fish.habitat, out LiquidType liquidType)) { - liquidType = LiquidType.all; - } - - int[] smallSize = fish.smallSize.Split("-").Select(int.Parse).ToArray(); - int[] bigSize = fish.bigSize.Split("-").Select(int.Parse).ToArray(); - fishes.Add(id, new FishTable.Fish( - Id: id, - FluidHabitat: liquidType, - Mastery: fish.fishMastery, - Level: fish.lv, - Rarity: fish.rank, - PointCount: fish.pointCount, - MasteryExp: fish.masteryPoint, - Exp: fish.exp, - FishingTime: fish.fishingTime, - CatchProbability: fish.catchProp, - BaitProbability: fish.baitProp, - SmallSize: new FishTable.Range(smallSize[0], smallSize[1]), - BigSize: new FishTable.Range(bigSize[0], bigSize[1]), - BaitEffectIds: fish.bait, - IndividualDropBoxId: fish.individualDropBoxID, - IgnoreSpotMastery: fish.ignoreSpotMastery)); - } - - // Parse Spots - var spots = new Dictionary(); - foreach ((int mapId, FishingSpot spot) in parser.ParseFishingSpot()) { - var liquidTypes = new List(); - foreach (string liquidType in spot.liquidType) { - if (Enum.TryParse(liquidType, out LiquidType type)) { - liquidTypes.Add(type); - } - } - - spots.Add(mapId, new FishTable.Spot( - Id: mapId, - MinMastery: spot.minMastery, - MaxMastery: spot.maxMastery, - LiquidTypes: liquidTypes, - GlobalFishBoxId: spot.globalFishBoxID, - IndividualFishBoxId: spot.individualFishBoxID, - GlobalDropBoxId: spot.globalDropBoxId, - IndividualDropBoxId: spot.individualDropBoxId, - SpotLevel: spot.spotLevel, - DropRank: spot.spotDropRank ? 1 : 0)); // TODO: Change this from a bool to int - } - - // Parse Lure - var lures = new Dictionary(); - foreach ((int id, FishLure lure) in parser.ParseFishLure()) { - lures.Add(id, new FishTable.Lure( - BuffId: lure.fishCode, - BuffLevel: (short) lure.additionalEffectLevel, - Catches: lure.catchRank.Select((t, i) => new FishTable.Lure.Catch(Rank: t, Probability: lure.catchProp[i])).ToArray(), - Spawns: lure.spawnRank.Select((t, i) => new FishTable.Lure.Spawn(FishId: t, Rate: lure.spawnProp[i])).ToArray(), - GlobalDropBoxId: lure.globalDropBoxID, - GlobalDropRank: lure.globalDropRank, - IndividualDropBoxId: lure.individualDropBoxID, - IndividualDropRank: lure.individualDropRank)); - } - - // Global Fish Boxes - Dictionary globalBoxes = ParseFishBox(parser.ParseGlobalFishBox()); - Dictionary individualBoxes = ParseFishBox(parser.ParseIndividualFishBox()); - - return new FishTable(fishes, spots, lures, globalBoxes, individualBoxes); - - Dictionary ParseFishBox(IEnumerable<(int, FishBox)> boxes) { - var results = new Dictionary(); - foreach ((int id, FishBox box) in boxes) { - Dictionary fishes = []; - foreach (FishBox.Fish data in box.fish) { - fishes[data.fishCode] = data.weight; - } - - results.Add(id, new FishTable.FishBox( - Id: id, - Probability: box.probability, - CubeRate: box.cubeRate, - Fishes: fishes)); - } - return results; - } - } - - private CombineSpawnTable ParseCombineSpawn() { - var groupDict = new Dictionary>(); - var npcDict = new Dictionary>(); - var objectDict = new Dictionary>(); - - foreach ((int id, SpawnGroup spawnGroup) in parser.ParseSpawnGroup()) { - var groupMetadata = new SpawnGroupMetadata( - GroupId: spawnGroup.groupId, - Type: Enum.TryParse(spawnGroup.groupType, out CombineSpawnGroupType type) ? type : CombineSpawnGroupType.none, - TotalCount: spawnGroup.combineCount, - ResetTick: spawnGroup.resetTick, - MapId: spawnGroup.fieldId); - - if (!groupDict.TryGetValue(spawnGroup.fieldId, out Dictionary? value)) { - value = new Dictionary(); - groupDict[spawnGroup.fieldId] = value; - } - - value[spawnGroup.groupId] = groupMetadata; - } - - foreach ((int id, SpawnNpc spawn) in parser.ParseSpawnNpc()) { - var spawnMetadata = new SpawnNpcMetadata( - CombineId: spawn.combineId, - GroupId: spawn.groupId, - Weight: spawn.weight, - SpawnId: spawn.spawnId); - - if (!npcDict.TryGetValue(spawn.groupId, out Dictionary? value)) { - value = new Dictionary(); - npcDict[spawn.groupId] = value; - } - - value[spawn.combineId] = spawnMetadata; - } - - foreach ((int id, SpawnInteractObject interactObject) in parser.ParseSpawnInteractObject()) { - var objectMetadata = new SpawnInteractObjectMetadata( - CombineId: interactObject.combineId, - GroupId: interactObject.groupId, - Weight: interactObject.weight, - RegionSpawnId: interactObject.regionSpawnId, - InteractId: interactObject.interactId, - Model: interactObject.model, - Asset: interactObject.asset, - Normal: interactObject.normal, - Reactable: interactObject.reactable, - Scale: interactObject.scale, - KeepAnimate: interactObject.isKeepAnimate); - - if (!objectDict.TryGetValue(interactObject.groupId, out Dictionary? value)) { - value = new Dictionary(); - objectDict[interactObject.groupId] = value; - } - - value[interactObject.combineId] = objectMetadata; - } - - return new CombineSpawnTable(groupDict, npcDict, objectDict); - } - - private EnchantOptionTable ParseEnchantOption() { - var results = new Dictionary(); - foreach ((int id, EnchantOption enchantOption) in parser.ParseEnchantOption()) { - IList basicAttributes = []; - foreach (int option in enchantOption.option) { - if (!Enum.TryParse(option.ToString(), out BasicAttribute basicAttribute)) { - Console.WriteLine($"Failed to parse basic attribute {option}"); - continue; - } - basicAttributes.Add(basicAttribute); - } - results.Add(id, new EnchantOptionMetadata( - Id: id, - Slot: enchantOption.slot, - EnchantLevel: enchantOption.grade, - Rarity: (short) enchantOption.rank, - Rate: enchantOption.rate, - MinLevel: enchantOption.minLv, - MaxLevel: enchantOption.maxLv, - Attributes: basicAttributes.ToArray())); - } - return new EnchantOptionTable(results); - } - - private ScriptEventConditionTable ParseScriptEventConditionTable() { - var results = new Dictionary>(); - - foreach ((int eventId, ScriptEventCondition scriptEventCondition) in parser.ParseScriptEventCondition()) { - ScriptEventType type = scriptEventCondition.type switch { - Parser.Enum.ScriptEventType.enchant_fail => ScriptEventType.EnchantFail, - Parser.Enum.ScriptEventType.enchant_item_select => ScriptEventType.EnchantSelect, - Parser.Enum.ScriptEventType.enchant_complete => ScriptEventType.EnchantComplete, - Parser.Enum.ScriptEventType.merge_select => ScriptEventType.EmpowerSelect, - Parser.Enum.ScriptEventType.merge_try => ScriptEventType.EmpowerTry, - Parser.Enum.ScriptEventType.merge_result => ScriptEventType.EmpowerResult, - Parser.Enum.ScriptEventType.remake_fail => ScriptEventType.RerollFail, - Parser.Enum.ScriptEventType.remake_item_select => ScriptEventType.RerollItemSelect, - Parser.Enum.ScriptEventType.remake_option_select => ScriptEventType.RerollOptionSelect, - Parser.Enum.ScriptEventType.remake_complete => ScriptEventType.RerollComplete, - _ => ScriptEventType.EnchantFail, - }; - - List enchantLevels = []; - if (scriptEventCondition.enchantLevel.Contains('-')) { - string[] enchantLevelSplit = scriptEventCondition.enchantLevel.Split('-'); - int startInt = int.TryParse(enchantLevelSplit[0], out int start) ? start : 0; - int endInt = int.TryParse(enchantLevelSplit[1], out int end) ? end : 0; - - for (int i = startInt; i <= endInt; i++) { - enchantLevels.Add(i); - } - } else { - if (!int.TryParse(scriptEventCondition.enchantLevel, out int enchantLevel)) { - if (scriptEventCondition.enchantLevel == "MAX") { - enchantLevels.Add(15); - } - } else { - enchantLevels.Add(enchantLevel); - } - } - - if (!results.TryGetValue(type, out Dictionary? value)) { - value = new Dictionary(); - results[type] = value; - } - - value[scriptEventCondition.id] = new ScriptEventConditionMetadata( - Id: scriptEventCondition.id, - EventType: type, - ErrorCode: (ItemEnchantError) scriptEventCondition.enchantError, - Rarity: (short) scriptEventCondition.rank, - EnchantLevel: enchantLevels.ToArray(), - FailCount: scriptEventCondition.failCount, - DamageType: (EnchantDamageType) scriptEventCondition.isDamaged, - ResultType: (EnchantResult) scriptEventCondition.result - ); - } - return new ScriptEventConditionTable(results); - } - - private UnlimitedEnchantOptionTable ParseUnlimitedEnchantOption() { - var results = new Dictionary>(); - foreach ((int slot, IDictionary enchantOptions) in parser.ParseUnlimitedEnchantOption()) { - var levelDictionary = new Dictionary(); - foreach ((int[] optionLevel, UnlimitedEnchantOption enchantOption) in enchantOptions) { - int minLevel = optionLevel[0]; - int maxLevel = optionLevel.Length > 1 ? optionLevel[1] : minLevel; - - for (int level = minLevel; level <= maxLevel; level++) { - Dictionary values = []; - Dictionary rates = []; - Dictionary specialValues = []; - Dictionary specialRates = []; - AddBasic(values, rates, (BasicAttribute) enchantOption.option1, enchantOption.value1, enchantOption.rate1); - AddBasic(values, rates, (BasicAttribute) enchantOption.option2, enchantOption.value2, enchantOption.rate2); - AddBasic(values, rates, (BasicAttribute) enchantOption.option3, enchantOption.value3, enchantOption.rate3); - AddBasic(values, rates, (BasicAttribute) enchantOption.option4, enchantOption.value4, enchantOption.rate4); - AddSpecial(specialValues, specialRates, (SpecialAttribute) enchantOption.sa_option1, enchantOption.sa_value1, enchantOption.sa_rate1); - AddSpecial(specialValues, specialRates, (SpecialAttribute) enchantOption.sa_option2, enchantOption.sa_value2, enchantOption.sa_rate2); - - levelDictionary[level] = new UnlimitedEnchantOptionTable.Option( - Values: values, - Rates: rates, - SpecialValues: specialValues, - SpecialRates: specialRates); - - } - } - results[slot] = levelDictionary; - } - return new UnlimitedEnchantOptionTable(results); - - void AddBasic(Dictionary values, Dictionary rates, BasicAttribute attribute, int value, float rate) { - if (value != 0) { - values.Add(attribute, value); - } - if (rate != 0) { - rates.Add(attribute, rate); - } - } - - void AddSpecial(Dictionary values, Dictionary rates, SpecialAttribute attribute, int value, float rate) { - if (attribute == SpecialAttribute.None) { - return; - } - if (value != 0) { - values.Add(attribute, value); - } - if (rate != 0) { - rates.Add(attribute, rate); - } - } - } -} +using System.Globalization; +using System.Xml; +using Maple2.Database.Extensions; +using Maple2.File.Ingest.Utils; +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Enum; +using Maple2.File.Parser.Xml.Table.Server; +using Maple2.Model; +using Maple2.Model.Common; +using Maple2.Model.Enum; +using Maple2.Model.Error; +using Maple2.Model.Game; +using Maple2.Model.Game.Shop; +using Maple2.Model.Metadata; +using DayOfWeek = System.DayOfWeek; +using ExpType = Maple2.Model.Enum.ExpType; +using Fish = Maple2.File.Parser.Xml.Table.Server.Fish; +using FishingSpot = Maple2.File.Parser.Xml.Table.Server.FishingSpot; +using GuildNpcType = Maple2.Model.Enum.GuildNpcType; +using IndividualItemDrop = Maple2.File.Parser.Xml.Table.Server.IndividualItemDrop; +using InstanceType = Maple2.Model.Enum.InstanceType; +using JobConditionTable = Maple2.Model.Metadata.JobConditionTable; +using MergeOption = Maple2.File.Parser.Xml.Table.Server.MergeOption; +using ScriptEventType = Maple2.Model.Enum.ScriptEventType; +using ScriptType = Maple2.Model.Enum.ScriptType; +using TimeEventType = Maple2.File.Parser.Enum.TimeEventType; + +namespace Maple2.File.Ingest.Mapper; + +public class ServerTableMapper : TypeMapper { + private readonly ServerTableParser parser; + + public ServerTableMapper(M2dReader xmlReader) { + parser = new ServerTableParser(xmlReader); + } + + protected override IEnumerable Map() { + yield return new ServerTableMetadata { + Name = ServerTableNames.INSTANCE_FIELD, + Table = ParseInstanceField(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.SCRIPT_CONDITION, + Table = ParseScriptCondition(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.SCRIPT_FUNCTION, + Table = ParseScriptFunction(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.SCRIPT_EVENT, + Table = ParseScriptEventConditionTable(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.JOB_CONDITION, + Table = ParseJobCondition(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.BONUS_GAME, + Table = ParseBonusGameTable(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.GLOBAL_DROP_ITEM_BOX, + Table = ParseGlobalItemDropTable(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.USER_STAT, + Table = ParseUserStat(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.INDIVIDUAL_DROP_ITEM, + Table = ParseIndividualItemDropTable(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.PRESTIGE_EXP, + Table = ParsePrestigeExpTable(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.PRESTIGE_ID_EXP, + Table = ParsePrestigeIdExpTable(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.TIME_EVENT, + Table = ParseTimeEventTable(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.GAME_EVENT, + Table = ParseGameEventTable(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.OX_QUIZ, + Table = ParseOxQuizTable(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.ITEM_MERGE, + Table = ParseItemMergeOptionTable(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.SHOP, + Table = ParseShop(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.SHOP_ITEM, + Table = ParseShopItems(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.BEAUTY_SHOP, + Table = ParseBeautyShops(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.MERET_MARKET, + Table = ParseMeretCustomShop(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.FISH, + Table = ParseFish(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.COMBINE_SPAWN, + Table = ParseCombineSpawn(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.ENCHANT_OPTION, + Table = ParseEnchantOption(), + }; + yield return new ServerTableMetadata { + Name = ServerTableNames.UNLIMITED_ENCHANT_OPTION, + Table = ParseUnlimitedEnchantOption(), + }; + + } + + private InstanceFieldTable ParseInstanceField() { + var results = new Dictionary(); + foreach ((int instanceId, InstanceField instanceField) in parser.ParseInstanceField()) { + foreach (int fieldId in instanceField.fieldIDs) { + + InstanceFieldMetadata instanceFieldMetadata = new( + MapId: fieldId, + Type: Enum.TryParse(instanceField.instanceType.ToString(), out InstanceType instanceType) ? instanceType : InstanceType.none, + InstanceId: instanceId, + BackupSourcePortal: instanceField.backupSourcePortal, + PoolCount: instanceField.poolCount, + SaveField: instanceField.isSaveField, + NpcStatFactorId: instanceField.npcStatFactorID, + MaxCount: instanceField.maxCount, + OpenType: instanceField.openType, + OpenValue: instanceField.openValue + ); + + results.Add(fieldId, instanceFieldMetadata); + } + } + + return new InstanceFieldTable(results); + } + + private ScriptConditionTable ParseScriptCondition() { + var results = new Dictionary>(); + results = MergeNpcScriptConditions(results, parser.ParseNpcScriptCondition()); + results = MergeQuestScriptConditions(results, parser.ParseQuestScriptCondition()); + + return new ScriptConditionTable(results); + } + + private Dictionary> MergeNpcScriptConditions(Dictionary> results, IEnumerable<(int NpcId, IDictionary ScriptConditions)> parser) { + foreach ((int npcId, IDictionary scripts) in parser) { + var scriptConditions = new Dictionary(); + foreach ((int scriptId, NpcScriptCondition scriptCondition) in scripts) { + var questStarted = new Dictionary(); + foreach (string quest in scriptCondition.quest_start) { + KeyValuePair parsedQuest = ParseToIntKeyValuePair(quest); + questStarted.Add(parsedQuest.Key, parsedQuest.Value); + } + + var questsCompleted = new Dictionary(); + foreach (string quest in scriptCondition.quest_complete) { + KeyValuePair parsedQuest = ParseToIntKeyValuePair(quest); + questsCompleted.Add(parsedQuest.Key, parsedQuest.Value); + } + + var items = new List>(); + for (int i = 0; i < scriptCondition.item.Length; i++) { + KeyValuePair parsedItem = ParseToIntKeyValuePair(scriptCondition.item[i]); + string itemCount = scriptCondition.itemCount.ElementAtOrDefault(i) ?? "1"; + if (!int.TryParse(itemCount, out int itemAmount)) { + itemAmount = 1; + } + var item = new ItemComponent(parsedItem.Key, -1, itemAmount, ItemTag.None); + items.Add(new KeyValuePair(item, parsedItem.Value)); + } + + scriptConditions.Add(scriptId, new ScriptConditionMetadata( + Id: npcId, + ScriptId: scriptId, + Type: ScriptType.Npc, + Maid: new ScriptConditionMetadata.MaidData( + Authority: scriptCondition.maid_auth, + Expired: scriptCondition.maid_expired != "!1", + ReadyToPay: scriptCondition.maid_ready_to_pay != "!1", + ClosenessRank: scriptCondition.maid_affinity_grade, + ClosenessTime: ParseToIntKeyValuePair(scriptCondition.maid_affinity_time), + MoodTime: ParseToIntKeyValuePair(scriptCondition.maid_mood_time), + DaysBeforeExpired: ParseToIntKeyValuePair(scriptCondition.maid_day_before_expired) + ), + Wedding: new ScriptConditionMetadata.WeddingData( + HasReservation: scriptCondition.weddingHallBooking < 0 ? null : scriptCondition.weddingHallBooking == 1, + MarriageDays: scriptCondition.marriageDate, + UserState: scriptCondition.weddingState < 0 ? null : (MaritalStatus) scriptCondition.weddingState, + HallState: ParseToStringKeyValuePair(scriptCondition.weddingHallState), + CoolingOff: scriptCondition.coolingOff), + JobCode: scriptCondition.job?.Select(job => (JobCode) job).ToList() ?? [], + QuestStarted: questStarted, + QuestCompleted: questsCompleted, + Items: items, + Buff: ParseToIntKeyValuePair(scriptCondition.buff), + Meso: ParseToIntKeyValuePair(scriptCondition.meso), + Level: ParseToIntKeyValuePair(scriptCondition.level), + AchieveCompleted: ParseToIntKeyValuePair(scriptCondition.achieve_complete), + DeathPenalty: scriptCondition.panelty == 1, + InGuild: scriptCondition.guild + )); + } + results.Add(npcId, scriptConditions); + } + return results; + } + + private Dictionary> MergeQuestScriptConditions(Dictionary> results, IEnumerable<(int NpcId, IDictionary ScriptConditions)> parser) { + foreach ((int questId, IDictionary scripts) in parser) { + var scriptConditions = new Dictionary(); + foreach ((int scriptId, QuestScriptCondition scriptCondition) in scripts) { + var questStarted = new Dictionary(); + foreach (string quest in scriptCondition.quest_start) { + KeyValuePair parsedQuest = ParseToIntKeyValuePair(quest); + questStarted.Add(parsedQuest.Key, parsedQuest.Value); + } + + var questsCompleted = new Dictionary(); + foreach (string quest in scriptCondition.quest_complete) { + KeyValuePair parsedQuest = ParseToIntKeyValuePair(quest); + questsCompleted.Add(parsedQuest.Key, parsedQuest.Value); + } + + var items = new List>(); + for (int i = 0; i < scriptCondition.item.Length; i++) { + KeyValuePair parsedItem = ParseToIntKeyValuePair(scriptCondition.item[i]); + string itemCount = scriptCondition.itemCount.ElementAtOrDefault(i) ?? "1"; + if (!int.TryParse(itemCount, out int itemAmount)) { + itemAmount = 1; + } + var item = new ItemComponent(parsedItem.Key, -1, itemAmount, ItemTag.None); + items.Add(new KeyValuePair(item, parsedItem.Value)); + } + + scriptConditions.Add(scriptId, new ScriptConditionMetadata( + Id: questId, + ScriptId: scriptId, + Type: ScriptType.Quest, + Maid: new ScriptConditionMetadata.MaidData( + Authority: scriptCondition.maid_auth, + Expired: scriptCondition.maid_expired != "!1", + ReadyToPay: scriptCondition.maid_ready_to_pay != "!1", + ClosenessRank: scriptCondition.maid_affinity_grade, + ClosenessTime: ParseToIntKeyValuePair(scriptCondition.maid_affinity_time), + MoodTime: ParseToIntKeyValuePair(scriptCondition.maid_mood_time), + DaysBeforeExpired: ParseToIntKeyValuePair(scriptCondition.maid_day_before_expired) + ), + Wedding: new ScriptConditionMetadata.WeddingData( + HasReservation: scriptCondition.weddingHallBooking < 0 ? null : scriptCondition.weddingHallBooking == 1, + MarriageDays: scriptCondition.marriageDate, + UserState: scriptCondition.weddingState < 0 ? null : (MaritalStatus) scriptCondition.weddingState, + HallState: ParseToStringKeyValuePair(scriptCondition.weddingHallState), + CoolingOff: scriptCondition.coolingOff), + JobCode: scriptCondition.job?.Select(job => (JobCode) job).ToList() ?? [], + QuestStarted: questStarted, + QuestCompleted: questsCompleted, + Items: items, + Buff: ParseToIntKeyValuePair(scriptCondition.buff), + Meso: ParseToIntKeyValuePair(scriptCondition.meso), + Level: ParseToIntKeyValuePair(scriptCondition.level), + AchieveCompleted: ParseToIntKeyValuePair(scriptCondition.achieve_complete), + InGuild: scriptCondition.guild, + DeathPenalty: scriptCondition.panelty == 1 + )); + } + results.Add(questId, scriptConditions); + } + return results; + } + + private static KeyValuePair ParseToIntKeyValuePair(string input) { + bool value = !input.StartsWith("!"); + + if (!value) { + input = input.Replace("!", ""); + } + + if (!int.TryParse(input, out int key)) { + key = 0; + } + return new KeyValuePair(key, value); + } + + private static KeyValuePair ParseToStringKeyValuePair(string input) { + bool value = !input.StartsWith("!"); + + if (!value) { + input = input.Substring(1); + } + return new KeyValuePair(input, value); + } + + private ScriptFunctionTable ParseScriptFunction() { + var results = new Dictionary>>(); + results = MergeNpcScriptFunctions(results, parser.ParseNpcScriptFunction()); + results = MergeQuestScriptFunctions(results, parser.ParseQuestScriptFunction()); + + return new ScriptFunctionTable(results); + } + + private static Dictionary>> MergeNpcScriptFunctions(Dictionary>> results, IEnumerable<(int NpcId, IDictionary ScriptFunctions)> parser) { + foreach ((int npcId, IDictionary scripts) in parser) { + var scriptDict = new Dictionary>(); // scriptIds, functionDict + foreach ((int scriptId, NpcScriptFunction scriptFunction) in scripts) { + var presentItems = new List(); + for (int i = 0; i < scriptFunction.presentItemID.Length; i++) { + short itemRarity = scriptFunction.presentItemRank.ElementAtOrDefault(i) != default(short) ? scriptFunction.presentItemRank.ElementAtOrDefault(i) : (short) -1; + int itemAmount = scriptFunction.presentItemAmount.ElementAtOrDefault(i) != default ? scriptFunction.presentItemAmount.ElementAtOrDefault(i) : 1; + presentItems.Add(new ItemComponent(scriptFunction.presentItemID[i], itemRarity, itemAmount, ItemTag.None)); + } + + var collectItems = new List(); + for (int i = 0; i < scriptFunction.collectItemID.Length; i++) { + int itemAmount = scriptFunction.collectItemAmount.ElementAtOrDefault(i) != default ? scriptFunction.collectItemAmount.ElementAtOrDefault(i) : 1; + collectItems.Add(new ItemComponent(scriptFunction.collectItemID[i], -1, itemAmount, ItemTag.None)); + } + + var metadata = new ScriptFunctionMetadata( + Id: npcId, // NpcId or QuestId + ScriptId: scriptId, + Type: ScriptType.Npc, + FunctionId: scriptFunction.functionID, + EndFunction: scriptFunction.endFunction, + PortalId: scriptFunction.portal, + UiName: scriptFunction.uiName, + UiArg: scriptFunction.uiArg, + UiArg2: scriptFunction.uiArg2, + MoveMapId: scriptFunction.moveFieldID, + MovePortalId: scriptFunction.moveFieldPortalID, + MoveMapMovie: scriptFunction.moveFieldMovie, + Emoticon: scriptFunction.emoticon, + PresentItems: presentItems, + CollectItems: collectItems, + SetTriggerValueTriggerId: scriptFunction.setTriggerValueTriggerID, + SetTriggerValueKey: scriptFunction.setTriggerValueKey, + SetTriggerValue: scriptFunction.setTriggerValue, + Divorce: scriptFunction.divorce, + PresentExp: scriptFunction.presentExp, + CollectMeso: scriptFunction.collectMeso, + MaidMoodIncrease: scriptFunction.maidMoodUp, + MaidClosenessIncrease: scriptFunction.maidAffinityUp, + MaidPay: scriptFunction.maidPay + ); + if (!scriptDict.TryGetValue(scriptId, out Dictionary? functionDict)) { + functionDict = new Dictionary { + { scriptFunction.functionID, metadata }, + }; + scriptDict.Add(scriptId, functionDict); + } else { + functionDict.Add(scriptFunction.functionID, metadata); + } + } + results.Add(npcId, scriptDict); + } + return results; + } + + private static Dictionary>> MergeQuestScriptFunctions(Dictionary>> results, IEnumerable<(int NpcId, IDictionary ScriptFunctions)> parser) { + foreach ((int questId, IDictionary scripts) in parser) { + var scriptDict = new Dictionary>(); // scriptIds, functionDict + foreach ((int scriptId, QuestScriptFunction scriptFunction) in scripts) { + var presentItems = new List(); + for (int i = 0; i < scriptFunction.presentItemID.Length; i++) { + short itemRarity = scriptFunction.presentItemRank.ElementAtOrDefault(i) != default(short) ? scriptFunction.presentItemRank.ElementAtOrDefault(i) : (short) -1; + int itemAmount = scriptFunction.presentItemAmount.ElementAtOrDefault(i) != default ? scriptFunction.presentItemAmount.ElementAtOrDefault(i) : 1; + presentItems.Add(new ItemComponent(scriptFunction.presentItemID[i], itemRarity, itemAmount, ItemTag.None)); + } + + var collectItems = new List(); + for (int i = 0; i < scriptFunction.collectItemID.Length; i++) { + int itemAmount = scriptFunction.collectItemAmount.ElementAtOrDefault(i) != default ? scriptFunction.collectItemAmount.ElementAtOrDefault(i) : 1; + collectItems.Add(new ItemComponent(scriptFunction.collectItemID[i], -1, itemAmount, ItemTag.None)); + } + + var metadata = new ScriptFunctionMetadata( + Id: questId, + ScriptId: scriptId, + Type: ScriptType.Quest, + FunctionId: scriptFunction.functionID, + EndFunction: scriptFunction.endFunction, + PortalId: scriptFunction.portal, + UiName: scriptFunction.uiName, + UiArg: scriptFunction.uiArg, + UiArg2: scriptFunction.uiArg2, + MoveMapId: scriptFunction.moveFieldID, + MovePortalId: scriptFunction.moveFieldPortalID, + MoveMapMovie: scriptFunction.moveFieldMovie, + Emoticon: scriptFunction.emoticon, + PresentItems: presentItems, + CollectItems: collectItems, + SetTriggerValueTriggerId: scriptFunction.setTriggerValueTriggerID, + SetTriggerValueKey: scriptFunction.setTriggerValueKey, + SetTriggerValue: scriptFunction.setTriggerValue, + Divorce: scriptFunction.divorce, + PresentExp: scriptFunction.presentExp, + CollectMeso: scriptFunction.collectMeso, + MaidMoodIncrease: scriptFunction.maidMoodUp, + MaidClosenessIncrease: scriptFunction.maidAffinityUp, + MaidPay: scriptFunction.maidPay + ); + if (!scriptDict.TryGetValue(scriptId, out Dictionary? functionDict)) { + functionDict = new Dictionary { + { scriptFunction.functionID, metadata }, + }; + scriptDict.Add(scriptId, functionDict); + } else { + functionDict.Add(scriptFunction.functionID, metadata); + } + } + results.Add(questId, scriptDict); + } + return results; + } + + private JobConditionTable ParseJobCondition() { + var results = new Dictionary(); + foreach ((int npcId, Parser.Xml.Table.Server.JobConditionTable jobCondition) in parser.ParseJobConditionTable()) { + DateTime date = DateTime.TryParseExact(jobCondition.date, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out date) ? date : DateTime.MinValue; + results.Add(npcId, new JobConditionMetadata( + NpcId: npcId, + ScriptId: jobCondition.scriptID, + StartedQuestId: jobCondition.quest_start, + CompletedQuestId: jobCondition.quest_complete, + JobCode: (JobCode) jobCondition.job, + MaidAuthority: jobCondition.maid_auth, + MaidClosenessTime: jobCondition.maid_affinity_time, + MaidCosenessRank: jobCondition.maid_affinity_grade, + Date: date.ToEpochSeconds(), + BuffId: jobCondition.buff, + Mesos: jobCondition.meso, + Level: jobCondition.level, + Home: jobCondition.home, + Roulette: jobCondition.roulette, + Guild: jobCondition.guild, + CompletedAchievement: jobCondition.achieve_complete, + IsBirthday: jobCondition.birthday, + ChangeToJobCode: (JobCode) jobCondition.jobCode, + MapId: jobCondition.map, + MoveMapId: jobCondition.moveFieldID, + MovePortalId: jobCondition.movePortalID, + DeathPenalty: jobCondition.panelty + )); + } + + return new JobConditionTable(results); + } + + private BonusGameTable ParseBonusGameTable() { + var bonusGames = new Dictionary(); + foreach ((int type, int id, BonusGame bonusGame) in parser.ParseBonusGame()) { + List slots = []; + foreach (BonusGame.Slot slot in bonusGame.slot) { + slots.Add(new BonusGameTable.Game.Slot( + MinProp: slot.minProp, + MaxProp: slot.maxProp)); + } + bonusGames.Add(id, new BonusGameTable.Game( + Id: id, + ConsumeItem: new ItemComponent( + ItemId: bonusGame.consumeItemID, + Rarity: 1, + Amount: bonusGame.consumeItemCount, + Tag: ItemTag.None), + Slots: slots.ToArray())); + } + + var drops = new Dictionary(); + foreach ((int type, int id, BonusGameDrop gameDrop) in parser.ParseBonusGameDrop()) { + List items = []; + foreach (BonusGameDrop.Item item in gameDrop.item) { + items.Add(new BonusGameTable.Drop.Item( + ItemComponent: new ItemComponent( + ItemId: item.id, + Rarity: item.rank, + Amount: item.count, + Tag: ItemTag.None), + Probability: item.prop, + Notice: item.notice)); + } + drops.Add(id, new BonusGameTable.Drop( + Id: id, + Items: items.ToArray())); + } + + return new BonusGameTable(bonusGames, drops); + } + + private GlobalDropItemBoxTable ParseGlobalItemDropTable() { + var dropGroups = new Dictionary>>(); + + foreach ((int id, GlobalDropItemBox itemDrop) in parser.ParseGlobalDropItemBox()) { + var groups = new List(); + foreach (GlobalDropItemBox.Group group in itemDrop.v) { + List dropCounts = []; + for (int i = 0; i < group.dropCount.Length; i++) { + dropCounts.Add(new GlobalDropItemBoxTable.Group.DropCount( + Amount: group.dropCount[i], + Probability: group.dropCountProbability[i])); + } + groups.Add(new GlobalDropItemBoxTable.Group( + GroupId: group.dropGroupIDs, + MinLevel: group.minLevel, + MaxLevel: group.maxLevel, + DropCounts: dropCounts, + OwnerDrop: group.isOwnerDrop, + MapTypeCondition: (MapType) group.mapTypeCondition, + ContinentCondition: (Continent) group.continentCondition)); + } + + if (!dropGroups.TryGetValue(id, out Dictionary>? groupDict)) { + groupDict = new Dictionary> { + { id, groups }, + }; + dropGroups.Add(id, groupDict); + } else { + groupDict.Add(id, groups); + } + } + + var dropItems = new Dictionary>(); + foreach ((int id, GlobalDropItemSet itemBox) in parser.ParseGlobalDropItemSet()) { + var items = new List(); + + foreach (GlobalDropItemSet.Item item in itemBox.v) { + int minCount = item.minCount <= 0 ? 1 : item.minCount; + int maxCount = item.maxCount < item.minCount ? item.minCount : item.maxCount; + items.Add(new GlobalDropItemBoxTable.Item( + Id: item.itemID, + MinLevel: item.minLevel, + MaxLevel: item.maxLevel, + DropCount: new GlobalDropItemBoxTable.Range(minCount, maxCount), + Rarity: item.grade, + Weight: item.weight, + MapIds: item.mapDependency, + QuestConstraint: item.constraintsQuest)); + } + + dropItems.Add(id, items); + } + return new GlobalDropItemBoxTable(dropGroups, dropItems); + } + + private UserStatTable ParseUserStat() { + static IReadOnlyDictionary UserStatMetadataMapper(UserStat userStat) { + Dictionary stats = new() { + { BasicAttribute.Strength, (long) userStat.str }, + { BasicAttribute.Dexterity, (long) userStat.dex }, + { BasicAttribute.Intelligence, (long) userStat.@int }, + { BasicAttribute.Luck, (long) userStat.luk }, + { BasicAttribute.Health, (long) userStat.hp }, + { BasicAttribute.HpRegen, (long) userStat.hp_rgp }, + { BasicAttribute.HpRegenInterval, (long) userStat.hp_inv }, + { BasicAttribute.Spirit, (long) userStat.sp }, + { BasicAttribute.SpRegen, (long) userStat.sp_rgp }, + { BasicAttribute.SpRegenInterval, (long) userStat.sp_inv }, + { BasicAttribute.Stamina, (long) userStat.ep }, + { BasicAttribute.StaminaRegen, (long) userStat.ep_rgp }, + { BasicAttribute.StaminaRegenInterval, (long) userStat.ep_inv }, + { BasicAttribute.AttackSpeed, (long) userStat.asp }, + { BasicAttribute.MovementSpeed, (long) userStat.msp }, + { BasicAttribute.Accuracy, (long) userStat.atp }, + { BasicAttribute.Evasion, (long) userStat.evp }, + { BasicAttribute.CriticalRate, (long) userStat.cap }, + { BasicAttribute.CriticalDamage, (long) userStat.cad }, + { BasicAttribute.CriticalEvasion, (long) userStat.car }, + { BasicAttribute.Defense, (long) userStat.ndd }, + { BasicAttribute.PerfectGuard, (long) userStat.abp }, + { BasicAttribute.JumpHeight, (long) userStat.jmp }, + { BasicAttribute.PhysicalAtk, (long) userStat.pap }, + { BasicAttribute.MagicalAtk, (long) userStat.map }, + { BasicAttribute.PhysicalRes, (long) userStat.par }, + { BasicAttribute.MagicalRes, (long) userStat.mar }, + { BasicAttribute.MinWeaponAtk, (long) userStat.wapmin }, + { BasicAttribute.MaxWeaponAtk, (long) userStat.wapmax }, + { BasicAttribute.Damage, (long) userStat.dmg }, + { BasicAttribute.Piercing, (long) userStat.pen }, + { BasicAttribute.BonusAtk, (long) userStat.base_atk }, + { BasicAttribute.PetBonusAtk, (long) userStat.sp_value }, + }; + + return stats; + } + + return new UserStatTable( + new Dictionary>> { + { JobCode.Newbie, parser.ParseUserStat1().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, + { JobCode.Knight, parser.ParseUserStat10().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, + { JobCode.Berserker, parser.ParseUserStat20().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, + { JobCode.Wizard, parser.ParseUserStat30().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, + { JobCode.Priest, parser.ParseUserStat40().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, + { JobCode.Archer, parser.ParseUserStat50().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, + { JobCode.HeavyGunner, parser.ParseUserStat60().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, + { JobCode.Thief, parser.ParseUserStat70().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, + { JobCode.Assassin, parser.ParseUserStat80().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, + { JobCode.RuneBlader, parser.ParseUserStat90().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, + { JobCode.Striker, parser.ParseUserStat100().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, + { JobCode.SoulBinder, parser.ParseUserStat110().ToDictionary(x => x.Level, x => UserStatMetadataMapper(x.UserStat)) }, + } + ); + } + + private IndividualDropItemTable ParseIndividualItemDropTable() { + var results = new Dictionary>(); + + foreach ((int id, IndividualItemDrop dropBox) in parser.ParseIndividualItemDrop()) { + var entries = new Dictionary(); + + foreach (IndividualItemDrop.Group group in dropBox.group) { + List items = []; + foreach (IndividualItemDrop.Group.Item item in group.v) { + int minCount = item.minCount <= 0 ? 1 : item.minCount; + int maxCount = item.maxCount < item.minCount ? item.minCount : item.maxCount; + List rarities = item.gradeProbability + .Select((probability, i) => new IndividualDropItemTable.Item.Rarity(probability, item.grade[i])) + .ToList(); + + if (rarities.Count == 0) { + if (item.grade.Length > 0) { + foreach (short grade in item.grade) { + rarities.Add(new IndividualDropItemTable.Item.Rarity(100, grade)); + } + } else if (item.uiItemRank != 0) { + rarities.Add(new IndividualDropItemTable.Item.Rarity(100, item.uiItemRank)); + } + } + items.Add(new IndividualDropItemTable.Item( + Ids: [item.itemID, item.itemID2], + Announce: item.isAnnounce, + ProperJobWeight: item.properJobWeight, + ImproperJobWeight: item.imProperJobWeight, + Weight: item.weight, + DropCount: new IndividualDropItemTable.Range(minCount, maxCount), + Rarities: rarities, + EnchantLevel: item.enchantLevel, + SocketDataId: item.socketDataID, + DeductTradeCount: item.tradableCountDeduction, + DeductRepackLimit: item.rePackingLimitCountDeduction, + Bind: item.isBindCharacter, + DisableBreak: item.disableBreak, + MapIds: item.mapDependency, + QuestId: item.constraintsQuest ? GetQuestId(dropBox.comment, item.reference1) : 0 + )); + } + + IList dropCounts = group.dropCount.Zip(group.dropCountProbability, (count, probability) => new IndividualDropItemTable.Entry.DropCount(count, probability)).ToList(); + if (dropCounts.Count == 0) { + dropCounts.Add(new IndividualDropItemTable.Entry.DropCount(1, 100)); + } + + var entry = new IndividualDropItemTable.Entry( + GroupId: group.dropGroupID, + SmartDropRate: group.smartDropRate, + DropCounts: dropCounts, + MinLevel: group.dropGroupMinLevel, + ServerDrop: group.serverDrop, + SmartGender: group.isApplySmartGenderDrop, + Items: items + ); + + entries.Add(group.dropGroupID, entry); + + } + + results.Add(id, entries); + } + return new IndividualDropItemTable(results); + + int GetQuestId(string comment, string reference1) { + if (reference1.Contains("Quest")) { + string[] referenceArray = reference1.Split("/"); + int referenceQuestIndex = Array.IndexOf(referenceArray, "Quest"); + + if (!int.TryParse(referenceArray[referenceQuestIndex - 2], out int questId) && comment.Contains("Quest")) { + string[] commentArray = comment.Split("/"); + int commentQuestIndex = Array.IndexOf(commentArray, "Quest"); + if (string.IsNullOrEmpty(commentArray[commentQuestIndex - 2])) { + return 0; + } + return !int.TryParse(commentArray[commentQuestIndex - 2], out questId) ? 0 : questId; + } + } + return 0; + } + } + + private PrestigeExpTable ParsePrestigeExpTable() { + var results = new Dictionary(); + + foreach ((AdventureExpType type, AdventureExpTable table) in parser.ParseAdventureExp()) { + ExpType expType = ToExpType(type); + results.Add(expType, table.value); + } + + return new PrestigeExpTable(results); + } + + private PrestigeIdExpTable ParsePrestigeIdExpTable() { + var results = new Dictionary(); + foreach ((int id, AdventureIdExpTable table) in parser.ParseAdventureIdExp()) { + results.Add(id, new PrestigeIdExpTable.Entry( + Id: id, + Value: table.value, + Type: ToExpType(table.expType))); + } + + return new PrestigeIdExpTable(results); + } + + private static ExpType ToExpType(AdventureExpType type) { + return type switch { + AdventureExpType.Exp_MapCommon => ExpType.mapCommon, + AdventureExpType.Exp_MapHidden => ExpType.mapHidden, + AdventureExpType.Exp_TaxiStation => ExpType.taxi, + AdventureExpType.Exp_Telescope => ExpType.telescope, + AdventureExpType.Exp_RareChest => ExpType.rareChest, + AdventureExpType.Exp_RareChestFirst => ExpType.rareChestFirst, + AdventureExpType.Exp_NormalChest => ExpType.normalChest, + AdventureExpType.Exp_DropItem => ExpType.dropItem, + AdventureExpType.Exp_DungeonBoss => ExpType.dungeonBoss, + AdventureExpType.Exp_MusicMasteryLv1 => ExpType.musicMastery1, + AdventureExpType.Exp_MusicMasteryLv2 => ExpType.musicMastery2, + AdventureExpType.Exp_MusicMasteryLv3 => ExpType.musicMastery3, + AdventureExpType.Exp_MusicMasteryLv4 => ExpType.musicMastery4, + AdventureExpType.Exp_Arcade => ExpType.arcade, + AdventureExpType.Exp_Fishing => ExpType.fishing, + AdventureExpType.Exp_Rest => ExpType.rest, + AdventureExpType.Exp_Quest => ExpType.quest, + AdventureExpType.Exp_PvpBloodMineRank1 => ExpType.bloodMineRank1, + AdventureExpType.Exp_PvpBloodMineRank2 => ExpType.bloodMineRank2, + AdventureExpType.Exp_PvpBloodMineRank3 => ExpType.bloodMineRank3, + AdventureExpType.Exp_PvpBloodMineRankOther => ExpType.bloodMineRankOther, + AdventureExpType.Exp_PvpRedDuelWin => ExpType.redDuelWin, + AdventureExpType.Exp_PvpRedDuelLose => ExpType.redDuelLose, + AdventureExpType.Exp_PvpBtiTeamWin => ExpType.btiTeamWin, + AdventureExpType.Exp_PvpBtiTeamLose => ExpType.btiTeamLose, + AdventureExpType.Exp_PvpRankDuelWin => ExpType.rankDuelWin, + AdventureExpType.Exp_PvpRankDuelLose => ExpType.rankDuelLose, + AdventureExpType.Exp_Gathering => ExpType.gathering, + AdventureExpType.Exp_Manufacturing => ExpType.manufacturing, + AdventureExpType.Exp_RandomDungeonBonus => ExpType.randomDungeonBonus, + AdventureExpType.Exp_MiniGame => ExpType.miniGame, + AdventureExpType.Exp_UserMiniGame => ExpType.userMiniGame, + AdventureExpType.Exp_UserMiniGameExtra => ExpType.userMiniGameExtra, + AdventureExpType.Exp_Mission => ExpType.mission, + AdventureExpType.Exp_DungeonRelative => ExpType.dungeonRelative, + AdventureExpType.Exp_GuildUserExp => ExpType.guildUserExp, + AdventureExpType.Exp_DailyGuildQuest => ExpType.dailyGuildQuest, + AdventureExpType.Exp_WeeklyGuildQuest => ExpType.weeklyGuildQuest, + AdventureExpType.Exp_PetTaming => ExpType.petTaming, + AdventureExpType.Exp_DailyMission => ExpType.dailymission, + AdventureExpType.Exp_DailyMissionLevelUp => ExpType.dailymissionLevelUp, + AdventureExpType.Exp_mapleSurvival => ExpType.mapleSurvival, + AdventureExpType.Exp_DarkStream => ExpType.darkStream, + AdventureExpType.Exp_DungeonClear => ExpType.dungeonClear, + AdventureExpType.Exp_KillMonster => ExpType.monster, + AdventureExpType.Exp_QuestETC => ExpType.questEtc, + AdventureExpType.Exp_EpicQuest => ExpType.epicQuest, + AdventureExpType.Exp_KillMonsterBoss => ExpType.monsterBoss, + AdventureExpType.Exp_KillMonsterElite => ExpType.monsterElite, + _ => ExpType.none, + }; + } + + private TimeEventTable ParseTimeEventTable() { + var results = new Dictionary(); + foreach ((int id, TimeEventData data) in parser.ParseTimeEventData()) { + // TODO: Handle other event types + if (data.type == TimeEventType.GlobalEvent) { + var entries = new GlobalPortalMetadata.Field[3]; // UI only supports 3 fields + entries[0] = new GlobalPortalMetadata.Field( + Name: data.eventName1, + MapId: data.eventField1.Length == 0 ? 0 : data.eventField1[0], + PortalId: data.eventField1.Length == 0 ? 0 : data.eventField1[1]); + entries[1] = new GlobalPortalMetadata.Field( + Name: data.eventName2, + MapId: data.eventField2.Length == 0 ? 0 : data.eventField2[0], + PortalId: data.eventField2.Length == 0 ? 0 : data.eventField2[1]); + entries[2] = new GlobalPortalMetadata.Field( + Name: data.eventName3, + MapId: data.eventField3.Length == 0 ? 0 : data.eventField3[0], + PortalId: data.eventField3.Length == 0 ? 0 : data.eventField3[1]); + int[] startTimeArray = ParseTimeToArray(data.startTime); + int[] endTimeArray = ParseTimeToArray(data.endTime); + int[] cycleArray = ParseTimeToArray(data.cycleTime); + int[] randomArray = ParseTimeToArray(data.randomTime); + int[] lifeArray = ParseTimeToArray(data.lifeTime); + results.Add(id, new GlobalPortalMetadata( + Id: id, + Probability: data.prob, + StartTime: new DateTime(startTimeArray[0], startTimeArray[1], startTimeArray[2], startTimeArray[3], startTimeArray[4], startTimeArray[5]), + EndTime: new DateTime(endTimeArray[0], endTimeArray[1], endTimeArray[2], endTimeArray[3], endTimeArray[4], endTimeArray[5]), + CycleTime: new TimeSpan(cycleArray[2], cycleArray[3], cycleArray[4], cycleArray[5]), + RandomTime: new TimeSpan(randomArray[2], randomArray[3], randomArray[4], randomArray[5]), + LifeTime: new TimeSpan(lifeArray[2], lifeArray[3], lifeArray[4], lifeArray[5]), + PopupMessage: data.popupMessage, + SoundId: data.soundID, + Entries: entries)); + } + } + return new TimeEventTable(results); + + int[] ParseTimeToArray(string time) { + string[] timeArray = time.Split('-'); + int[] timeInt = new int[timeArray.Length]; + for (int i = 0; i < timeArray.Length; i++) { + timeInt[i] = int.Parse(timeArray[i]); + } + return timeInt; + } + } + + private GameEventTable ParseGameEventTable() { + var results = new Dictionary(); + foreach ((int id, GameEvent data) in parser.ParseGameEvent()) { + if (!Enum.TryParse(data.eventType, out GameEventType eventType)) { + Console.WriteLine($"Unknown GameEventType: {data.eventType}"); + } + + GameEventData? eventData = ParseGameEventData(eventType, data.value1, data.value2, data.value3, data.value4); + if (eventData == null) { + continue; + } + + DateTime startTime = DateTime.TryParseExact(data.eventStart, "yyyy-MM-dd-HH-mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out startTime) ? startTime : DateTime.MinValue; + DateTime endTime = DateTime.TryParseExact(data.eventEnd, "yyyy-MM-dd-HH-mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out endTime) ? endTime : DateTime.MinValue; + + // Only add events that are not expired + if (endTime < DateTime.UtcNow) { + continue; + } + + (TimeSpan partTimeStart, TimeSpan partTimeEnd) = ParsePartTime(data.partTime); + results.Add(id, new GameEventMetadata( + Id: id, + Type: eventType, + StartTime: startTime, + EndTime: endTime, + StartPartTime: partTimeStart, + EndPartTime: partTimeEnd, + ActiveDays: data.dayOfWeek.Length == 0 ? [] : data.dayOfWeek.Select(ParseDayOfWeek).ToArray(), + Data: eventData, + Value1: data.value1, + Value2: data.value2, + Value3: data.value3, + Value4: data.value4)); + } + return new GameEventTable(results); + + DayOfWeek ParseDayOfWeek(Maple2.File.Parser.Enum.DayOfWeek dayofWeek) { + return dayofWeek switch { + Maple2.File.Parser.Enum.DayOfWeek.sun => DayOfWeek.Sunday, + Maple2.File.Parser.Enum.DayOfWeek.mon => DayOfWeek.Monday, + Maple2.File.Parser.Enum.DayOfWeek.tue => DayOfWeek.Tuesday, + Maple2.File.Parser.Enum.DayOfWeek.wed => DayOfWeek.Wednesday, + Maple2.File.Parser.Enum.DayOfWeek.thu => DayOfWeek.Thursday, + Maple2.File.Parser.Enum.DayOfWeek.fri => DayOfWeek.Friday, + Maple2.File.Parser.Enum.DayOfWeek.sat => DayOfWeek.Saturday, + _ => DayOfWeek.Sunday, + }; + } + } + + private (TimeSpan, TimeSpan) ParsePartTime(string partTimeString) { + string[] partTimeStringArray = partTimeString.Split("-").ToArray(); + if (partTimeStringArray.Length != 2) { + return (TimeSpan.Zero, TimeSpan.Zero); + } + TimeSpan startTime = TimeSpan.Parse(partTimeStringArray[0]); + TimeSpan endTime = TimeSpan.Parse(partTimeStringArray[1]); + return (startTime, endTime); + } + + private GameEventData? ParseGameEventData(GameEventType type, string value1, string value2, string value3, string value4) { + var value1Xml = new XmlDocument(); + var value2Xml = new XmlDocument(); + var value3Xml = new XmlDocument(); + var value4Xml = new XmlDocument(); + + switch (type) { + case GameEventType.BlueMarble: + if (!string.IsNullOrEmpty(value1)) { + value1Xml.LoadXml(value1); + } + + value2Xml.LoadXml(value2); + var rounds = new List(); + var requiredItem = new ItemComponent(0, 0, 0, ItemTag.None); + + XmlNode? roundNode = value1Xml.FirstChild; + if (roundNode != null) { + if (roundNode.Attributes?["consumeItemID"] != null) { + if (!int.TryParse(roundNode.Attributes?["consumeItemID"]?.Value, out int itemId)) { + itemId = 0; + } + if (!int.TryParse(roundNode.Attributes?["consumeItemCount"]?.Value, out int itemCount)) { + itemCount = 1; + } + requiredItem = new ItemComponent(itemId, -1, itemCount, ItemTag.None); + } + + foreach (XmlNode vNode in roundNode.ChildNodes) { + if (!int.TryParse(vNode.Attributes?["round"]?.Value, out int round)) { + round = 0; + } + + if (!int.TryParse(vNode.Attributes?["itemID"]?.Value, out int itemId)) { + itemId = 0; + } + + if (!int.TryParse(vNode.Attributes?["itemCount"]?.Value, out int itemCount)) { + itemCount = 1; + } + + rounds.Add(new BlueMarble.Round( + RoundCount: round, + Item: new ItemComponent( + ItemId: itemId, + Rarity: 1, + Amount: itemCount, + Tag: ItemTag.None))); + } + } + + XmlNode? slotsNode = value2Xml.FirstChild; + if (slotsNode == null) { + return null; + } + + var slots = new List(); + foreach (XmlNode vNode in slotsNode.ChildNodes) { + if (!Enum.TryParse(vNode.Attributes?["type"]?.Value, true, out BlueMarbleSlotType slotType)) { + slotType = BlueMarbleSlotType.Item; + } + + if (!int.TryParse(vNode.Attributes?["arg1"]?.Value, out int arg1)) { + arg1 = 0; + } + + if (!int.TryParse(vNode.Attributes?["arg2"]?.Value, out int arg2)) { + arg2 = 0; + } + + int moveAmount = 0; + if (slotType is BlueMarbleSlotType.Backward or BlueMarbleSlotType.Forward) { + moveAmount = arg1; + } + + var blueMarbleSlotItem = new ItemComponent(0, 0, 0, ItemTag.None); + if (slotType is BlueMarbleSlotType.Item or BlueMarbleSlotType.Paradise) { + // TODO: Get rarity from item xmls + blueMarbleSlotItem = new ItemComponent(arg1, -1, arg2, ItemTag.None); + } + + slots.Add(new BlueMarble.Slot( + Type: slotType, + MoveAmount: moveAmount, + Item: blueMarbleSlotItem)); + } + return new BlueMarble( + RequiredItem: requiredItem, + Rounds: rounds.ToArray(), + Slots: slots.ToArray()); + case GameEventType.StringBoard: + return new StringBoard( + Text: value4, + StringId: int.TryParse(value1, out int stringId) ? stringId : 0); + case GameEventType.StringBoardLink: + return new StringBoardLink( + Link: value1); + case GameEventType.TrafficOptimizer: + // values are hardcoded seeing as these are not shown in the table. + return new TrafficOptimizer( + RideSyncInterval: 100, + UserSyncInterval: 100, + LinearMovementInterval: 100, + GuideObjectSyncInterval: 100); + case GameEventType.LobbyMap: + return new LobbyMap( + MapId: int.TryParse(value1, out int lobbyMapId) ? lobbyMapId : 0); + case GameEventType.EventFieldPopup: + return new EventFieldPopup( + MapId: int.TryParse(value1, out int fieldPopupMapId) ? fieldPopupMapId : 0); + case GameEventType.SaleChat: + return new SaleChat( + WorldChatDiscount: int.TryParse(value1, out int worldChatDiscount) ? worldChatDiscount : 0, + ChannelChatDiscount: int.TryParse(value2, out int channelChatDiscount) ? channelChatDiscount : 0); + case GameEventType.AttendGift: + value1Xml = new XmlDocument(); + value1Xml.LoadXml(value1); + + var rewards = new List(); + if (value1Xml.FirstChild == null) { + return null; + } + foreach (XmlNode node in value1Xml.FirstChild.ChildNodes) { + if (!int.TryParse(node.Attributes?["itemID"]?.Value, out int itemId)) { + itemId = 0; + } + if (!int.TryParse(node.Attributes?["count"]?.Value, out int itemCount)) { + itemCount = 1; + } + if (!short.TryParse(node.Attributes?["grade"]?.Value, out short grade)) { + grade = -1; + } + rewards.Add(new RewardItem(itemId, grade, itemCount)); + } + + value2Xml = new XmlDocument(); + value2Xml.LoadXml(value2); + if (value2Xml.FirstChild is not { Name: "ms2" }) { + return null; + } + + XmlNode? stringNode = value2Xml.FirstChild.SelectSingleNode("string"); + XmlNode? configNode = value2Xml.FirstChild.SelectSingleNode("Config"); + if (stringNode == null || configNode == null) { + return null; + } + + string name = stringNode.Attributes?["name"]?.Value ?? string.Empty; + string mailTitle = stringNode.Attributes?["mailTitle"]?.Value ?? string.Empty; + string mailContent = stringNode.Attributes?["mailContents"]?.Value ?? string.Empty; + string link = stringNode.Attributes?["detailUrl"]?.Value ?? string.Empty; + + if (!int.TryParse(configNode.Attributes?["requirePlaySeconds"]?.Value, out int requiredPlaySeconds)) { + requiredPlaySeconds = 0; + } + + AttendGift.Require? giftRequirement = null; + if (!string.IsNullOrEmpty(value3)) { + value3Xml.LoadXml(value3); + if (value3Xml.FirstChild is { Name: "ms2" }) { + XmlNode? requirementNode = value3Xml.FirstChild.SelectSingleNode("require"); + if (requirementNode != null) { + if (!Enum.TryParse(requirementNode.Attributes?["type"]?.Value, true, out AttendGiftRequirement requirement)) { + requirement = AttendGiftRequirement.None; + } + + if (!int.TryParse(requirementNode.Attributes?["value1"]?.Value, out int requirementValue1)) { + requirementValue1 = 0; + } + + if (!int.TryParse(requirementNode.Attributes?["value2"]?.Value, out int requirementValue2)) { + requirementValue2 = 0; + } + + giftRequirement = new AttendGift.Require( + Type: requirement, + Value1: requirementValue1, + Value2: requirementValue2); + } + } + } + + return new AttendGift( + Items: rewards.ToArray(), + Name: name, + MailTitle: mailTitle, + MailContent: mailContent, + Link: link, + RequiredPlaySeconds: requiredPlaySeconds, + Requirement: giftRequirement); + case GameEventType.ReturnUser: + var requiredTime = DateTimeOffset.MinValue; + int daysInactive = 0; + if (DateTime.TryParseExact(value1, "yyyy-MM-dd-HH-mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime returnUserTime)) { + requiredTime = new DateTimeOffset(returnUserTime); + } else if (int.TryParse(value1, out daysInactive)) { + } + + return new ReturnUser( + SeasonId: int.TryParse(value3, out int season) ? season : 0, + DateInactiveSince: requiredTime, + DaysInactive: daysInactive, + QuestIds: string.IsNullOrEmpty(value4) ? [] : value4.Split(',').Select(int.Parse).ToArray(), + RequiredLevel: int.TryParse(value1, out int levelRequirement) ? levelRequirement : 0, + RequiredUserValue: int.TryParse(value2, out int userValue) ? userValue : 0); + case GameEventType.NewUser: + var requiredNewUserTime = DateTimeOffset.MinValue; + if (DateTime.TryParseExact(value1, "yyyy-MM-dd-HH-mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime newUserTime)) { + requiredNewUserTime = new DateTimeOffset(newUserTime); + } + + return new NewUser( + SeasonId: int.TryParse(value2, out int newUserSeason) ? newUserSeason : 0, + DateCreatedBy: requiredNewUserTime); + case GameEventType.ReturnUserCandidate: + var unknownTime = DateTimeOffset.MinValue; + if (DateTime.TryParseExact(value4, "yyyy-MM-dd-HH-mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime requiredDateTime)) { + unknownTime = new DateTimeOffset(requiredDateTime); + } + return new ReturnUserCandidate( + Season: int.TryParse(value3, out int returnUserCandidateSeason) ? returnUserCandidateSeason : 0, + SeasonId: int.TryParse(value2, out int returnUserCandidateSeasonId) ? returnUserCandidateSeasonId : 0, + MinLevel: int.TryParse(value1, out int returnUserCandidateMinLevel) ? returnUserCandidateMinLevel : 0, + UnknownDate: unknownTime); + case GameEventType.ActiveUser: + int[] value1Values = value1.Split(',').Select(int.Parse).ToArray(); + + value2Xml.LoadXml(value2); + int meret = 0; + if (value2Xml.FirstChild is { Name: "money" }) { + foreach (XmlNode childNode in value2Xml.FirstChild.ChildNodes) { + if (childNode.Name == "v") { + string valueType = childNode.Attributes?["type"]?.Value ?? string.Empty; + if (valueType == "merat_e") { + string? amountStr = childNode.Attributes?["amount"]?.Value; + if (!int.TryParse(amountStr, out int meratEAmount)) { + Console.WriteLine($"Failed to parse merat_e amount: {amountStr} for event type {type}"); + } else { + meret = meratEAmount; + } + } + } + } + } + return new ActiveUser( + MailId: value1Values[0], + MailDaysExpire: value1Values[1], + Meret: meret, + MinLevel: int.TryParse(value3, out int minLevel) ? minLevel : 0); + case GameEventType.RPS: + value1Xml = new XmlDocument(); + value1Xml.LoadXml(value1); + if (value1Xml.FirstChild is not { Name: "ms2" }) { + return null; + } + + XmlNode? rpseventNode = value1Xml.FirstChild.SelectSingleNode("rps_event"); + if (rpseventNode == null) { + return null; + } + + int ticketId = 0; + var rpsRewards = new List(); + foreach (XmlNode childNode in rpseventNode) { + if (childNode.Name == "gameTicket" && int.TryParse(childNode.Attributes?["itemID"]?.Value, out int itemId)) { + ticketId = itemId; + } + + if (childNode.Name == "rewardData") { + if (!int.TryParse(childNode.Attributes?["condPlayCount"]?.Value, out int playCount)) { + playCount = 1; + } + + // items + var rpsItems = new List(); + foreach (XmlNode itemNode in childNode.ChildNodes) { + foreach (XmlNode valueNode in itemNode.ChildNodes) { + if (!int.TryParse(valueNode.Attributes?["itemID"]?.Value, out int rpsRewardItemId)) { + rpsRewardItemId = 0; + } + + if (!short.TryParse(valueNode.Attributes?["grade"]?.Value, out short rpsRewardGrade)) { + rpsRewardGrade = 1; + } + + if (!int.TryParse(valueNode.Attributes?["count"]?.Value, out int rpsRewardCount)) { + rpsRewardCount = 1; + } + + rpsItems.Add(new RewardItem(rpsRewardItemId, rpsRewardGrade, rpsRewardCount)); + } + } + + rpsRewards.Add(new Rps.RewardData( + PlayCount: playCount, + Rewards: rpsItems.ToArray())); + } + } + + return new Rps( + GameTicketId: ticketId, + Rewards: rpsRewards.ToArray(), + ActionsHtml: value2); + case GameEventType.LoginNotice: + return new LoginNotice(); + case GameEventType.FieldEffect: + return new FieldEffect( + MapIds: value1.Split(',').Select(int.Parse).ToArray(), + Effect: value2); + case GameEventType.DTReward: + string[] itemStrings = value1.Split(';'); + + List items = []; + foreach (string itemString in itemStrings) { + int[] itemData = itemString.Split(',').Select(int.Parse).ToArray(); + items.Add(new DTReward.Entry( + StartDuration: itemData[0], + EndDuration: itemData[1], + MailContentId: itemData[2], + Item: new RewardItem( + itemId: itemData[3], + amount: itemData[4], + rarity: (short) itemData[5]))); + } + + return new DTReward( + Entries: items.ToArray()); + case GameEventType.ConstructShowItem: + return new ConstructShowItem( + CategoryId: int.TryParse(value1, out int categoryId) ? categoryId : 0, + CategoryName: value2, + ItemIds: value4.Split(',').Select(int.Parse).ToArray()); + case GameEventType.MassiveConstructionEvent: + return new MassiveConstructionEvent( + MapIds: value1.Split(',').Select(int.Parse).ToArray()); + case GameEventType.UGCMapContractSale: + return new UGCMapContractSale( + DiscountAmount: int.TryParse(value1, out int contractSaleAmount) ? contractSaleAmount : 0); + case GameEventType.UGCMapExtensionSale: + return new UGCMapExtensionSale( + DiscountAmount: int.TryParse(value1, out int extensionSaleAmount) ? extensionSaleAmount : 0); + case GameEventType.Gallery: + value1Xml.LoadXml(value1); + if (value1Xml.FirstChild is not { Name: "cards" }) { + return null; + } + + var questIds = new List(); + foreach (XmlNode valueNode in value1Xml.FirstChild) { + if (!int.TryParse(valueNode.Attributes?["quest"]?.Value, out int questId)) { + continue; + } + questIds.Add(questId); + } + + value2Xml.LoadXml(value2); + if (value2Xml.FirstChild is not { Name: "items" }) { + return null; + } + + var galleryRewards = new List(); + foreach (XmlNode itemNode in value2Xml.FirstChild) { + if (!int.TryParse(itemNode.Attributes?["itemID"]?.Value, out int itemId)) { + continue; + } + + if (!short.TryParse(itemNode.Attributes?["grade"]?.Value, out short grade)) { + grade = 1; + } + + if (!int.TryParse(itemNode.Attributes?["count"]?.Value, out int count)) { + count = 1; + } + + galleryRewards.Add(new RewardItem(itemId, grade, count)); + } + return new Gallery( + QuestIds: questIds.ToArray(), + RewardItems: galleryRewards.ToArray(), + RevealDayLimit: int.TryParse(value3, out int revealDayLimit) ? revealDayLimit : 1, + Image: value4); + case GameEventType.BingoEvent: + value1Xml.LoadXml(value1); + if (value1Xml.FirstChild is not { Name: "ms2" }) { + return null; + } + + var numbers = new List(); + foreach (XmlNode childNode in value1Xml.FirstChild.ChildNodes) { + if (childNode.Name == "number") { + int[] dayNumbers = childNode.Attributes?["value"]?.Value.Split(',').Select(int.Parse).ToArray() ?? []; + numbers.Add(dayNumbers); + } + } + + + value2Xml.LoadXml(value2); + if (value2Xml.FirstChild is not { Name: "ms2" }) { + return null; + } + + + var bingoRewards = new List(); + foreach (XmlNode childNode in value2Xml.FirstChild.ChildNodes) { + if (childNode.Name == "reward") { + var bingoItems = new List(); + foreach (XmlNode itemNode in childNode.ChildNodes) { + if (!int.TryParse(itemNode.Attributes?["itemID"]?.Value, out int itemId)) { + continue; + } + + if (!short.TryParse(itemNode.Attributes?["grade"]?.Value, out short grade)) { + grade = 1; + } + + if (!int.TryParse(itemNode.Attributes?["count"]?.Value, out int count)) { + count = 1; + } + + bingoItems.Add(new RewardItem(itemId, grade, count)); + } + bingoRewards.Add(new BingoEvent.BingoReward( + Items: bingoItems.ToArray())); + } + } + + int pencilItemId = int.TryParse(value3, out int pencilId) ? pencilId : 0; + int pencilPlusItemId = int.TryParse(value4, out int plusPencilId) ? plusPencilId : 0; + return new BingoEvent( + Numbers: numbers.ToArray(), + Rewards: bingoRewards.ToArray(), + PencilItemId: pencilItemId, + PencilPlusItemId: pencilPlusItemId); + case GameEventType.TimeRunEvent: + value1Xml.LoadXml(value1); + if (value1Xml.FirstChild is not { Name: "ms" }) { + return null; + } + + var quests = new List(); + foreach (XmlNode childNode in value1Xml.FirstChild.ChildNodes) { + if (childNode.Name == "quest") { + if (!int.TryParse(childNode.Attributes?["questID"]?.Value, out int questId)) { + continue; + } + if (!int.TryParse(childNode.Attributes?["distance"]?.Value, out int distance)) { + continue; + } + if (!int.TryParse(childNode.Attributes?["openingDay"]?.Value, out int openingDay)) { + continue; + } + quests.Add(new TimeRunEvent.Quest( + Id: questId, + Distance: distance, + OpeningDay: openingDay)); + } + } + + value2Xml.LoadXml(value2); + if (value2Xml.FirstChild is not { Name: "ms" }) { + return null; + } + XmlNode? rewardNode = value2Xml.FirstChild.FirstChild; + if (rewardNode == null) { + return null; + } + + if (!int.TryParse(rewardNode.Attributes?["itemID"]?.Value, out int timerunEventItemId)) { + return null; + } + if (!int.TryParse(rewardNode.Attributes?["count"]?.Value, out int timerunEventItemCount)) { + return null; + } + if (!short.TryParse(rewardNode.Attributes?["grade"]?.Value, out short timerunEventItemGrade)) { + return null; + } + + if (!int.TryParse(value3, out int startTimeRunEventItemId)) { + return null; + } + return new TimeRunEvent( + StartItemId: startTimeRunEventItemId, + Quests: quests.ToArray(), + StepRewards: new Dictionary(), // No step rewards were added in the metadata. + FinalReward: new RewardItem( + itemId: timerunEventItemId, + amount: timerunEventItemCount, + rarity: timerunEventItemGrade)); + case GameEventType.MapleSurvivalOpenPeriod: + return new MapleSurvivalOpenPeriod(); + case GameEventType.ShutdownMapleSurvival: + return new ShutdownMapleSurvival(); + case GameEventType.SaleAutoPlayInstrument: + if (!int.TryParse(value1, out int performanceDiscount) && string.IsNullOrEmpty(value2)) { + return null; + } + return new SaleAutoPlayInstrument( + Discount: performanceDiscount, + ContentType: value2); + case GameEventType.SaleAutoFishing: + if (!int.TryParse(value1, out int fishingDiscount) && string.IsNullOrEmpty(value2)) { + return null; + } + return new SaleAutoFishing( + Discount: fishingDiscount, + ContentType: value2); + default: + return null; + } + } + + private OxQuizTable ParseOxQuizTable() { + var results = new Dictionary(); + foreach ((int id, OxQuiz quiz) in parser.ParseOxQuiz()) { + results.Add(id, new OxQuizTable.Entry( + Id: quiz.quizID, + CategoryId: quiz.categoryID, + Category: quiz.categoryStr, + Question: quiz.quizStr, + Level: quiz.level, + IsTrue: quiz.answer, + Answer: quiz.answerStr)); + } + return new OxQuizTable(results); + } + + private ItemMergeTable ParseItemMergeOptionTable() { + var results = new Dictionary>(); + foreach ((int id, MergeOption mergeOption) in parser.ParseItemMergeOption()) { + var slots = new Dictionary(); + foreach (MergeOption.Slot slotEntry in mergeOption.slot) { + var ingredients = new List(); + + ItemComponent? ingredient1 = ParseItemMaterial(slotEntry.itemMaterial1); + if (ingredient1 != null) { + ingredients.Add(ingredient1); + } + ItemComponent? ingredient2 = ParseItemMaterial(slotEntry.itemMaterial2); + if (ingredient2 != null) { + ingredients.Add(ingredient2); + } + + var basicOptions = new Dictionary(); + var specialOptions = new Dictionary(); + + foreach (MergeOption.Option mergeOptionEntry in slotEntry.option) { + if (mergeOptionEntry.optionName is "str" or "dex" or "int" or "luk" or "hp" or "hp_rgp" or "hp_inv" or "sp" or "sp_rgp" or "sp_inv" or "ep" or "ep_rgp" or "ep_inv" or "asp" or "msp" or "atp" or "evp" or + "cap" or "cad" or "car" or "ndd" or "abp" or "jmp" or "pap" or "map" or "par" or "mar" or "wapmin" or "wapmax" or "dmg" or "pen" or "rmsp" or "bap" or "bap_pet") { + var basicAttribute = mergeOptionEntry.optionName.ToBasicAttribute(); + List> values = []; + List> rates = []; + List weights = []; + int min = mergeOptionEntry.min; + if (basicAttribute is BasicAttribute.Piercing or BasicAttribute.PerfectGuard or + BasicAttribute.JumpHeight) { + // Looping by 10 because that's the max amount of values in the xml + for (int i = 0; i < 10; i++) { + (int value, int weight) = mergeOptionEntry[i]; + if (value == 0) { + continue; + } + rates.Add(new ItemMergeTable.Range(min + 1, value)); + values.Add(new ItemMergeTable.Range(0, 0)); + weights.Add(weight); + min = value; + } + } else { + for (int i = 0; i < 10; i++) { + (int value, int weight) = mergeOptionEntry[i]; + if (value == 0) { + continue; + } + values.Add(new ItemMergeTable.Range(min + 1, value)); + rates.Add(new ItemMergeTable.Range(0, 0)); + weights.Add(weight); + min = value; + } + } + + basicOptions[basicAttribute] = new ItemMergeTable.Option( + Values: values.ToArray(), + Rates: rates.ToArray(), + Weights: weights.ToArray()); + } else { + var specialAttribute = mergeOptionEntry.optionName.ToSpecialAttribute(); + List> values = []; + List> rates = []; + List weights = []; + int min = mergeOptionEntry.min; + if (specialAttribute is SpecialAttribute.HpOnKill or SpecialAttribute.ReduceCooldown or SpecialAttribute.ReduceKnockBack or SpecialAttribute.MassiveOxSpeed or SpecialAttribute.MassiveTrapMasterSpeed or + SpecialAttribute.MassiveFinalSurvivalSpeed or SpecialAttribute.MassiveCrazyRunnerSpeed or SpecialAttribute.MassiveShCrazyRunnerSpeed or SpecialAttribute.MassiveEscapeSpeed or SpecialAttribute.MassiveSpringBeachSpeed or + SpecialAttribute.MassiveDanceDanceSpeed or SpecialAttribute.DarkStreamEvp or SpecialAttribute.CompleteFieldMissionSpeed or SpecialAttribute.AdditionalEffect95000018 or SpecialAttribute.AdditionalEffect95000012 or + SpecialAttribute.AdditionalEffect95000014 or SpecialAttribute.AdditionalEffect95000020 or SpecialAttribute.AdditionalEffect95000021 or SpecialAttribute.AdditionalEffect95000022 or SpecialAttribute.AdditionalEffect95000023 + or SpecialAttribute.AdditionalEffect95000024 or SpecialAttribute.AdditionalEffect95000025 or SpecialAttribute.AdditionalEffect95000026 or SpecialAttribute.AdditionalEffect95000027 or SpecialAttribute.AdditionalEffect95000028 or + SpecialAttribute.AdditionalEffect95000029 or SpecialAttribute.DashDistance or SpecialAttribute.SpiritOnKill or SpecialAttribute.StaminaOnKill or SpecialAttribute.PvpDamage or SpecialAttribute.ReducePvpDamage or SpecialAttribute.SkillLevelUpTier1 + or SpecialAttribute.SkillLevelUpTier2 or SpecialAttribute.SkillLevelUpTier3 or SpecialAttribute.SkillLevelUpTier4 or SpecialAttribute.SkillLevelUpTier5 or SpecialAttribute.SkillLevelUpTier6 or SpecialAttribute.SkillLevelUpTier7 or SpecialAttribute.SkillLevelUpTier8 + or SpecialAttribute.SkillLevelUpTier9 or SpecialAttribute.SkillLevelUpTier10 or SpecialAttribute.SkillLevelUpTier11 or SpecialAttribute.SkillLevelUpTier12 or SpecialAttribute.SkillLevelUpTier13 or SpecialAttribute.SkillLevelUpTier14 or SpecialAttribute.ChaosRaidAttackSpeed + or SpecialAttribute.ChaosRaidAccuracy or SpecialAttribute.ChaosRaidHp or SpecialAttribute.PetTrapReward) { + for (int i = 0; i < 10; i++) { + (int value, int weight) = mergeOptionEntry[i]; + if (value == 0) { + continue; + } + values.Add(new ItemMergeTable.Range(min + 1, value)); + rates.Add(new ItemMergeTable.Range(0, 0)); + weights.Add(weight); + min = value; + } + } else { + for (int i = 0; i < 10; i++) { + (int value, int weight) = mergeOptionEntry[i]; + if (value == 0) { + continue; + } + rates.Add(new ItemMergeTable.Range(min + 1, value)); + values.Add(new ItemMergeTable.Range(0, 0)); + weights.Add(weight); + min = value; + } + } + + specialOptions[specialAttribute] = new ItemMergeTable.Option( + Values: values.ToArray(), + Rates: rates.ToArray(), + Weights: weights.ToArray()); + } + } + var slot = new ItemMergeTable.Entry( + Slot: slotEntry.part, + MesoCost: slotEntry.consumeMeso, + Materials: ingredients.ToArray(), + BasicOptions: basicOptions, + SpecialOptions: specialOptions + ); + + slots.Add(slotEntry.part, slot); + } + results.Add(id, slots); + } + // Hardcoding values seeing as the missing ids here are utilizing table id 37000055 + for (int i = 37000056; i < 37000064; i++) { + if (results.TryGetValue(37000055, out Dictionary? dictionary)) { + results.Add(i, dictionary); + } + } + return new ItemMergeTable(results); + + ItemComponent? ParseItemMaterial(string[] itemMaterial) { + var tag = ItemTag.None; + if (itemMaterial.Length > 0) { + string[] item = itemMaterial[0].Split(':'); + if (item.Length == 2) { + tag = Enum.TryParse(item[1], out ItemTag itemTag) ? itemTag : ItemTag.None; + } + int itemId = int.TryParse(item[0], out int id) ? id : 0; + int rarity = int.TryParse(itemMaterial[1], out int r) ? r : 1; + int amount = int.TryParse(itemMaterial[2], out int a) ? a : 1; + if (tag != ItemTag.None || itemId > 0) { + return new ItemComponent(itemId, rarity, amount, tag); + } + } + return null; + } + } + + private ShopTable ParseShop() { + var results = new Dictionary(); + foreach ((int shopId, ShopGameInfo shopInfo) in parser.ParseShopGameInfo()) { + var entry = new ShopMetadata( + Id: shopInfo.shopID, + CategoryId: shopInfo.categoryID, + Name: shopInfo.iconName, + FrameType: (ShopFrameType) shopInfo.uiFrameType, + DisplayOnlyUsable: shopInfo.showOnlyUsableItem, + HideStats: shopInfo.hideOptionInfo, + DisplayProbability: shopInfo.showProbInfo, + IsOnlySell: shopInfo.isOnlySell, + OpenWallet: shopInfo.isOpenTokenPocket, + DisplayNew: false, // this isn't present in the table + DisableDisplayOrderSort: shopInfo.disableDisplayOrderSort, + RestockTime: string.IsNullOrEmpty(shopInfo.resetFixedTime) ? 0 : DateTime.ParseExact(shopInfo.resetFixedTime, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds(), + EnableReset: shopInfo.resetEnable, + RestockData: new ShopRestockData( + ResetType: (ResetType) shopInfo.resetType, + CurrencyType: (ShopCurrencyType) shopInfo.resetPaymentType, + ExcessCurrencyType: (ShopCurrencyType) shopInfo.resetPaymentType, // not present in the table, using resetPaymentType for now + MinItemCount: shopInfo.resetListMin, + MaxItemCount: shopInfo.resetListMax, + Price: shopInfo.resetPrice, + EnablePriceMultiplier: false, // not present in the table + DisableInstantRestock: shopInfo.resetButtonHide, + AccountWide: shopInfo.resetByAccount) + ); + results.Add(shopId, entry); + } + return new ShopTable(results); + } + + private ShopItemTable ParseShopItems() { + var results = new Dictionary>(); + foreach ((int shopId, ShopGame data) in parser.ParseShopGame()) { + var shopResults = new Dictionary(); + foreach (ShopGame.Item item in data.item) { + // Check if item exists in ItemMetadataById + if (!ItemMapper.ItemMetadataById.TryGetValue(item.id, out ItemMetadata? itemMeta)) { + continue; + } + + string[] achievementArray = string.IsNullOrEmpty(item.requireAchieve) ? [] : item.requireAchieve.Split(","); + int achievementId = 0; + int achievementRank = 0; + if (achievementArray.Length == 2) { + if (!int.TryParse(achievementArray[0], out achievementId)) { + achievementId = 0; + } + if (!int.TryParse(achievementArray[1], out achievementRank)) { + achievementRank = 1; + } + } + + byte championshipRank = 0; + short championShipJoinCount = 0; + if (item.requireChampionshipInfo.Length == 2) { + championshipRank = (byte) item.requireChampionshipInfo[0]; + championShipJoinCount = (short) item.requireChampionshipInfo[1]; + } + + var npcType = GuildNpcType.Unknown; + short guildNpcLevel = 0; + if (item.requireGuildNpc.Length == 2) { + npcType = item.requireGuildNpc[0] switch { + "goods" => GuildNpcType.Goods, + "equip" => GuildNpcType.Equip, + "gemstone" => GuildNpcType.Gemstone, + "itemMerge" => GuildNpcType.ItemMerge, + "music" => GuildNpcType.Music, + "quest" => GuildNpcType.Quest, + _ => GuildNpcType.Unknown, + }; + if (short.TryParse(item.requireGuildNpc[1], out short level)) { + guildNpcLevel = level; + } + } + + RestrictedBuyData? restrictedBuyData = null; + if (!string.IsNullOrEmpty(item.startDate) && !string.IsNullOrEmpty(item.endDate)) { + var buyTimeOfDays = new List(); + foreach (string partTime in item.partTime) { + (TimeSpan startPartTime, TimeSpan endPartTime) = ParsePartTime(partTime); + buyTimeOfDays.Add(new BuyTimeOfDay(startPartTime.Seconds, endPartTime.Seconds)); + } + restrictedBuyData = new RestrictedBuyData { + Days = item.dayOfWeek.Length == 0 ? [] : Array.ConvertAll(item.dayOfWeek, day => (ShopBuyDay) day), + TimeRanges = buyTimeOfDays, + StartTime = string.IsNullOrEmpty(item.startDate) ? 0 : DateTime.ParseExact(item.startDate, "yyyy-MM-dd-HH-mm", CultureInfo.InvariantCulture).ToEpochSeconds(), + EndTime = string.IsNullOrEmpty(item.endDate) ? 0 : DateTime.ParseExact(item.endDate, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds(), + }; + } + + var entry = new ShopItemMetadata( + Id: item.sn, + ShopId: shopId, + ItemId: item.id, + Rarity: (byte) item.grade, + Cost: new ShopCost { + Amount = (int) item.price, + ItemId = item.paymentItemID, + SaleAmount = 0, // ? + Type = (ShopCurrencyType) item.paymentType, + }, + SellCount: item.sellCount, + Category: item.category, + Requirements: new ShopItemMetadata.Requirement( + GuildTrophy: item.requireGuildTrophy, + Achievement: new ShopItemMetadata.Achievement( + Id: achievementId, + Rank: achievementRank), + Championship: new ShopItemMetadata.Championship( + Rank: championshipRank, + JoinCount: championShipJoinCount), + GuildNpc: new ShopItemMetadata.GuildNpc( + Type: npcType, + Level: guildNpcLevel), + QuestAlliance: new ShopItemMetadata.QuestAlliance( + Type: item.requireAlliance switch { + "MapleUnion" => ReputationType.MapleAlliance, + "TriaRoyalGuard" => ReputationType.RoyalGuard, + "DarkWind" => ReputationType.DarkWind, + "GreenHood" => ReputationType.GreenHood, + "LumiKnight" => ReputationType.Lumiknight, + "MapleUnion_KritiasExped" => ReputationType.KritiasMapleAlliance, + "GreenHood_KritiasExped" => ReputationType.KritiasGreenHood, + "Lumiknight_KritiasExped" => ReputationType.KritiasLumiknight, + "Georg" => ReputationType.Humanitas, + _ => ReputationType.None, + }, + Grade: item.requireAllianceGrade)), + RestrictedBuyData: restrictedBuyData, + SellUnit: (short) item.sellUnit, + Label: (ShopItemLabel) item.frameType, + IconTag: item.paymentIconTag, + WearForPreview: item.wearForPreview, + RandomOption: item.randomOption, + Probability: item.prob, + IsPremiumItem: item.premiumItem); + shopResults.Add(item.sn, entry); + } + results.Add(shopId, shopResults); + } + return new ShopItemTable(results); + } + + private BeautyShopTable ParseBeautyShops() { + var results = new Dictionary(); + results = MergeBeautyShopData(results, parser.ParseShopBeauty()); + results = MergeBeautyShopData(results, parser.ParseShopBeautyCoupon()); + results = MergeBeautyShopData(results, parser.ParseShopBeautySpecialHair()); + return new BeautyShopTable(results); + } + + private Dictionary MergeBeautyShopData(Dictionary entries, IEnumerable<(int, ShopBeauty)> beautyParser) { + foreach ((int shopId, ShopBeauty shop) in beautyParser) { + List itemGroups = []; + foreach (ShopBeauty.ItemGroup group in shop.itemGroup) { + itemGroups.Add(new BeautyShopItemGroup( + StartTime: string.IsNullOrEmpty(group.saleStartTime) ? 0 : DateTime.ParseExact(group.saleStartTime, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds(), + Items: ParseBeautyShopItems(group.item).ToArray())); + } + + entries.Add(shopId, new BeautyShopMetadata( + Id: shop.shopID, + Category: (BeautyShopCategory) shop.categoryID, + SubType: shop.shopID switch { + // Hardcoding this because I'm not sure where this information is located in the xmls + 500 => 16, + 501 => 19, + 504 => 17, + 505 => 28, + 506 => 18, + 508 => 21, + 509 => 0, + 510 => 20, + _ => 0, + }, + StyleCostMetadata: new BeautyShopCostMetadata( + CurrencyType: (ShopCurrencyType) shop.stylePaymentType, + Price: shop.stylePrice, + Icon: shop.stylePaymentIconTag, + PaymentItemId: shop.stylePaymentItemID), + ColorCostMetadata: new BeautyShopCostMetadata( + CurrencyType: (ShopCurrencyType) shop.colorPaymentType, + Price: shop.colorPrice, + Icon: shop.colorPaymentIconTag, + PaymentItemId: shop.colorPaymentItemID), + IsRandom: shop.random, + IsByItem: shop.byItem, + ReturnCouponId: shop.returnCouponID, + CouponId: shop.displayCouponID, + CouponTag: Enum.TryParse(shop.couponTag, out ItemTag tag) ? tag : ItemTag.None, + Items: ParseBeautyShopItems(shop.item).ToArray(), + ItemGroups: itemGroups.ToArray())); + } + return entries; + + IEnumerable ParseBeautyShopItems(IList items) { + foreach (ShopBeauty.Item item in items) { + yield return new BeautyShopItem( + Id: item.id, + Cost: new BeautyShopCostMetadata( + CurrencyType: (ShopCurrencyType) item.paymentType, + Price: item.price, + Icon: item.paymentIconTag, + PaymentItemId: item.paymentItemID), + Weight: item.weight, + AchievementId: item.achieveID, + AchievementRank: (byte) item.achieveGrade, + RequiredLevel: item.requireLevel, + SaleTag: (ShopItemLabel) item.saleTag); + } + } + } + + private MeretMarketTable ParseMeretCustomShop() { + var results = new Dictionary(); + foreach ((int id, ShopMeretCustom entry) in parser.ParseShopMeretCustom()) { + foreach (ShopMeretCustom addQuantity in entry.additionalQuantity) { + results.Add(addQuantity.id, ParseMarketItemMetadata(addQuantity, entry)); + } + results.Add(id, ParseMarketItemMetadata(entry)); + } + return new MeretMarketTable(results); + + MeretMarketItemMetadata ParseMarketItemMetadata(ShopMeretCustom item, ShopMeretCustom? parent = null) { + string saleStartTime = string.IsNullOrEmpty(parent?.saleStartTime) ? item.saleStartTime : parent.saleStartTime; + string saleEndTime = string.IsNullOrEmpty(parent?.saleEndTime) ? item.saleEndTime : parent.saleEndTime; + string promoStartTime = string.IsNullOrEmpty(parent?.promoSaleStartTime) ? item.promoSaleStartTime : parent.promoSaleStartTime; + string promoEndTime = string.IsNullOrEmpty(parent?.promoSaleEndTime) ? item.promoSaleEndTime : parent.promoSaleEndTime; + int[] jobRequirement = parent?.jobRequire ?? item.jobRequire; + return new MeretMarketItemMetadata( + Id: item.id, + ParentId: parent?.id ?? 0, + TabId: parent?.tabID ?? item.tabID, + Banner: item.banner, + BannerTag: (MeretMarketBannerTag) item.bannerTag, + ItemId: parent?.itemID ?? item.itemID, + Rarity: (byte) (parent?.grade ?? item.grade), + Quantity: item.quantity, + BonusQuantity: item.bonusQuantity, + DurationInDays: item.durationDay, + SaleTag: (MeretMarketItemSaleTag) item.saleTag, + CurrencyType: (MeretMarketCurrencyType) (parent?.paymentType ?? item.paymentType), + Price: item.price, + SalePrice: item.salePrice == 0 ? item.price : item.salePrice, + SaleStartTime: string.IsNullOrEmpty(saleStartTime) ? 0 : DateTime.ParseExact(saleStartTime, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds(), + SaleEndTime: string.IsNullOrEmpty(saleEndTime) ? 0 : DateTime.ParseExact(saleEndTime, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds(), + JobRequirement: jobRequirement.Select(job => (JobCode) job).AsEnumerable().FilterFlags(), + RestockUnavailable: parent?.noRestock ?? item.noRestock, + RequireMinLevel: parent?.minLevel ?? item.minLevel, + RequireMaxLevel: parent?.maxLevel ?? item.maxLevel, + RequireAchievementId: parent?.achieveID ?? item.achieveID, + RequireAchievementRank: parent?.achieveGrade ?? item.achieveGrade, + PcCafe: parent?.pcCafe ?? item.pcCafe, + Giftable: parent?.giftable ?? item.giftable, + ShowSaleTime: item.showSaleTime, + PromoName: item.promoName, + PromoStartTime: string.IsNullOrEmpty(promoStartTime) ? 0 : DateTime.ParseExact(promoStartTime, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds(), + PromoEndTime: string.IsNullOrEmpty(promoEndTime) ? 0 : DateTime.ParseExact(promoEndTime, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture).ToEpochSeconds()); + } + } + + private FishTable ParseFish() { + // Parse Fish + var fishes = new Dictionary(); + foreach ((int id, Fish fish) in parser.ParseFish()) { + if (!Enum.TryParse(fish.habitat, out LiquidType liquidType)) { + liquidType = LiquidType.all; + } + + int[] smallSize = fish.smallSize.Split("-").Select(int.Parse).ToArray(); + int[] bigSize = fish.bigSize.Split("-").Select(int.Parse).ToArray(); + fishes.Add(id, new FishTable.Fish( + Id: id, + FluidHabitat: liquidType, + Mastery: fish.fishMastery, + Level: fish.lv, + Rarity: fish.rank, + PointCount: fish.pointCount, + MasteryExp: fish.masteryPoint, + Exp: fish.exp, + FishingTime: fish.fishingTime, + CatchProbability: fish.catchProp, + BaitProbability: fish.baitProp, + SmallSize: new FishTable.Range(smallSize[0], smallSize[1]), + BigSize: new FishTable.Range(bigSize[0], bigSize[1]), + BaitEffectIds: fish.bait, + IndividualDropBoxId: fish.individualDropBoxID, + IgnoreSpotMastery: fish.ignoreSpotMastery)); + } + + // Parse Spots + var spots = new Dictionary(); + foreach ((int mapId, FishingSpot spot) in parser.ParseFishingSpot()) { + var liquidTypes = new List(); + foreach (string liquidType in spot.liquidType) { + if (Enum.TryParse(liquidType, out LiquidType type)) { + liquidTypes.Add(type); + } + } + + spots.Add(mapId, new FishTable.Spot( + Id: mapId, + MinMastery: spot.minMastery, + MaxMastery: spot.maxMastery, + LiquidTypes: liquidTypes, + GlobalFishBoxId: spot.globalFishBoxID, + IndividualFishBoxId: spot.individualFishBoxID, + GlobalDropBoxId: spot.globalDropBoxId, + IndividualDropBoxId: spot.individualDropBoxId, + SpotLevel: spot.spotLevel, + DropRank: spot.spotDropRank ? 1 : 0)); // TODO: Change this from a bool to int + } + + // Parse Lure + var lures = new Dictionary(); + foreach ((int id, FishLure lure) in parser.ParseFishLure()) { + lures.Add(id, new FishTable.Lure( + BuffId: lure.fishCode, + BuffLevel: (short) lure.additionalEffectLevel, + Catches: lure.catchRank.Select((t, i) => new FishTable.Lure.Catch(Rank: t, Probability: lure.catchProp[i])).ToArray(), + Spawns: lure.spawnRank.Select((t, i) => new FishTable.Lure.Spawn(FishId: t, Rate: lure.spawnProp[i])).ToArray(), + GlobalDropBoxId: lure.globalDropBoxID, + GlobalDropRank: lure.globalDropRank, + IndividualDropBoxId: lure.individualDropBoxID, + IndividualDropRank: lure.individualDropRank)); + } + + // Global Fish Boxes + Dictionary globalBoxes = ParseFishBox(parser.ParseGlobalFishBox()); + Dictionary individualBoxes = ParseFishBox(parser.ParseIndividualFishBox()); + + return new FishTable(fishes, spots, lures, globalBoxes, individualBoxes); + + Dictionary ParseFishBox(IEnumerable<(int, FishBox)> boxes) { + var results = new Dictionary(); + foreach ((int id, FishBox box) in boxes) { + Dictionary fishes = []; + foreach (FishBox.Fish data in box.fish) { + fishes[data.fishCode] = data.weight; + } + + results.Add(id, new FishTable.FishBox( + Id: id, + Probability: box.probability, + CubeRate: box.cubeRate, + Fishes: fishes)); + } + return results; + } + } + + private CombineSpawnTable ParseCombineSpawn() { + var groupDict = new Dictionary>(); + var npcDict = new Dictionary>(); + var objectDict = new Dictionary>(); + + foreach ((int id, SpawnGroup spawnGroup) in parser.ParseSpawnGroup()) { + var groupMetadata = new SpawnGroupMetadata( + GroupId: spawnGroup.groupId, + Type: Enum.TryParse(spawnGroup.groupType, out CombineSpawnGroupType type) ? type : CombineSpawnGroupType.none, + TotalCount: spawnGroup.combineCount, + ResetTick: spawnGroup.resetTick, + MapId: spawnGroup.fieldId); + + if (!groupDict.TryGetValue(spawnGroup.fieldId, out Dictionary? value)) { + value = new Dictionary(); + groupDict[spawnGroup.fieldId] = value; + } + + value[spawnGroup.groupId] = groupMetadata; + } + + foreach ((int id, SpawnNpc spawn) in parser.ParseSpawnNpc()) { + var spawnMetadata = new SpawnNpcMetadata( + CombineId: spawn.combineId, + GroupId: spawn.groupId, + Weight: spawn.weight, + SpawnId: spawn.spawnId); + + if (!npcDict.TryGetValue(spawn.groupId, out Dictionary? value)) { + value = new Dictionary(); + npcDict[spawn.groupId] = value; + } + + value[spawn.combineId] = spawnMetadata; + } + + foreach ((int id, SpawnInteractObject interactObject) in parser.ParseSpawnInteractObject()) { + var objectMetadata = new SpawnInteractObjectMetadata( + CombineId: interactObject.combineId, + GroupId: interactObject.groupId, + Weight: interactObject.weight, + RegionSpawnId: interactObject.regionSpawnId, + InteractId: interactObject.interactId, + Model: interactObject.model, + Asset: interactObject.asset, + Normal: interactObject.normal, + Reactable: interactObject.reactable, + Scale: interactObject.scale, + KeepAnimate: interactObject.isKeepAnimate); + + if (!objectDict.TryGetValue(interactObject.groupId, out Dictionary? value)) { + value = new Dictionary(); + objectDict[interactObject.groupId] = value; + } + + value[interactObject.combineId] = objectMetadata; + } + + return new CombineSpawnTable(groupDict, npcDict, objectDict); + } + + private EnchantOptionTable ParseEnchantOption() { + var results = new Dictionary(); + foreach ((int id, EnchantOption enchantOption) in parser.ParseEnchantOption()) { + IList basicAttributes = []; + foreach (int option in enchantOption.option) { + if (!Enum.TryParse(option.ToString(), out BasicAttribute basicAttribute)) { + Console.WriteLine($"Failed to parse basic attribute {option}"); + continue; + } + basicAttributes.Add(basicAttribute); + } + results.Add(id, new EnchantOptionMetadata( + Id: id, + Slot: enchantOption.slot, + EnchantLevel: enchantOption.grade, + Rarity: (short) enchantOption.rank, + Rate: enchantOption.rate, + MinLevel: enchantOption.minLv, + MaxLevel: enchantOption.maxLv, + Attributes: basicAttributes.ToArray())); + } + return new EnchantOptionTable(results); + } + + private ScriptEventConditionTable ParseScriptEventConditionTable() { + var results = new Dictionary>(); + + foreach ((int eventId, ScriptEventCondition scriptEventCondition) in parser.ParseScriptEventCondition()) { + ScriptEventType type = scriptEventCondition.type switch { + Parser.Enum.ScriptEventType.enchant_fail => ScriptEventType.EnchantFail, + Parser.Enum.ScriptEventType.enchant_item_select => ScriptEventType.EnchantSelect, + Parser.Enum.ScriptEventType.enchant_complete => ScriptEventType.EnchantComplete, + Parser.Enum.ScriptEventType.merge_select => ScriptEventType.EmpowerSelect, + Parser.Enum.ScriptEventType.merge_try => ScriptEventType.EmpowerTry, + Parser.Enum.ScriptEventType.merge_result => ScriptEventType.EmpowerResult, + Parser.Enum.ScriptEventType.remake_fail => ScriptEventType.RerollFail, + Parser.Enum.ScriptEventType.remake_item_select => ScriptEventType.RerollItemSelect, + Parser.Enum.ScriptEventType.remake_option_select => ScriptEventType.RerollOptionSelect, + Parser.Enum.ScriptEventType.remake_complete => ScriptEventType.RerollComplete, + _ => ScriptEventType.EnchantFail, + }; + + List enchantLevels = []; + if (scriptEventCondition.enchantLevel.Contains('-')) { + string[] enchantLevelSplit = scriptEventCondition.enchantLevel.Split('-'); + int startInt = int.TryParse(enchantLevelSplit[0], out int start) ? start : 0; + int endInt = int.TryParse(enchantLevelSplit[1], out int end) ? end : 0; + + for (int i = startInt; i <= endInt; i++) { + enchantLevels.Add(i); + } + } else { + if (!int.TryParse(scriptEventCondition.enchantLevel, out int enchantLevel)) { + if (scriptEventCondition.enchantLevel == "MAX") { + enchantLevels.Add(15); + } + } else { + enchantLevels.Add(enchantLevel); + } + } + + if (!results.TryGetValue(type, out Dictionary? value)) { + value = new Dictionary(); + results[type] = value; + } + + value[scriptEventCondition.id] = new ScriptEventConditionMetadata( + Id: scriptEventCondition.id, + EventType: type, + ErrorCode: (ItemEnchantError) scriptEventCondition.enchantError, + Rarity: (short) scriptEventCondition.rank, + EnchantLevel: enchantLevels.ToArray(), + FailCount: scriptEventCondition.failCount, + DamageType: (EnchantDamageType) scriptEventCondition.isDamaged, + ResultType: (EnchantResult) scriptEventCondition.result + ); + } + return new ScriptEventConditionTable(results); + } + + private UnlimitedEnchantOptionTable ParseUnlimitedEnchantOption() { + var results = new Dictionary>(); + foreach ((int slot, IDictionary enchantOptions) in parser.ParseUnlimitedEnchantOption()) { + var levelDictionary = new Dictionary(); + foreach ((int[] optionLevel, UnlimitedEnchantOption enchantOption) in enchantOptions) { + int minLevel = optionLevel[0]; + int maxLevel = optionLevel.Length > 1 ? optionLevel[1] : minLevel; + + for (int level = minLevel; level <= maxLevel; level++) { + Dictionary values = []; + Dictionary rates = []; + Dictionary specialValues = []; + Dictionary specialRates = []; + AddBasic(values, rates, (BasicAttribute) enchantOption.option1, enchantOption.value1, enchantOption.rate1); + AddBasic(values, rates, (BasicAttribute) enchantOption.option2, enchantOption.value2, enchantOption.rate2); + AddBasic(values, rates, (BasicAttribute) enchantOption.option3, enchantOption.value3, enchantOption.rate3); + AddBasic(values, rates, (BasicAttribute) enchantOption.option4, enchantOption.value4, enchantOption.rate4); + AddSpecial(specialValues, specialRates, (SpecialAttribute) enchantOption.sa_option1, enchantOption.sa_value1, enchantOption.sa_rate1); + AddSpecial(specialValues, specialRates, (SpecialAttribute) enchantOption.sa_option2, enchantOption.sa_value2, enchantOption.sa_rate2); + + levelDictionary[level] = new UnlimitedEnchantOptionTable.Option( + Values: values, + Rates: rates, + SpecialValues: specialValues, + SpecialRates: specialRates); + + } + } + results[slot] = levelDictionary; + } + return new UnlimitedEnchantOptionTable(results); + + void AddBasic(Dictionary values, Dictionary rates, BasicAttribute attribute, int value, float rate) { + if (value != 0) { + values.Add(attribute, value); + } + if (rate != 0) { + rates.Add(attribute, rate); + } + } + + void AddSpecial(Dictionary values, Dictionary rates, SpecialAttribute attribute, int value, float rate) { + if (attribute == SpecialAttribute.None) { + return; + } + if (value != 0) { + values.Add(attribute, value); + } + if (rate != 0) { + rates.Add(attribute, rate); + } + } + } +} diff --git a/Maple2.File.Ingest/Mapper/SkillMapper.cs b/Maple2.File.Ingest/Mapper/SkillMapper.cs index b57ad891c..e02a35cfe 100644 --- a/Maple2.File.Ingest/Mapper/SkillMapper.cs +++ b/Maple2.File.Ingest/Mapper/SkillMapper.cs @@ -1,171 +1,171 @@ -using System.ComponentModel; -using System.Diagnostics; -using System.Reflection; -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Xml.Skill; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; - -namespace Maple2.File.Ingest.Mapper; - -public class SkillMapper : TypeMapper { - private readonly SkillParser parser; - - public SkillMapper(M2dReader xmlReader, string language) { - parser = new SkillParser(xmlReader, language); - } - - protected override IEnumerable Map() { - List magicPaths = []; - List cubeMagicPaths = []; - foreach ((int id, string name, SkillData data) in parser.Parse()) { - if (data.basic == null) continue; // Old_JobChange_01 - Debug.Assert(data.basic.kinds.groupIDs.Length <= 1); - - - // Note: 90000775 has cubeMagicPathID="2147483647" which should be cubeMagicPathID="9000073111" - Dictionary levels = data.level.ToDictionary( - level => level.value, - level => new SkillMetadataLevel( - Condition: level.beginCondition.Convert(), - Change: level.changeSkill?.Convert(), - AutoTargeting: level.autoTargeting?.Convert(), - Consume: new SkillMetadataConsume( - Meso: level.consume.money, - UseItem: level.consume.useItem, - HpRate: level.consume.hpRate, - Stat: level.consume.stat.ToDictionary()), - Detect: new SkillMetadataDetect( - IncludeCaster: level.detectProperty?.includeCaster == 1, - Distance: level.detectProperty?.distance ?? 0), - Recovery: new SkillMetadataRecovery( - SpValue: level.recoveryProperty.spValue, - SpRate: level.recoveryProperty.spRate), - Skills: level.conditionSkill.Select(skill => skill.Convert()).ToArray(), - Motions: level.motion.Select(motion => new SkillMetadataMotion( - new SkillMetadataMotionProperty( - SequenceName: motion.motionProperty.sequenceName, - SequenceSpeed: motion.motionProperty.sequenceSpeed, - MoveDistance: motion.motionProperty.movedistance, - FaceTarget: motion.motionProperty.faceTarget), - Attacks: motion.attack.Select(attack => new SkillMetadataAttack( - Point: attack.point, - PointGroup: attack.pointGroupID, - TargetCount: attack.targetCount, - MagicPathId: attack.magicPathID, - CubeMagicPathId: attack.cubeMagicPathID == int.MaxValue ? 9000073111 : attack.cubeMagicPathID, - HitImmuneBreak: attack.hitImmuneBreak, - BrokenOffence: attack.brokenOffence, - CompulsionTypes: attack.compulsionType.Select(type => (CompulsionType) type).ToArray(), - Pet: attack.petTamingProperty != null ? new SkillMetadataPet( - TamingGroup: attack.petTamingProperty.tamingGroup, - TrapLevel: attack.petTamingProperty.trapLevel, - TamingPoint: attack.petTamingProperty.tamingPoint, - ForcedTaming: attack.petTamingProperty.forcedTaming - ) : null, - Range: Convert(attack.rangeProperty), - Arrow: new SkillMetadataArrow( - Overlap: attack.arrowProperty.overlap, - Explosion: attack.arrowProperty.explosion, - RayPhysXTest: attack.arrowProperty.rayPhysxTest, - NonTarget: (SkillTargetType) attack.arrowProperty.nonTarget, - BounceType: (BounceType) attack.arrowProperty.bounceType, - BounceCount: attack.arrowProperty.bounceCount, - BounceRadius: attack.arrowProperty.bounceRadius, - BounceOverlap: attack.arrowProperty.bounceType > 0 && attack.arrowProperty.bounceOverlap, - Collision: attack.arrowProperty.collision, - CollisionAdd: attack.arrowProperty.collisionAdd, - RayType: attack.arrowProperty.rayType), - Damage: new SkillMetadataDamage( - Count: attack.damageProperty.count, - Rate: attack.damageProperty.rate, - HitSpeed: attack.damageProperty.hitSpeedRate, - HitDelay: attack.damageProperty.hitPauseTime, - IsConstDamage: attack.damageProperty.isConstDamageValue != 0, - Value: attack.damageProperty.value, - DamageByTargetMaxHp: attack.damageProperty.damageByTargetMaxHP, - SuperArmorBreak: attack.damageProperty.superArmorBreak, - Push: attack.damageProperty.push > 0 ? new SkillMetadataPush( - Type: attack.damageProperty.push, - EaseType: attack.damageProperty.pushEaseType, - ApplyField: attack.damageProperty.pushApplyField, - Distance: attack.damageProperty.pushdistance, - UpDistance: attack.damageProperty.pushUpDistance, - Down: attack.damageProperty.pushDown, - Fall: attack.damageProperty.pushFall, - Duration: attack.damageProperty.pushduration, - Probability: attack.damageProperty.pushprob, - Priority: attack.damageProperty.pushPriority, - PriorityHitImmune: attack.damageProperty.pushPriorityHitImmune - ) : null - ), - Skills: attack.conditionSkill.Where(skill => !skill.dependOnDamageCount).Select(skill => skill.Convert()).ToArray(), - SkillsOnDamage: attack.conditionSkill.Where(skill => skill.dependOnDamageCount).Select(skill => skill.Convert()).ToArray() - )).ToArray(), - AttackPoints: motion.CollectAttackPoints() - )).ToArray() - )); - - yield return new StoredSkillMetadata( - Id: id, - Name: name, - Property: new SkillMetadataProperty( - Type: (SkillType) data.basic.kinds.type, - SubType: (SkillSubType) data.basic.kinds.subType, - RangeType: (RangeType) data.basic.kinds.rangeType, - AttackType: (AttackType) data.basic.ui.attackType, - Element: (Element) data.basic.kinds.element, - State: string.IsNullOrEmpty(data.basic.kinds.state) - ? ActorState.None - : Enum.GetValues() - .FirstOrDefault(enumValue => - enumValue.GetType() - .GetField(enumValue.ToString()) - ?.GetCustomAttribute() - ?.Description == data.basic.kinds.state), - ContinueSkill: data.basic.kinds.continueSkill, - SpRecoverySkill: data.basic.kinds.spRecoverySkill, - ImmediateActive: data.basic.kinds.immediateActive, - UnrideOnHit: data.basic.kinds.unrideOnHit, - UnrideOnUse: data.basic.kinds.unrideOnUse, - ReleaseObjectWeapon: data.basic.kinds.releaseObjectWeapon, - Emotion: data.basic.kinds.emotion, - SkillGroup: data.basic.kinds.groupIDs.FirstOrDefault(), - MaxLevel: levels.Keys.Max()), - State: new SkillMetadataState( - InBattle: data.basic.stateAttr.battle == 1, - SuperArmor: (SuperArmor) data.basic.stateAttr.superArmor, - UseInGameTime: data.basic.stateAttr.useInGameTime == 1, - IgnoreReduceCooldown: data.basic.stateAttr.ignoreReduceCooldown == 1, - CooldownGroupId: data.basic.stateAttr.cooldownGroupID, - RechargeMaxCount: data.basic.stateAttr.rechargeMaxCount), - Levels: levels - ); - } - } - - private static SkillMetadataRange Convert(RegionSkill region) { - return new SkillMetadataRange( - Type: region.rangeType switch { - "box" => SkillRegion.Box, - "cylinder" => SkillRegion.Cylinder, - "circle" => SkillRegion.Cylinder, - "frustum" => SkillRegion.Frustum, - "hole_cylinder" => SkillRegion.HoleCylinder, - "1200" => SkillRegion.None, // skill/60/60012051.xml - _ => SkillRegion.None, - }, - Distance: region.distance, - Height: region.height, - Width: region.height, - EndWidth: region.endWidth, - RotateZDegree: region.rangeZRotateDegree, - RangeAdd: region.rangeAdd, - RangeOffset: region.rangeOffset, - IncludeCaster: (SkillTargetType) region.includeCaster, - ApplyTarget: (ApplyTargetType) region.applyTarget, - CastTarget: (SkillTargetType) region.castTarget - ); - } -} +using System.ComponentModel; +using System.Diagnostics; +using System.Reflection; +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Xml.Skill; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; + +namespace Maple2.File.Ingest.Mapper; + +public class SkillMapper : TypeMapper { + private readonly SkillParser parser; + + public SkillMapper(M2dReader xmlReader, string language) { + parser = new SkillParser(xmlReader, language); + } + + protected override IEnumerable Map() { + List magicPaths = []; + List cubeMagicPaths = []; + foreach ((int id, string name, SkillData data) in parser.Parse()) { + if (data.basic == null) continue; // Old_JobChange_01 + Debug.Assert(data.basic.kinds.groupIDs.Length <= 1); + + + // Note: 90000775 has cubeMagicPathID="2147483647" which should be cubeMagicPathID="9000073111" + Dictionary levels = data.level.ToDictionary( + level => level.value, + level => new SkillMetadataLevel( + Condition: level.beginCondition.Convert(), + Change: level.changeSkill?.Convert(), + AutoTargeting: level.autoTargeting?.Convert(), + Consume: new SkillMetadataConsume( + Meso: level.consume.money, + UseItem: level.consume.useItem, + HpRate: level.consume.hpRate, + Stat: level.consume.stat.ToDictionary()), + Detect: new SkillMetadataDetect( + IncludeCaster: level.detectProperty?.includeCaster == 1, + Distance: level.detectProperty?.distance ?? 0), + Recovery: new SkillMetadataRecovery( + SpValue: level.recoveryProperty.spValue, + SpRate: level.recoveryProperty.spRate), + Skills: level.conditionSkill.Select(skill => skill.Convert()).ToArray(), + Motions: level.motion.Select(motion => new SkillMetadataMotion( + new SkillMetadataMotionProperty( + SequenceName: motion.motionProperty.sequenceName, + SequenceSpeed: motion.motionProperty.sequenceSpeed, + MoveDistance: motion.motionProperty.movedistance, + FaceTarget: motion.motionProperty.faceTarget), + Attacks: motion.attack.Select(attack => new SkillMetadataAttack( + Point: attack.point, + PointGroup: attack.pointGroupID, + TargetCount: attack.targetCount, + MagicPathId: attack.magicPathID, + CubeMagicPathId: attack.cubeMagicPathID == int.MaxValue ? 9000073111 : attack.cubeMagicPathID, + HitImmuneBreak: attack.hitImmuneBreak, + BrokenOffence: attack.brokenOffence, + CompulsionTypes: attack.compulsionType.Select(type => (CompulsionType) type).ToArray(), + Pet: attack.petTamingProperty != null ? new SkillMetadataPet( + TamingGroup: attack.petTamingProperty.tamingGroup, + TrapLevel: attack.petTamingProperty.trapLevel, + TamingPoint: attack.petTamingProperty.tamingPoint, + ForcedTaming: attack.petTamingProperty.forcedTaming + ) : null, + Range: Convert(attack.rangeProperty), + Arrow: new SkillMetadataArrow( + Overlap: attack.arrowProperty.overlap, + Explosion: attack.arrowProperty.explosion, + RayPhysXTest: attack.arrowProperty.rayPhysxTest, + NonTarget: (SkillTargetType) attack.arrowProperty.nonTarget, + BounceType: (BounceType) attack.arrowProperty.bounceType, + BounceCount: attack.arrowProperty.bounceCount, + BounceRadius: attack.arrowProperty.bounceRadius, + BounceOverlap: attack.arrowProperty.bounceType > 0 && attack.arrowProperty.bounceOverlap, + Collision: attack.arrowProperty.collision, + CollisionAdd: attack.arrowProperty.collisionAdd, + RayType: attack.arrowProperty.rayType), + Damage: new SkillMetadataDamage( + Count: attack.damageProperty.count, + Rate: attack.damageProperty.rate, + HitSpeed: attack.damageProperty.hitSpeedRate, + HitDelay: attack.damageProperty.hitPauseTime, + IsConstDamage: attack.damageProperty.isConstDamageValue != 0, + Value: attack.damageProperty.value, + DamageByTargetMaxHp: attack.damageProperty.damageByTargetMaxHP, + SuperArmorBreak: attack.damageProperty.superArmorBreak, + Push: attack.damageProperty.push > 0 ? new SkillMetadataPush( + Type: attack.damageProperty.push, + EaseType: attack.damageProperty.pushEaseType, + ApplyField: attack.damageProperty.pushApplyField, + Distance: attack.damageProperty.pushdistance, + UpDistance: attack.damageProperty.pushUpDistance, + Down: attack.damageProperty.pushDown, + Fall: attack.damageProperty.pushFall, + Duration: attack.damageProperty.pushduration, + Probability: attack.damageProperty.pushprob, + Priority: attack.damageProperty.pushPriority, + PriorityHitImmune: attack.damageProperty.pushPriorityHitImmune + ) : null + ), + Skills: attack.conditionSkill.Where(skill => !skill.dependOnDamageCount).Select(skill => skill.Convert()).ToArray(), + SkillsOnDamage: attack.conditionSkill.Where(skill => skill.dependOnDamageCount).Select(skill => skill.Convert()).ToArray() + )).ToArray(), + AttackPoints: motion.CollectAttackPoints() + )).ToArray() + )); + + yield return new StoredSkillMetadata( + Id: id, + Name: name, + Property: new SkillMetadataProperty( + Type: (SkillType) data.basic.kinds.type, + SubType: (SkillSubType) data.basic.kinds.subType, + RangeType: (RangeType) data.basic.kinds.rangeType, + AttackType: (AttackType) data.basic.ui.attackType, + Element: (Element) data.basic.kinds.element, + State: string.IsNullOrEmpty(data.basic.kinds.state) + ? ActorState.None + : Enum.GetValues() + .FirstOrDefault(enumValue => + enumValue.GetType() + .GetField(enumValue.ToString()) + ?.GetCustomAttribute() + ?.Description == data.basic.kinds.state), + ContinueSkill: data.basic.kinds.continueSkill, + SpRecoverySkill: data.basic.kinds.spRecoverySkill, + ImmediateActive: data.basic.kinds.immediateActive, + UnrideOnHit: data.basic.kinds.unrideOnHit, + UnrideOnUse: data.basic.kinds.unrideOnUse, + ReleaseObjectWeapon: data.basic.kinds.releaseObjectWeapon, + Emotion: data.basic.kinds.emotion, + SkillGroup: data.basic.kinds.groupIDs.FirstOrDefault(), + MaxLevel: levels.Keys.Max()), + State: new SkillMetadataState( + InBattle: data.basic.stateAttr.battle == 1, + SuperArmor: (SuperArmor) data.basic.stateAttr.superArmor, + UseInGameTime: data.basic.stateAttr.useInGameTime == 1, + IgnoreReduceCooldown: data.basic.stateAttr.ignoreReduceCooldown == 1, + CooldownGroupId: data.basic.stateAttr.cooldownGroupID, + RechargeMaxCount: data.basic.stateAttr.rechargeMaxCount), + Levels: levels + ); + } + } + + private static SkillMetadataRange Convert(RegionSkill region) { + return new SkillMetadataRange( + Type: region.rangeType switch { + "box" => SkillRegion.Box, + "cylinder" => SkillRegion.Cylinder, + "circle" => SkillRegion.Cylinder, + "frustum" => SkillRegion.Frustum, + "hole_cylinder" => SkillRegion.HoleCylinder, + "1200" => SkillRegion.None, // skill/60/60012051.xml + _ => SkillRegion.None, + }, + Distance: region.distance, + Height: region.height, + Width: region.height, + EndWidth: region.endWidth, + RotateZDegree: region.rangeZRotateDegree, + RangeAdd: region.rangeAdd, + RangeOffset: region.rangeOffset, + IncludeCaster: (SkillTargetType) region.includeCaster, + ApplyTarget: (ApplyTargetType) region.applyTarget, + CastTarget: (SkillTargetType) region.castTarget + ); + } +} diff --git a/Maple2.File.Ingest/Mapper/TableMapper.cs b/Maple2.File.Ingest/Mapper/TableMapper.cs index 05826a4cf..4013c902a 100644 --- a/Maple2.File.Ingest/Mapper/TableMapper.cs +++ b/Maple2.File.Ingest/Mapper/TableMapper.cs @@ -1,1826 +1,1826 @@ -using System.Diagnostics; -using System.Globalization; -using System.Numerics; -using Maple2.Database.Extensions; -using Maple2.File.Ingest.Utils; -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Enum; -using Maple2.File.Parser.Xml; -using Maple2.File.Parser.Xml.Table; -using Maple2.Model.Common; -using Maple2.Model.Enum; -using Maple2.Model.Game; -using Maple2.Model.Metadata; -using Maple2.Tools.Extensions; -using DayOfWeek = System.DayOfWeek; -using ChatSticker = Maple2.File.Parser.Xml.Table.ChatSticker; -using DungeonCooldownType = Maple2.Model.Enum.DungeonCooldownType; -using DungeonGroupType = Maple2.Model.Enum.DungeonGroupType; -using DungeonPlayType = Maple2.Model.Enum.DungeonPlayType; -using ExpType = Maple2.Model.Enum.ExpType; -using GuildBuff = Maple2.File.Parser.Xml.Table.GuildBuff; -using GuildNpc = Maple2.File.Parser.Xml.Table.GuildNpc; -using GuildNpcType = Maple2.Model.Enum.GuildNpcType; -using InteractObject = Maple2.File.Parser.Xml.Table.InteractObject; -using ItemOptionConstant = Maple2.Model.Metadata.ItemOptionConstant; -using ItemSocket = Maple2.File.Parser.Xml.Table.ItemSocket; -using JobTable = Maple2.Model.Metadata.JobTable; -using MagicPath = Maple2.Model.Metadata.MagicPath; -using MasteryType = Maple2.Model.Enum.MasteryType; -using MeretMarketCategory = Maple2.File.Parser.Xml.Table.MeretMarketCategory; -using WeddingHall = Maple2.File.Parser.Xml.Table.WeddingHall; -using WeddingPackage = Maple2.Model.Metadata.WeddingPackage; -using WeddingReward = Maple2.Model.Metadata.WeddingReward; - -namespace Maple2.File.Ingest.Mapper; - -public class TableMapper : TypeMapper { - private readonly TableParser parser; - private readonly ItemOptionParser optionParser; - private readonly string language; - - public TableMapper(M2dReader xmlReader, string language) { - parser = new TableParser(xmlReader, language); - optionParser = new ItemOptionParser(xmlReader); - } - - protected override IEnumerable Map() { - yield return new TableMetadata { Name = TableNames.CHAT_EMOTICON, Table = ParseChatSticker() }; - yield return new TableMetadata { Name = TableNames.DEFAULT_ITEMS, Table = ParseDefaultItems() }; - yield return new TableMetadata { Name = TableNames.ITEM_BREAK_INGREDIENT, Table = ParseItemBreakIngredient() }; - yield return new TableMetadata { Name = TableNames.ITEM_GEMSTONE_UPGRADE, Table = ParseItemGemstoneUpgrade() }; - yield return new TableMetadata { Name = TableNames.ITEM_EXTRACTION, Table = ParseItemExtraction() }; - yield return new TableMetadata { Name = TableNames.JOB, Table = ParseJobTable() }; - yield return new TableMetadata { Name = TableNames.MAGIC_PATH, Table = ParseMagicPath() }; - yield return new TableMetadata { Name = TableNames.INSTRUMENT_CATEGORY_INFO, Table = ParseInstrument() }; - yield return new TableMetadata { Name = TableNames.INTERACT_OBJECT, Table = ParseInteractObject() }; - yield return new TableMetadata { Name = TableNames.ITEM_LAPENSHARD_UPGRADE, Table = ParseLapenshardUpgradeTable() }; - yield return new TableMetadata { Name = TableNames.ITEM_SOCKET, Table = ParseItemSocketTable() }; - yield return new TableMetadata { Name = TableNames.MASTERY_RECIPE, Table = ParseMasteryRecipe() }; - yield return new TableMetadata { Name = TableNames.MASTERY, Table = ParseMasteryReward() }; - yield return new TableMetadata { Name = TableNames.GUILD, Table = ParseGuildTable() }; - yield return new TableMetadata { Name = TableNames.VIP, Table = ParsePremiumClubTable() }; - yield return new TableMetadata { Name = TableNames.INDIVIDUAL_ITEM_DROP, Table = ParseIndividualItemDropTable() }; - yield return new TableMetadata { Name = TableNames.COLOR_PALETTE, Table = ParseColorPaletteTable() }; - yield return new TableMetadata { Name = TableNames.MERET_MARKET_CATEGORY, Table = ParseMeretMarketCategoryTable() }; - yield return new TableMetadata { Name = TableNames.SHOP_BEAUTY_COUPON, Table = ParseShopBeautyCouponTable() }; - yield return new TableMetadata { Name = TableNames.SHOP_FURNISHING, Table = ParseFurnishingShopTable() }; - yield return new TableMetadata { Name = TableNames.GACHA_INFO, Table = ParseGachaInfoTable() }; - yield return new TableMetadata { Name = TableNames.NAME_TAG_SYMBOL, Table = ParseInsigniaTable() }; - yield return new TableMetadata { Name = TableNames.EXP, Table = ParseExpTable() }; - yield return new TableMetadata { Name = TableNames.COMMON_EXP, Table = ParseCommonExpTable() }; - yield return new TableMetadata { Name = TableNames.UGC_DESIGN, Table = ParseUgcDesignTable() }; - yield return new TableMetadata { Name = TableNames.LEARNING_QUEST, Table = ParseLearningQuestTable() }; - yield return new TableMetadata { Name = TableNames.BLACK_MARKET_TABLE, Table = ParseBlackMarketTable() }; - yield return new TableMetadata { Name = TableNames.CHANGE_JOB, Table = ParseChangeJobTable() }; - yield return new TableMetadata { Name = TableNames.CHAPTER_BOOK, Table = ParseChapterBookTable() }; - yield return new TableMetadata { Name = TableNames.FIELD_MISSION, Table = ParseFieldMissionTable() }; - yield return new TableMetadata { Name = TableNames.WORLD_MAP, Table = ParseWorldMapTable() }; - yield return new TableMetadata { Name = TableNames.SURVIVAL_SKIN_INFO, Table = ParseSurvivalSkinTable() }; - yield return new TableMetadata { Name = TableNames.BANNER, Table = ParseBanner() }; - yield return new TableMetadata { Name = TableNames.MASTERY_UGC_HOUSING, Table = ParseMasteryUgcHousingTable() }; - yield return new TableMetadata { Name = TableNames.UGC_HOUSING_POINT_REWARD, Table = ParseUgcHousingPointRewardTable() }; - yield return new TableMetadata { Name = TableNames.REWARD_CONTENT, Table = ParseRewardContentTable() }; - yield return new TableMetadata { Name = TableNames.SEASON_DATA, Table = ParseSeasonDataTable() }; - yield return new TableMetadata { Name = TableNames.SMART_PUSH, Table = ParseSmartPushTable() }; - yield return new TableMetadata { Name = TableNames.AUTO_ACTION, Table = ParseAutoActionTable() }; - - // Marriage/Wedding - yield return new TableMetadata { Name = TableNames.WEDDING, Table = ParseWeddingTable() }; - - // Prestige - yield return new TableMetadata { Name = TableNames.PRESTIGE_LEVEL_ABILITY, Table = ParsePrestigeLevelAbilityTable() }; - yield return new TableMetadata { Name = TableNames.PRESTIGE_LEVEL_REWARD, Table = ParsePrestigeLevelRewardTable() }; - yield return new TableMetadata { Name = TableNames.PRESTIGE_MISSION, Table = ParsePrestigeMissionTable() }; - - yield return new TableMetadata { Name = TableNames.FISHING_ROD, Table = ParseFishingRod() }; - // Scroll - yield return new TableMetadata { Name = TableNames.ENCHANT_SCROLL, Table = ParseEnchantScrollTable() }; - yield return new TableMetadata { Name = TableNames.ITEM_REMAKE_SCROLL, Table = ParseItemRemakeScrollTable() }; - yield return new TableMetadata { Name = TableNames.ITEM_REPACKING_SCROLL, Table = ParseItemRepackingScrollTable() }; - yield return new TableMetadata { Name = TableNames.ITEM_SOCKET_SCROLL, Table = ParseItemSocketScrollTable() }; - yield return new TableMetadata { Name = TableNames.ITEM_EXCHANGE_SCROLL, Table = ParseItemExchangeScrollTable() }; - // ItemOption - yield return new TableMetadata { Name = TableNames.ITEM_OPTION_CONSTANT, Table = ParseItemOptionConstant() }; - yield return new TableMetadata { Name = TableNames.ITEM_OPTION_RANDOM, Table = ParseItemOptionRandom() }; - yield return new TableMetadata { Name = TableNames.ITEM_OPTION_STATIC, Table = ParseItemOptionStatic() }; - yield return new TableMetadata { Name = TableNames.ITEM_OPTION_PICK, Table = ParseItemOptionPick() }; - yield return new TableMetadata { Name = TableNames.ITEM_OPTION_VARIATION, Table = ParseItemVariation() }; - - foreach ((string type, ItemEquipVariationTable table) in ParseItemEquipVariation()) { - yield return new TableMetadata { Name = TableNames.ItemOptionVariationTableNames[type], Table = table }; - } - // SetItemOption - yield return new TableMetadata { Name = TableNames.SET_ITEM, Table = ParseSetItem() }; - - //Dungeon - yield return new TableMetadata { Name = TableNames.DUNGEON_ROOM, Table = ParseDungeonRoom() }; - yield return new TableMetadata { Name = TableNames.DUNGEON_RANK_REWARD, Table = ParseDungeonRankReward() }; - yield return new TableMetadata { Name = TableNames.DUNGEON_CONFIG, Table = ParseDungeonConfigTable() }; - yield return new TableMetadata { Name = TableNames.DUNGEON_MISSION, Table = ParseDungeonMissionTable() }; - } - - private ChatStickerTable ParseChatSticker() { - var results = new Dictionary(); - foreach ((int id, ChatSticker sticker) in parser.ParseChatSticker()) { - results[id] = new ChatStickerMetadata( - Id: id, - GroupId: sticker.group_id); - } - - return new ChatStickerTable(results); - } - - private DefaultItemsTable ParseDefaultItems() { - var common = new Dictionary(); - var job = new Dictionary>(); - - foreach (IGrouping Items)> groups in parser.ParseDefaultItems().GroupBy(entry => entry.JobCode)) { - var equips = new Dictionary(); - foreach ((_, string slot, IList items) in groups) { - var equipSlot = Enum.Parse(slot); - - List itemIds = items.Select(item => item.id).ToList(); - Dictionary dict = groups.Key == 0 ? common : equips; - if (dict.Remove(equipSlot, out int[]? existingIds)) { - itemIds.AddRange(existingIds); - } - dict.Add(equipSlot, itemIds.Distinct().Order().ToArray()); - } - - if (equips.Count > 0) { - job.Add((JobCode) groups.Key, equips); - } - } - - return new DefaultItemsTable(common, job); - } - - private ItemBreakTable ParseItemBreakIngredient() { - var results = new Dictionary>(); - foreach ((int itemId, ItemBreakIngredient item) in parser.ParseItemBreakIngredient()) { - var ingredients = new List(); - if (item.IngredientItemID1 > 0 && item.IngredientCount1 > 0) { - ingredients.Add(new ItemBreakTable.Ingredient(item.IngredientItemID1, item.IngredientCount1)); - } - if (item.IngredientItemID2 > 0 && item.IngredientCount2 > 0) { - ingredients.Add(new ItemBreakTable.Ingredient(item.IngredientItemID2, item.IngredientCount2)); - } - if (item.IngredientItemID3 > 0 && item.IngredientCount3 > 0) { - ingredients.Add(new ItemBreakTable.Ingredient(item.IngredientItemID3, item.IngredientCount3)); - } - - results.Add(itemId, ingredients); - } - - return new ItemBreakTable(results); - } - - private GemstoneUpgradeTable ParseItemGemstoneUpgrade() { - var results = new Dictionary(); - foreach ((int itemId, ItemGemstoneUpgrade upgrade) in parser.ParseItemGemstoneUpgrade()) { - var ingredients = new List(); - if (upgrade.IngredientCount1 > 0 && upgrade.IngredientItemID1?.Length > 1) { - ingredients.Add(new GemstoneUpgradeTable.Ingredient(Enum.Parse(upgrade.IngredientItemID1[1]), upgrade.IngredientCount1)); - } - if (upgrade.IngredientCount2 > 0 && upgrade.IngredientItemID2?.Length > 1) { - ingredients.Add(new GemstoneUpgradeTable.Ingredient(Enum.Parse(upgrade.IngredientItemID2[1]), upgrade.IngredientCount2)); - } - if (upgrade.IngredientCount3 > 0 && upgrade.IngredientItemID3?.Length > 1) { - ingredients.Add(new GemstoneUpgradeTable.Ingredient(Enum.Parse(upgrade.IngredientItemID3[1]), upgrade.IngredientCount3)); - } - if (upgrade.IngredientCount4 > 0 && upgrade.IngredientItemID4?.Length > 1) { - ingredients.Add(new GemstoneUpgradeTable.Ingredient(Enum.Parse(upgrade.IngredientItemID4[1]), upgrade.IngredientCount4)); - } - - results.Add(itemId, new GemstoneUpgradeTable.Entry(upgrade.GemLevel, upgrade.NextItemID, ingredients)); - } - - return new GemstoneUpgradeTable(results); - } - - private ItemExtractionTable ParseItemExtraction() { - var results = new Dictionary(); - foreach ((int targetItemId, ItemExtraction item) in parser.ParseItemExtraction()) { - results.Add(targetItemId, new ItemExtractionTable.Entry(item.TargetItemID, item.TryCount, item.ScrollCount, item.ResultItemID)); - } - - return new ItemExtractionTable(results); - } - - private JobTable ParseJobTable() { - var results = new Dictionary(); - foreach (Parser.Xml.Table.JobTable data in parser.ParseJobTable()) { - var skills = new Dictionary { - [SkillRank.Basic] = data.skills.skill - .Where(skill => skill.subJobCode <= data.code) // This is not actually correct, but works. - .Select(skill => new JobTable.Skill(skill.main, skill.sub, skill.maxLevel, skill.quickSlotPriority)) - .ToArray(), - [SkillRank.Awakening] = data.skills.skill - .Where(skill => skill.subJobCode > data.code) // This is not actually correct, but works. - .Select(skill => new JobTable.Skill(skill.main, skill.sub, skill.maxLevel, skill.quickSlotPriority)) - .ToArray(), - }; - - results[(JobCode) data.code] = new JobTable.Entry(Tutorial: new JobTable.Tutorial(StartField: data.startField, - SkipField: data.tutorialSkipField.Length > 0 ? data.tutorialSkipField[0] : 0, - SkipItem: data.tutorialSkipItem, - OpenMaps: data.tutorialClearOpenMaps, - OpenTaxis: data.tutorialClearOpenTaxis, - StartItem: data.startInvenItem.item.Select(item => new JobTable.Item(item.itemID, item.grade, item.count)).ToArray(), - Reward: data.reward.item.Select(item => new JobTable.Item(item.itemID, item.grade, 1)).ToArray()), - Skills: skills, - BaseSkills: data.learn.SelectMany(learn => learn.skill) - .SelectMany(skill => skill.sub.Append(skill.id)) - .OrderBy(id => id) - .ToArray()); - } - - return new JobTable(results); - } - - private MagicPathTable ParseMagicPath() { - var results = new Dictionary>(); - foreach ((long id, MagicType type) in parser.ParseMagicPath()) { - // Dropping duplicates for now (60073021, 50000303, 5009, 5101) - if (results.ContainsKey(id)) { - continue; - } - - List moves = type.move.Select(move => new MagicPath( - Align: move.align, - AlignHeight: move.alignCubeHeight, - Rotate: move.rotation, - IgnoreAdjust: move.ignoreAdjustCubePosition, - ExplosionByDestroy: move.explosionByDestroy, - CatmullRom: move.catmullrom != 0, - IgnorePhysXTestInitPosition: move.ignorePhysxTestInitPosition, - IgnoreCancelAtSpawnTime: move.ignoreCancelAtSpawnTime, - Direction: move.direction != default ? Vector3.Normalize(move.direction) : default, - FireOffset: move.fireOffsetPosition, - FireFixed: move.fireFixedPosition, - ControlValue0: move.controlValue0, - ControlValue1: move.controlValue1, - ControlEndOffsetValue: move.controlEndOffsetValue, - TraceTargetOffset: move.traceTargetOffsetPos, - TraceTargetDuration: move.traceTargetDuration, - Velocity: move.vel, - Distance: move.distance, - RotateZDegree: move.dirRotZDegree, - LifeTime: move.lifeTime, - DelayTime: move.delayTime, - SpawnTime: move.spawnTime, - DestroyTime: move.destroyTime, - ControlRate: move.controlRate, - LookAtType: move.lookAtType, - PiercingAttackInterval: move.piercingAttackInterval, - PiercingAttackMaxTargetCount: move.piercingAttackMaxTargetCount, - NonTargetMoveDistance: move.nonTargetMoveDistance, - MoveEndHoldDuration: move.moveEndHoldDuration - )).ToList(); - results[id] = moves; - } - - return new MagicPathTable(results); - } - - private InstrumentTable ParseInstrument() { - var categories = new Dictionary(); - foreach ((int _, InstrumentCategoryInfo info) in parser.ParseInstrumentCategoryInfo()) { - categories[info.id] = (info.GMId, info.percussionId); - } - - var results = new Dictionary(); - foreach ((int id, InstrumentInfo info) in parser.ParseInstrumentInfo()) { - if (!categories.ContainsKey(info.category)) { - Console.WriteLine($"Instrument {id} does not have a matching category: {info.category}"); - continue; - } - - (int midiId, int percussionId) = categories[info.category]; - results[id] = new InstrumentMetadata( - Id: info.id, - EquipId: info.equipItemId, - ScoreCount: info.soloRelayScoreCount, - Category: info.category, - MidiId: midiId, - PercussionId: percussionId); - } - - return new InstrumentTable(results); - } - - private InteractObjectTable ParseInteractObject() { - var results = new Dictionary(); - results = MergeInteractObjectTable(results, parser.ParseInteractObjectMastery()); - results = MergeInteractObjectTable(results, parser.ParseInteractObject().Select(entry => (entry.Id, entry.Info))); - return new InteractObjectTable(results); - } - private Dictionary MergeInteractObjectTable(Dictionary results, IEnumerable<(int Id, InteractObject Info)> parser) { - foreach ((int id, InteractObject info) in parser) { - var spawn = new InteractObjectMetadataSpawn[info.spawn.code.Length]; - for (int i = 0; i < spawn.Length; i++) { - spawn[i] = new InteractObjectMetadataSpawn( - Id: info.spawn.code[i], - Radius: info.spawn.radius[i], - Count: info.spawn.count[i], - Probability: info.spawn.prop[i], - LifeTime: info.spawn.lifeTime[i]); - } - - results[id] = new InteractObjectMetadata( - Id: info.id, - Type: (InteractType) info.type, - Collection: info.collection, - ReactCount: info.reactCount, - TargetPortalId: info.portal.targetPortalId, - GuildPosterId: info.guild.housePosterId, - WeaponItemId: info.weapon.weaponItemId, - Item: new InteractObjectMetadataItem(info.item.code, info.item.consume, info.item.rank, info.item.checkCount, info.gathering.receipeID), - Time: new InteractObjectMetadataTime(info.time.resetTime, info.time.reactTime, info.time.hideTime), - Drop: new InteractObjectMetadataDrop(info.drop.objectDropRank, info.drop.globalDropBoxId ?? [], info.drop.individualDropBoxId ?? [], info.drop.dropHeight, info.drop.dropDistance), - AdditionalEffect: new InteractObjectMetadataEffect( - Condition: ParseConditional(info.conditionAdditionalEffect), - Invoke: ParseInvoke(info.additionalEffect), - ModifyCode: info.additionalEffect.modify.code, - ModifyTime: info.additionalEffect.modify.modifyTime), - Spawn: spawn - ); - } - return results; - - InteractObjectMetadataEffect.ConditionEffect[] ParseConditional(InteractObject.ConditionAdditionalEffect additionalEffect) { - if (additionalEffect.id.Length == 0 || additionalEffect.id[0] == 0) { - return []; - } - - return additionalEffect.id.Zip(additionalEffect.level, (effectId, level) => - new InteractObjectMetadataEffect.ConditionEffect(effectId, level)).ToArray(); - } - - InteractObjectMetadataEffect.InvokeEffect[] ParseInvoke(InteractObject.AdditionalEffect additionalEffect) { - if (additionalEffect.invoke.code.Length == 0 || additionalEffect.invoke.code[0] == 0) { - return []; - } - - return additionalEffect.invoke.code - .Zip(additionalEffect.invoke.level, (effectId, level) => new { skillId = effectId, level }) - .Zip(additionalEffect.invoke.prop, (effect, prop) => - new InteractObjectMetadataEffect.InvokeEffect(effect.skillId, effect.level, prop)) - .ToArray(); - } - } - - private ItemOptionConstantTable ParseItemOptionConstant() { - var results = new Dictionary>(); - foreach (ItemOptionConstantData entry in optionParser.ParseConstant()) { - var statValues = new Dictionary(); - var statRates = new Dictionary(); - foreach (BasicAttribute attribute in Enum.GetValues()) { - int value = entry.StatValue((byte) attribute); - if (value != default) { - statValues[attribute] = value; - } - float rate = entry.StatRate((byte) attribute); - if (rate != default) { - statRates[attribute] = rate; - } - } - - var specialValues = new Dictionary(); - var specialRates = new Dictionary(); - foreach (SpecialAttribute attribute in Enum.GetValues()) { - byte index = attribute.OptionIndex(); - if (index == byte.MaxValue) continue; - - SpecialAttribute fixAttribute = attribute.SgiTarget(entry.sgi_target); - int value = entry.SpecialValue(index); - if (value != default) { - specialValues[fixAttribute] = value; - } - float rate = entry.SpecialRate(index); - if (rate != default) { - specialRates[fixAttribute] = rate; - } - } - - if (!results.ContainsKey(entry.code)) { - results[entry.code] = new Dictionary(); - } - - var option = new ItemOptionConstant( - Values: statValues, - Rates: statRates, - SpecialValues: specialValues, - SpecialRates: specialRates); - (results[entry.code] as Dictionary)!.Add(entry.grade, option); - } - - return new ItemOptionConstantTable(results); - } - - private ItemOptionRandomTable ParseItemOptionRandom() { - return new ItemOptionRandomTable(optionParser.ParseRandom().ToDictionary()); - } - - private ItemOptionStaticTable ParseItemOptionStatic() { - return new ItemOptionStaticTable(optionParser.ParseStatic().ToDictionary()); - } - - private ItemOptionPickTable ParseItemOptionPick() { - var results = new Dictionary>(); - foreach (ItemOptionPick entry in optionParser.ParsePick()) { - var constantValue = new Dictionary(); - for (int i = 0; i < entry.constant_value.Length; i += 2) { - if (string.IsNullOrWhiteSpace(entry.constant_value[i])) continue; - constantValue.Add(entry.constant_value[i].ToBasicAttribute(), int.Parse(entry.constant_value[i + 1])); - } - var constantRate = new Dictionary(); - for (int i = 0; i < entry.constant_rate.Length; i += 2) { - if (string.IsNullOrWhiteSpace(entry.constant_rate[i])) continue; - constantRate.Add(entry.constant_rate[i].ToBasicAttribute(), int.Parse(entry.constant_rate[i + 1])); - } - var staticValue = new Dictionary(); - for (int i = 0; i < entry.static_value.Length; i += 2) { - if (string.IsNullOrWhiteSpace(entry.static_value[i])) continue; - staticValue.Add(entry.static_value[i].ToBasicAttribute(), int.Parse(entry.static_value[i + 1])); - } - var staticRate = new Dictionary(); - for (int i = 0; i < entry.static_rate.Length; i += 2) { - if (string.IsNullOrWhiteSpace(entry.static_rate[i])) continue; - staticRate.Add(entry.static_rate[i].ToBasicAttribute(), int.Parse(entry.static_rate[i + 1])); - } - var randomValue = new Dictionary(); - for (int i = 0; i < entry.random_value.Length; i += 2) { - if (string.IsNullOrWhiteSpace(entry.random_value[i])) continue; - randomValue.Add(entry.random_value[i].ToBasicAttribute(), int.Parse(entry.random_value[i + 1])); - } - var randomRate = new Dictionary(); - for (int i = 0; i < entry.random_rate.Length; i += 2) { - if (string.IsNullOrWhiteSpace(entry.random_rate[i])) continue; - randomRate.Add(entry.random_rate[i].ToBasicAttribute(), int.Parse(entry.random_rate[i + 1])); - } - - if (!results.ContainsKey(entry.optionPickID)) { - results[entry.optionPickID] = new Dictionary(); - } - - var option = new ItemOptionPickTable.Option(constantValue, constantRate, staticValue, staticRate, randomValue, randomRate); - (results[entry.optionPickID] as Dictionary)!.Add(entry.itemGrade, option); - } - return new ItemOptionPickTable(results); - } - - private ItemVariationTable ParseItemVariation() { - var values = new Dictionary>>(); - var rates = new Dictionary>>(); - var specialValues = new Dictionary>>(); - var specialRates = new Dictionary>>(); - foreach (ItemOptionVariation.Option option in optionParser.ParseVariation()) { - string name = option.OptionName; - if (name.StartsWith("sid")) continue; // Don't know what stat this maps to. - - if (option.OptionValueVariation != 0) { - var variation = new ItemVariationTable.Range( - Min: option.OptionValueMin, - Max: option.OptionValueMax, - Variation: option.OptionValueVariation); - try { - if (values.ContainsKey(name.ToBasicAttribute())) { - values[name.ToBasicAttribute()].Add(variation); - } else { - values.Add(name.ToBasicAttribute(), [variation]); - } - } catch (ArgumentOutOfRangeException) { - if (specialValues.ContainsKey(name.ToSpecialAttribute())) { - specialValues[name.ToSpecialAttribute()].Add(variation); - } else { - specialValues.Add(name.ToSpecialAttribute(), [variation]); - } - } - } else if (option.OptionRateVariation != 0) { - if (name.EndsWith("_rate")) { - name = name[..^"_rate".Length]; // sanitize suffix - } - - var variation = new ItemVariationTable.Range( - Min: option.OptionRateMin, - Max: option.OptionRateMax, - Variation: option.OptionRateVariation); - try { - if (rates.ContainsKey(name.ToBasicAttribute())) { - rates[name.ToBasicAttribute()].Add(variation); - } else { - rates.Add(name.ToBasicAttribute(), [variation]); - } - } catch (ArgumentOutOfRangeException) { - if (specialRates.ContainsKey(name.ToSpecialAttribute())) { - specialRates[name.ToSpecialAttribute()].Add(variation); - } else { - specialRates.Add(name.ToSpecialAttribute(), [variation]); - } - } - } - } - - Dictionary[]> valuesArray = values.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToArray()); - Dictionary[]> ratesArray = rates.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToArray()); - Dictionary[]> specialValuesArray = specialValues.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToArray()); - Dictionary[]> specialRatesArray = specialRates.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToArray()); - - return new ItemVariationTable(valuesArray, ratesArray, specialValuesArray, specialRatesArray); - } - - private IEnumerable<(string Type, ItemEquipVariationTable Table)> ParseItemEquipVariation() { - foreach ((string type, List options) in optionParser.ParseVariationEquip()) { - var values = new Dictionary[]>(); - var rates = new Dictionary[]>(); - var specialValues = new Dictionary[]>(); - var specialRates = new Dictionary[]>(); - foreach (ItemOptionVariationEquip.Option option in options) { - string name = option.name.ToLower(); - if (name.EndsWith("value")) { - var entries = new ItemEquipVariationTable.Set[18]; - for (int i = 0; i < 18; i++) { - entries[i] = new ItemEquipVariationTable.Set( - Value: (int) option[i], - Weight: 1); // TODO: Weight - } - - name = name[..^"value".Length]; // Remove suffix - try { - values.Add(name.ToBasicAttribute(), entries); - } catch (ArgumentOutOfRangeException) { - specialValues.Add(name.ToSpecialAttribute(), entries); - } - - } else if (name.EndsWith("rate")) { - var entries = new ItemEquipVariationTable.Set[18]; - for (int i = 0; i < 18; i++) { - entries[i] = new ItemEquipVariationTable.Set( - Value: option[i], - Weight: 1); // TODO: Weight - } - - name = name[..^"rate".Length]; // Remove suffix - try { - rates.Add(name.ToBasicAttribute(), entries); - } catch (ArgumentOutOfRangeException) { - specialRates.Add(name.ToSpecialAttribute(), entries); - } - } else { - throw new ArgumentException($"Invalid option name: {option.name}"); - } - } - - yield return (type, new ItemEquipVariationTable(values, rates, specialValues, specialRates)); - } - } - - private SetItemTable ParseSetItem() { - var options = new Dictionary(); - foreach ((int id, SetItemOption option) in parser.ParseSetItemOption()) { - var parts = new List(); - foreach (SetItemOption.Part part in option.part) { - var values = new Dictionary(); - var rates = new Dictionary(); - var specialValues = new Dictionary(); - var specialRates = new Dictionary(); - - foreach (BasicAttribute attribute in Enum.GetValues()) { - values.AddIfNotDefault(attribute, part.StatValue((byte) attribute)); - rates.AddIfNotDefault(attribute, part.StatRate((byte) attribute)); - } - - // Since 4 is already "Boss" we can ignore sgi_boss_target - Debug.Assert(part.sgi_boss_target is 0 or 4); - foreach (SpecialAttribute attribute in Enum.GetValues()) { - byte attributeOption = attribute.OptionIndex(); - - if (attributeOption != byte.MaxValue) { - SpecialAttribute fixAttribute = attribute.SgiTarget(part.sgi_target); - specialValues.AddIfNotDefault(fixAttribute, part.SpecialValue(attributeOption)); - specialRates.AddIfNotDefault(fixAttribute, part.SpecialRate(attributeOption)); - } - } - - parts.Add(new SetBonusMetadata( - Count: part.count, - AdditionalEffects: part.additionalEffectID.Zip(part.additionalEffectLevel, - (skillId, level) => new SetBonusAdditionalEffect(skillId, level)).ToArray(), - Values: values, - Rates: rates, - SpecialValues: specialValues, - SpecialRates: specialRates)); - } - - options[id] = parts.ToArray(); - } - - var results = new Dictionary(); - foreach ((int id, string name, SetItemInfo info) in parser.ParseSetItemInfo()) { - Debug.Assert(options.ContainsKey(info.optionID)); - - results[id] = new SetItemTable.Entry( - Info: new SetItemInfoMetadata( - Id: id, - Name: name, - ItemIds: info.itemIDs, - OptionId: info.optionID), - Options: options[info.optionID]); - } - - return new SetItemTable(results); - } - - private LapenshardUpgradeTable ParseLapenshardUpgradeTable() { - var results = new Dictionary(); - foreach ((int itemId, ItemLapenshardUpgrade upgrade) in parser.ParseItemLapenshardUpgrade()) { - var ingredients = new List(); - if (upgrade.IngredientCount1 > 0 && upgrade.IngredientItemID1?.Length > 1) { - ingredients.Add(new LapenshardUpgradeTable.Ingredient(Enum.Parse(upgrade.IngredientItemID1[1]), upgrade.IngredientCount1)); - } - if (upgrade.IngredientCount2 > 0 && upgrade.IngredientItemID2?.Length > 1) { - ingredients.Add(new LapenshardUpgradeTable.Ingredient(Enum.Parse(upgrade.IngredientItemID2[1]), upgrade.IngredientCount2)); - } - if (upgrade.IngredientCount3 > 0 && upgrade.IngredientItemID3?.Length > 1) { - ingredients.Add(new LapenshardUpgradeTable.Ingredient(Enum.Parse(upgrade.IngredientItemID3[1]), upgrade.IngredientCount3)); - } - - results.Add(itemId, new LapenshardUpgradeTable.Entry( - Level: upgrade.LapenLevel, - GroupId: upgrade.LapenGroupID, - NextItemId: upgrade.NextItemID, - RequireCount: upgrade.GroupLapenshardMinCount, - Ingredients: ingredients, - Meso: upgrade.meso)); - } - - return new LapenshardUpgradeTable(results); - } - - private ItemSocketTable ParseItemSocketTable() { - var results = new Dictionary>(); - IEnumerable> groups = parser.ParseItemSocket() - .Select(entry => entry.Socket) - .GroupBy(entry => entry.id); - foreach (IGrouping group in groups) { - var idResults = new Dictionary(); - foreach (ItemSocket socket in group) { - idResults.Add(socket.grade, new ItemSocketMetadata( - MaxCount: socket.maxCount, - OpenCount: socket.fixOpenCount)); - } - results.Add(group.Key, idResults); - } - - return new ItemSocketTable(results); - } - - private MasteryRecipeTable ParseMasteryRecipe() { - var results = new Dictionary(); - foreach ((long id, MasteryRecipe recipe) in parser.ParseMasteryRecipe()) { - var requiredItems = new List(); - ItemComponent? requiredItem1 = ParseMasteryIngredient(recipe.requireItem1); - if (requiredItem1 != null) requiredItems.Add(requiredItem1); - ItemComponent? requiredItem2 = ParseMasteryIngredient(recipe.requireItem2); - if (requiredItem2 != null) requiredItems.Add(requiredItem2); - ItemComponent? requiredItem3 = ParseMasteryIngredient(recipe.requireItem3); - if (requiredItem3 != null) requiredItems.Add(requiredItem3); - ItemComponent? requiredItem4 = ParseMasteryIngredient(recipe.requireItem4); - if (requiredItem4 != null) requiredItems.Add(requiredItem4); - ItemComponent? requiredItem5 = ParseMasteryIngredient(recipe.requireItem5); - if (requiredItem5 != null) requiredItems.Add(requiredItem5); - - var rewardItems = new List(); - ItemComponent? rewardItem1 = ParseMasteryIngredient(recipe.rewardItem1); - if (rewardItem1 != null) rewardItems.Add(rewardItem1); - ItemComponent? rewardItem2 = ParseMasteryIngredient(recipe.rewardItem2); - if (rewardItem2 != null) rewardItems.Add(rewardItem2); - ItemComponent? rewardItem3 = ParseMasteryIngredient(recipe.rewardItem3); - if (rewardItem3 != null) rewardItems.Add(rewardItem3); - ItemComponent? rewardItem4 = ParseMasteryIngredient(recipe.rewardItem4); - if (rewardItem4 != null) rewardItems.Add(rewardItem4); - ItemComponent? rewardItem5 = ParseMasteryIngredient(recipe.rewardItem5); - if (rewardItem5 != null) rewardItems.Add(rewardItem5); - - var entry = new MasteryRecipeTable.Entry( - Id: (int) id, - Type: (MasteryType) recipe.masteryType, - NoRewardExp: recipe.exceptRewardExp, - RequiredMastery: recipe.requireMastery, - RequiredMeso: recipe.requireMeso, - RequiredQuests: recipe.requireQuest, - RewardExp: recipe.rewardExp, - RewardMastery: recipe.rewardMastery, - HighRateLimitCount: recipe.highPropLimitCount, - NormalRateLimitCount: recipe.normalPropLimitCount, - RequiredItems: requiredItems, - HabitatMapId: recipe.habitatMapId, - RewardItems: rewardItems); - - results.Add((int) id, entry); - } - - return new MasteryRecipeTable(results); - } - - private static ItemComponent? ParseMasteryIngredient(IReadOnlyList ingredientArray) { - if (ingredientArray.Count == 0 || ingredientArray[0] == "0") { - return null; - } - - string[] idAndTag = ingredientArray[0].Split(":"); - int id = int.Parse(idAndTag[0]); - string tag = idAndTag.Length > 1 ? idAndTag[1] : string.Empty; - if (!short.TryParse(ingredientArray[1], out short rarity)) { - rarity = 1; - } - if (!int.TryParse(ingredientArray[2], out int amount)) { - amount = 1; - } - - return new ItemComponent( - ItemId: id, - Rarity: rarity, - Amount: amount, - Tag: string.IsNullOrWhiteSpace(tag) ? ItemTag.None : Enum.Parse(tag)); - } - - private static ItemComponent? ParseMasteryIngredient(IReadOnlyList ingredientArray) { - if (ingredientArray.Count == 0 || ingredientArray[0] == 0) { - return null; - } - - return new ItemComponent( - ItemId: ingredientArray[0], - Rarity: (short) ingredientArray[1], - Amount: ingredientArray[2], - Tag: ItemTag.None); - } - - private MasteryRewardTable ParseMasteryReward() { - var results = new Dictionary>(); - foreach ((Parser.Enum.MasteryType type, MasteryReward reward) in parser.ParseMasteryReward()) { - var masteryLevelDictionary = new Dictionary(); - foreach (MasteryLevel level in reward.v) { - masteryLevelDictionary.Add(level.grade, new MasteryRewardTable.Entry( - Value: level.value, - ItemId: level.rewardJobItemID, - ItemRarity: level.rewardJobItemRank, - ItemAmount: level.rewardJobItemCount)); - } - results.Add((MasteryType) type, masteryLevelDictionary); - } - return new MasteryRewardTable(results); - } - - private GuildTable ParseGuildTable() { - // Dictionary expTable = parser.ParseGuildExp() - // .ToDictionary(entry => (short) entry.Id, entry => entry.Item.value); - - var guildBuffs = new Dictionary>(); - foreach ((int id, IEnumerable buffs) in parser.ParseGuildBuff()) { - var buffLevels = new Dictionary(); - foreach (GuildBuff buff in buffs) { - buffLevels[buff.level] = new GuildTable.Buff( - Id: buff.additionalEffectId, - Level: buff.additionalEffectLevel, - RequireLevel: buff.requireLevel, - Cost: buff.cost, - UpgradeCost: buff.upgradeCost, - Duration: buff.duration); - } - guildBuffs.Add(id, buffLevels); - } - - var guildHouses = new Dictionary>(); - foreach ((int rank, IEnumerable houses) in parser.ParseGuildHouse()) { - var themes = new Dictionary(); - foreach (GuildHouse house in houses) { - themes.Add(house.theme, new GuildTable.House( - MapId: house.fieldID, - RequireLevel: house.upgradeReqGuildLevel, - UpgradeCost: house.upgradeCost, - ReThemeCost: house.rethemeCost, - Facilities: house.facility)); - } - guildHouses.Add(rank, themes); - } - - var guildNpcs = new Dictionary>(); - foreach ((Parser.Enum.GuildNpcType type, IEnumerable npcs) in parser.ParseGuildNpc()) { - var levels = new Dictionary(); - foreach (GuildNpc npc in npcs) { - levels.Add(npc.level, new GuildTable.Npc( - Type: (GuildNpcType) type, - Level: npc.level, - RequireGuildLevel: npc.requireGuildLevel, - RequireHouseLevel: npc.requireHouseLevel, - UpgradeCost: npc.upgradeCost)); - } - guildNpcs.Add((GuildNpcType) type, levels); - } - - var guildProperties = new SortedDictionary(); - foreach ((int level, GuildProperty property) in parser.ParseGuildProperty()) { - var entry = new GuildTable.Property( - Level: property.level, - Experience: property.accumExp, - Capacity: property.capacity, - FundMax: property.fundMax, - DonateMax: property.donationMax, - CheckInExp: property.attendGuildExp, - WinMiniGameExp: property.winMiniGameGuildExp, - LoseMiniGameExp: property.loseMiniGameGuildExp, - RaidExp: property.raidGuildExp, - CheckInFund: property.attendGuildFund, - WinMiniGameFund: property.winMiniGameGuildFund, - LoseMiniGameFund: property.loseMiniGameGuildFund, - RaidFund: property.raidGuildFund, - CheckInPlayerExpRate: property.attendUserExpFactor, - DonatePlayerExpRate: property.donationUserExpFactor, - CheckInCoin: property.attendGuildCoin, - DonateCoin: property.donateGuildCoin, - WinMiniGameCoin: property.winMiniGameGuildCoin, - LoseMiniGameCoin: property.loseMiniGameGuildCoin); - guildProperties.Add((short) level, entry); - } - - return new GuildTable( - Buffs: guildBuffs, - Houses: guildHouses, - Npcs: guildNpcs, - Properties: guildProperties); - } - - private FishingRodTable ParseFishingRod() { - var results = new Dictionary(); - foreach ((int id, FishingRod rod) in parser.ParseFishingRod()) { - var entry = new FishingRodTable.Entry( - ItemId: rod.itemCode, - MinMastery: rod.fishMasteryLimit, - AddMastery: rod.addFishMastery, - ReduceTime: rod.reduceFishingTime); - results.Add(id, entry); - } - return new FishingRodTable(results); - } - - private EnchantScrollTable ParseEnchantScrollTable() { - var results = new Dictionary(); - foreach ((int id, EnchantScroll scroll) in parser.ParseEnchantScroll()) { - var metadata = new EnchantScrollMetadata( - Type: (EnchantScrollType) scroll.scrollType, - MinLevel: scroll.minLv, - MaxLevel: scroll.maxLv, - Enchants: scroll.grade, - ItemTypes: scroll.slot, - Rarities: scroll.rank); - Array.Sort(metadata.Enchants); // Just in case - results.Add(id, metadata); - } - - return new EnchantScrollTable(results); - } - - private ItemRemakeScrollTable ParseItemRemakeScrollTable() { - var results = new Dictionary(); - foreach ((int id, ItemRemakeScroll scroll) in parser.ParseItemRemakeScroll()) { - results.Add(id, new ItemRemakeScrollMetadata( - MinLevel: scroll.minLv, - MaxLevel: scroll.maxLv, - ItemTypes: scroll.slot, - Rarities: scroll.rank, - RollAttribute: scroll.addOpKind == 1, - RollValueType: (RollValueType) scroll.addOpValue, - OnlyPet: scroll.onlyPet)); - } - - return new ItemRemakeScrollTable(results); - } - - private ItemRepackingScrollTable ParseItemRepackingScrollTable() { - var results = new Dictionary(); - foreach ((int id, ItemRepackingScroll scroll) in parser.ParseItemRepackingScroll()) { - results.Add(id, new ItemRepackingScrollMetadata( - MinLevel: scroll.minLv, - MaxLevel: scroll.maxLv, - ItemTypes: scroll.slot, - Rarities: scroll.rank, - IsPet: scroll.petType)); - } - - return new ItemRepackingScrollTable(results); - } - - private ItemSocketScrollTable ParseItemSocketScrollTable() { - // SELECT GROUP_CONCAT(Name), JSON_EXTRACT(`Function`, '$.Parameters') as param - // FROM item - // WHERE JSON_EXTRACT(`Function`, '$.Name')='ItemSocketScroll' - // GROUP BY param; - var socketCount = new Dictionary { - {10000001, 1}, {10000002, 2}, {10000003, 3}, - {10000011, 1}, {10000012, 2}, - {10000013, 1}, {10000014, 2}, - {10000015, 1}, - {10000016, 1}, - {10000017, 1}, - {10000018, 1}, - {10000019, 1}, - {10000020, 1}, - {10000021, 1}, - {10000022, 1}, {10000023, 2}, - {10000024, 1}, {10000025, 2}, - {10000026, 1}, {10000027, 2}, - {10000028, 1}, {10000029, 2}, - {10000030, 1}, - {10000031, 1}, {10000032, 2}, - {10000033, 1}, {10000034, 2}, - {10000035, 1}, {10000036, 2}, - }; - - var results = new Dictionary(); - foreach ((int id, ItemSocketScroll scroll) in parser.ParseItemSocketScroll()) { - results.Add(id, new ItemSocketScrollMetadata( - MinLevel: scroll.minLv, - MaxLevel: scroll.maxLv, - ItemTypes: scroll.slot, - Rarities: scroll.rank, - SocketCount: socketCount[id], - TradableCountDeduction: scroll.tradableCountDeduction)); - } - - return new ItemSocketScrollTable(results); - } - - private ItemExchangeScrollTable ParseItemExchangeScrollTable() { - var results = new Dictionary(); - foreach ((int id, ItemExchangeScroll scroll) in parser.ParseItemExchangeScroll()) { - var requiredItems = new List(); - foreach (ItemExchangeScroll.Item item in scroll.require.item) { - string[] idAndTag = item.id[0].Split(":"); - int requiredItemId = int.Parse(idAndTag[0]); - string requiredItemTag = idAndTag.Length > 1 ? idAndTag[1] : string.Empty; - if (!short.TryParse(item.id[1], out short rarity)) { - rarity = 1; - } - if (!int.TryParse(item.id[2], out int amount)) { - amount = 1; - } - requiredItems.Add(new ItemComponent( - ItemId: requiredItemId, - Tag: string.IsNullOrWhiteSpace(requiredItemTag) ? ItemTag.None : Enum.Parse(requiredItemTag), - Rarity: rarity, - Amount: amount)); - } - - results.Add(id, new ItemExchangeScrollMetadata( - RecipeScroll: new ItemComponent( - ItemId: scroll.receipe.id, - Rarity: (short) scroll.receipe.rank, - Amount: scroll.receipe.count, - Tag: ItemTag.None), - RewardItem: new ItemComponent( - ItemId: scroll.exchange.id, - Rarity: (short) scroll.exchange.rank, - Amount: scroll.exchange.count, - Tag: ItemTag.None), - TradeCountDeduction: scroll.tradableCountDeduction, - RequiredMeso: scroll.require.meso, - RequiredItems: requiredItems)); - } - return new ItemExchangeScrollTable(results); - } - - private PremiumClubTable ParsePremiumClubTable() { - var premiumClubBuffs = new Dictionary(); - foreach ((int id, PremiumClubEffect buff) in parser.ParsePremiumClubEffect()) { - premiumClubBuffs.Add(id, new PremiumClubTable.Buff( - Id: buff.effectID, - Level: buff.effectLevel)); - } - - var premiumClubItems = new Dictionary(); - foreach ((int id, PremiumClubItem item) in parser.ParsePremiumClubItem()) { - premiumClubItems.Add(id, new PremiumClubTable.Item( - Id: item.itemID, - Amount: item.itemCount, - Rarity: item.itemRank, - Period: 0)); - } - - var premiumClubPackages = new Dictionary(); - foreach ((int id, PremiumClubPackage package) in parser.ParsePremiumClubPackage()) { - DateTime startTime = DateTime.TryParseExact(package.salesStartDate, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out startTime) ? startTime : DateTime.MinValue; - DateTime endTime = DateTime.TryParseExact(package.salesEndDate, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out endTime) ? endTime : DateTime.MinValue; - var items = new List(); - for (int item = 0; item < package.bonusItemID.Length; item++) { - items.Add(new PremiumClubTable.Item( - Id: package.bonusItemID[item], - Amount: package.bonusItemCount[item], - Rarity: package.bonusItemRank[item], - Period: package.bonusItemPeriod[item])); - } - premiumClubPackages.Add(id, new PremiumClubTable.Package( - Disabled: package.disable, - StartDate: startTime.ToEpochSeconds(), - EndDate: endTime.ToEpochSeconds(), - Period: package.vipPeriod, - Price: package.price < package.salePrice ? package.salePrice : package.price, - BonusItems: items)); - } - - return new PremiumClubTable(premiumClubBuffs, premiumClubItems, premiumClubPackages); - } - - private IndividualItemDropTable ParseIndividualItemDropTable() { - var results = new Dictionary>>(); - results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDrop()); - results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropCharge()); - results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropEvent()); - results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropGacha()); - results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropPet()); - results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemGearBox()); - results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropEventNpc()); - results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropNewGacha()); - results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropQuestMob()); - results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropQuestObj()); - - return new IndividualItemDropTable(results); - } - - private Dictionary>> MergeIndividualItemDropTable(Dictionary>> results, IEnumerable<(int Id, IDictionary>)> parser) { - foreach ((int id, IDictionary> dict) in parser) { - foreach ((byte dropGroup, List drops) in dict) { - foreach (IndividualItemDrop drop in drops) { - var itemIds = new List { - drop.item, - }; - if (drop.item2 > 0) { - itemIds.Add(drop.item2); - } - - float minCount = drop.minCount; - float maxCount = drop.maxCount; - if (drop.item == 90000008) { // Experience Orb - minCount *= 10000; - maxCount *= 10000; - } - - var entry = new IndividualItemDropTable.Entry( - ItemIds: itemIds.ToArray(), - SmartGender: drop.isApplySmartGenderDrop, - SmartDropRate: drop.smartDropRate, - Rarity: drop.PackageUIShowGrade, - EnchantLevel: drop.enchantLevel, - ReduceTradeCount: drop.tradableCountDeduction, - ReduceRepackLimit: drop.rePackingLimitCountDeduction, - Bind: drop.isBindCharacter, - MinCount: (int) minCount, - MaxCount: (int) maxCount); - - if (!results.ContainsKey(id)) { - results.Add(id, new Dictionary> { - {drop.dropGroup, new List { - entry, - }}, - }); - } else if (!results[id].ContainsKey(dropGroup)) { - results[id].Add(drop.dropGroup, new List() { - entry, - }); - } else { - results[id][dropGroup].Add(entry); - } - } - } - } - return results; - } - - private ColorPaletteTable ParseColorPaletteTable() { - var results = new Dictionary>(); - foreach ((int id, ColorPalette palette) in parser.ParseColorPalette()) { - foreach (ColorPalette.Color color in palette.color) { - var entry = new ColorPaletteTable.Entry( - Primary: ParseColor(color.ch0), - Secondary: ParseColor(color.ch1), - Tertiary: ParseColor(color.ch2), - AchieveId: color.achieveID, - AchieveGrade: color.achieveGrade); - if (!results.ContainsKey(id)) { - results.Add(id, new Dictionary { - {color.colorSN, entry}, - }); - } else { - (results[id] as Dictionary)!.Add(color.colorSN, entry); - } - } - } - return new ColorPaletteTable(results); - } - - private Color ParseColor(System.Drawing.Color color) { - return new Color(color.B, color.G, color.R, color.A); - } - - private MeretMarketCategoryTable ParseMeretMarketCategoryTable() { - var results = new Dictionary>(); - foreach ((int id, MeretMarketCategory category) in parser.ParseMeretMarketCategory()) { - foreach (MeretMarketCategory.Tab tab in category.tab) { - var subTabIds = new List(); - foreach (MeretMarketCategory.Tab subTab in tab.tab) { - var subTabEntry = new MeretMarketCategoryTable.Tab( - Categories: subTab.category, - SortGender: subTab.sortGender, - SortJob: subTab.sortJob, - SubTabIds: []); - subTabIds.Add(subTab.id); - if (!results.ContainsKey(id)) { - results.Add(id, new Dictionary { - {subTab.id, subTabEntry}, - }); - } else { - (results[id] as Dictionary)!.Add(subTab.id, subTabEntry); - } - } - var tabEntry = new MeretMarketCategoryTable.Tab( - Categories: tab.category, - SortGender: tab.sortGender, - SortJob: tab.sortJob, - SubTabIds: subTabIds.ToArray()); - - if (!results.ContainsKey(id)) { - results.Add(id, new Dictionary { - {tab.id, tabEntry}, - }); - } else { - (results[id] as Dictionary)!.Add(tab.id, tabEntry); - } - } - } - return new MeretMarketCategoryTable(results); - } - - private ShopBeautyCouponTable ParseShopBeautyCouponTable() { - var results = new Dictionary>(); - foreach ((int id, ShopBeautyCoupon coupon) in parser.ParseShopBeautyCoupon()) { - results.Add(id, new List(coupon.item.Select(item => item.id))); - } - - return new ShopBeautyCouponTable(results); - } - private GachaInfoTable ParseGachaInfoTable() { - var results = new Dictionary(); - foreach ((int randomBoxId, GachaInfo gachaInfo) in parser.ParseGachaInfo()) { - results.Add(randomBoxId, new GachaInfoTable.Entry( - RandomBoxGroup: gachaInfo.randomBoxGroup, - DropBoxId: gachaInfo.individualDropBoxID, - ShopId: gachaInfo.shopID, - CoinItemId: gachaInfo.coinItemID, - CoinItemAmount: gachaInfo.coinItemAmount)); - } - - return new GachaInfoTable(results); - } - - private FurnishingShopTable ParseFurnishingShopTable() { - var results = new Dictionary(); - foreach ((int id, ShopFurnishing shop) in parser.ParseFurnishingShopUgcAll().Concat(parser.ParseFurnishingShopMaid())) { - results.Add(id, new FurnishingShopTable.Entry( - ItemId: shop!.id, - Buyable: shop.ugcHousingBuy, - FurnishingTokenType: (FurnishingCurrencyType) shop.ugcHousingMoneyType, - Price: shop.ugcHousingDefaultPrice - )); - } - - return new FurnishingShopTable(results); - } - - private InsigniaTable ParseInsigniaTable() { - var results = new Dictionary(); - foreach ((int id, NameTagSymbol symbol) in parser.ParseNameTagSymbol()) { - results.Add(id, new InsigniaTable.Entry( - Type: (InsigniaConditionType) symbol.conditionType, - Code: symbol.code, - BuffId: symbol.buffID, - BuffLevel: symbol.buffLv)); - } - - return new InsigniaTable(results); - } - - private ExpTable ParseExpTable() { - var baseResults = new Dictionary>(); - foreach ((int tableId, ExpBaseTable table) in parser.ParseExpBaseTable()) { - foreach (ExpBaseTable.Base tableBase in table.@base) { - if (!baseResults.ContainsKey(tableId)) { - baseResults.Add(tableId, new Dictionary{ - {tableBase.level, tableBase.exp}, - }); - } else { - (baseResults[tableId] as Dictionary)!.Add(tableBase.level, tableBase.exp); - } - } - } - - var nextExpResults = new Dictionary(); - foreach ((int level, NextExp entry) in parser.ParseNextExp()) { - nextExpResults.Add(entry.level, entry.value); - } - return new ExpTable(baseResults, nextExpResults); - } - - private CommonExpTable ParseCommonExpTable() { - var results = new Dictionary(); - foreach ((CommonExpType type, CommonExp exp) in parser.ParseCommonExp()) { - results.Add(ToExpType(type), new CommonExpTable.Entry(ExpTableId: exp.expTableID, Factor: exp.factor)); - } - return new CommonExpTable(results); - } - - private static ExpType ToExpType(CommonExpType commonExpType) { - if (Enum.TryParse(commonExpType.ToString(), out ExpType expType)) { - return expType; - } - return ExpType.none; - } - - private UgcDesignTable ParseUgcDesignTable() { - var results = new Dictionary(); - foreach ((int id, UgcDesign design) in parser.ParseUgcDesign()) { - results.Add(id, new UgcDesignTable.Entry( - ItemRarity: design.itemGrade, - CurrencyType: (MeretMarketCurrencyType) design.priceType, - CreatePrice: design.salePrice < design.price ? design.salePrice : design.price, - MarketMinPrice: design.marketMinPrice, - MarketMaxPrice: design.marketMaxPrice)); - } - return new UgcDesignTable(results); - } - - private LearningQuestTable ParseLearningQuestTable() { - var results = new Dictionary(); - foreach ((int id, LearningQuest quest) in parser.ParseLearningQuest()) { - results.Add(id, new LearningQuestTable.Entry( - Category: quest.category, - RequiredLevel: quest.reqLevel, - QuestId: quest.reqQuest, - RequiredMapId: quest.reqField, - GoToMapId: quest.gotoField, - GoToPortalId: quest.gotoPortal)); - } - return new LearningQuestTable(results); - } - - private PrestigeLevelAbilityTable ParsePrestigeLevelAbilityTable() { - var results = new Dictionary(); - foreach ((int id, AdventureLevelAbility ability) in parser.ParseAdventureLevelAbility()) { - results.Add(id, new PrestigeLevelAbilityMetadata( - Id: id, - RequiredLevel: ability.requireLevel, - Interval: ability.interval, - MaxCount: ability.maxCount, - BuffId: ability.additionalEffectId, - StartValue: ability.startValue, - AddValue: ability.addValue)); - } - return new PrestigeLevelAbilityTable(results); - } - - private PrestigeLevelRewardTable ParsePrestigeLevelRewardTable() { - var results = new Dictionary(); - foreach ((int id, AdventureLevelReward reward) in parser.ParseAdventureLevelReward()) { - results.Add(id, new PrestigeLevelRewardMetadata( - Id: reward.id, - Level: reward.level, - Type: Enum.TryParse(reward.type, out PrestigeAwardType type) ? type : PrestigeAwardType.none, - Rarity: reward.rank, - Value: reward.value - )); - } - return new PrestigeLevelRewardTable(results); - } - - private PrestigeMissionTable ParsePrestigeMissionTable() { - var results = new Dictionary(); - foreach ((int id, AdventureLevelMission mission) in parser.ParseAdventureLevelMission()) { - results.Add(id, new PrestigeMissionMetadata( - Id: mission.missionId, - Count: mission.missionCount, - Item: new ItemComponent( - ItemId: mission.itemId, - Rarity: mission.itemRank, - mission.itemCount, - Tag: ItemTag.None))); - } - return new PrestigeMissionTable(results); - } - - private BlackMarketTable ParseBlackMarketTable() { - var results = new Dictionary(); - (int id, BlackMarketCategory blackMarket) category = parser.ParseBlackMarketCategory(); - foreach (BlackMarketCategory.BlackMarketTab item in category.blackMarket.tab) { - ParseBlackMarketTab(item, results); - } - - return new BlackMarketTable(results); - } - - private void ParseBlackMarketTab(BlackMarketCategory.BlackMarketTab tab, Dictionary results) { - results.Add(tab.id, tab.category); - if (tab.tab.Count > 0) { - foreach (BlackMarketCategory.BlackMarketTab subTab in tab.tab) { - ParseBlackMarketTab(subTab, results); - } - } - } - - private ChangeJobTable ParseChangeJobTable() { - var results = new Dictionary(); - foreach ((int jobId, ChangeJob job) in parser.ParseChangeJob()) { - results.Add((Job) jobId, new ChangeJobMetadata( - Job: (Job) job.subJobCode, - ChangeJob: (Job) job.changeSubJobCode, - StartQuestId: job.startquestid, - EndQuestId: job.endquestid - )); - } - return new ChangeJobTable(results); - } - - private ChapterBookTable ParseChapterBookTable() { - var results = new Dictionary(); - foreach ((int id, ChapterBook book) in parser.ParseChapterBook()) { - var items = new List(); - var skillpoints = new List(); - int statPoints = 0; - switch (book.rewardType1) { - case QuestRewardType.skillPoint: - skillpoints.Add(ParseSkillPoint(book.rewardValue1)); - break; - case QuestRewardType.item: - items.Add(ParseItem(book.rewardValue1)); - break; - case QuestRewardType.statPoint: - statPoints += int.Parse(book.rewardValue1[0]); - break; - default: - break; - } - - switch (book.rewardType2) { - case QuestRewardType.skillPoint: - skillpoints.Add(ParseSkillPoint(book.rewardValue2)); - break; - case QuestRewardType.item: - items.Add(ParseItem(book.rewardValue2)); - break; - case QuestRewardType.statPoint: - statPoints += int.Parse(book.rewardValue2[0]); - break; - default: - break; - } - results.Add(id, new ChapterBookTable.Entry( - Id: id, - BeginQuestId: book.prologue, - EndQuestId: book.epilogue, - SkillPoints: skillpoints.ToArray(), - StatPoints: statPoints, - Items: items.ToArray())); - } - - return new ChapterBookTable(results); - - ChapterBookTable.Entry.SkillPoint ParseSkillPoint(string[] rewardValue) { - return new ChapterBookTable.Entry.SkillPoint( - Amount: int.Parse(rewardValue[0]), - Rank: short.Parse(rewardValue[1])); - } - - ItemComponent ParseItem(string[] rewardValue) { - return new ItemComponent( - ItemId: int.Parse(rewardValue[0]), - Amount: short.Parse(rewardValue[1]), - Rarity: int.Parse(rewardValue[2]), - Tag: ItemTag.None); - } - } - - private FieldMissionTable ParseFieldMissionTable() { - var results = new Dictionary(); - foreach ((int id, FieldMission mission) in parser.ParseFieldMission()) { - switch (mission.type) { - case QuestRewardType.item: - results.Add(id, new FieldMissionTable.Entry( - MissionCount: mission.mission, - StatPoints: 0, - Item: new ItemComponent( - ItemId: mission.value[0], - Rarity: mission.value[1], - Amount: mission.value[2], - Tag: ItemTag.None))); - continue; - case QuestRewardType.statPoint: - results.Add(id, new FieldMissionTable.Entry( - MissionCount: mission.mission, - StatPoints: mission.value[0], - Item: null)); - continue; - } - } - return new FieldMissionTable(results); - } - - private WorldMapTable ParseWorldMapTable() { - var mapList = new List(); - foreach ((string feature, var maps) in parser.ParseWorldMap()) { - if (feature != "Kritias_2018_12") { - continue; - } - foreach (var map in maps) { - if (!map.@public) { - continue; - } - - mapList.Add(new WorldMapTable.Map(map.code, map.x, map.y, map.z, map.size)); - } - } - - if (mapList.Count == 0) { - throw new InvalidOperationException("No maps ingested for WorldMapTable"); - } - return new WorldMapTable(mapList); - } - - private SurvivalSkinInfoTable ParseSurvivalSkinTable() { - var results = new Dictionary(); - foreach ((int id, MapleSurvivalSkinInfo skin) in parser.ParseMapleSurvivalSkinInfo()) { - MedalType type = skin.type switch { - SurvivalSkinType.gliding => MedalType.Gliding, - SurvivalSkinType.riding => MedalType.Riding, - SurvivalSkinType.effectTail => MedalType.Tail, - _ => throw new InvalidOperationException("Unknown SurvivalSkinType"), - }; - results.Add(id, type); - } - - return new SurvivalSkinInfoTable(results); - } - - private BannerTable ParseBanner() { - List results = []; - foreach ((int id, Banner banner) in parser.ParseBanner()) { - results.Add(new BannerTable.Entry( - Id: id, - MapId: banner.field, - Price: banner.price.ToList() - )); - } - return new BannerTable(results); - } - - private MasteryUgcHousingTable ParseMasteryUgcHousingTable() { - var results = new Dictionary(); - foreach ((int id, MasteryUgcHousing ugcHousing) in parser.ParseMasteryUgcHousing()) { - results.Add(id, new MasteryUgcHousingTable.Entry( - Level: ugcHousing.grade, - Exp: ugcHousing.value, - RewardJobItemId: ugcHousing.rewardJobItemID)); - } - return new MasteryUgcHousingTable(results); - } - - private UgcHousingPointRewardTable ParseUgcHousingPointRewardTable() { - var results = new Dictionary(); - foreach ((int id, UgcHousingPointReward ugcHousing) in parser.ParseUgcHousingPointReward()) { - results.Add(id, new UgcHousingPointRewardTable.Entry( - DecorationScore: ugcHousing.housingPoint, - IndividualDropBoxId: ugcHousing.individualDropBoxId)); - } - return new UgcHousingPointRewardTable(results); - } - - private WeddingTable ParseWeddingTable() { - var rewardsResults = new Dictionary(); - foreach ((WeddingRewardType type, Parser.Xml.Table.WeddingReward reward) in parser.ParseWeddingReward()) { - rewardsResults.Add((MarriageExpType) type, new WeddingReward( - Type: (MarriageExpType) type, - Amount: reward.rewardExp, - Limit: (MarriageExpLimit) reward.rewardLimit)); - } - - var packageResults = new Dictionary(); - foreach ((int id, Parser.Xml.Table.WeddingPackage package) in parser.ParseWeddingPackage()) { - var hallDataDic = new Dictionary(); - foreach (WeddingHall hall in package.weddingHall) { - List hallItems = []; - foreach (WeddingItem item in hall.weddingItem) { - hallItems.Add(new WeddingPackage.HallData.Item( - ItemId: item.itemID, - Amount: item.count, - Rarity: item.grade, - NightOnly: item.nightReward)); - } - - List completeHallItems = []; - foreach (WeddingItem item in hall.weddingCompleteItem) { - completeHallItems.Add(new WeddingPackage.HallData.Item( - ItemId: item.itemID, - Amount: item.count, - Rarity: item.grade, - NightOnly: item.nightReward)); - } - - hallDataDic.Add(hall.id, new WeddingPackage.HallData( - Id: hall.id, - MapId: hall.fieldID, - NightMapId: hall.nightFieldID, - Tier: hall.grade, - MeretCost: hall.merat, - Items: hallItems, - CompleteItems: completeHallItems)); - } - packageResults.Add(id, new WeddingPackage( - Id: id, - PlannerId: package.planner, - Halls: hallDataDic)); - } - return new WeddingTable(rewardsResults, packageResults); - } - - private DungeonRoomTable ParseDungeonRoom() { - var dungeons = new Dictionary(); - foreach ((int id, DungeonRoom dungeon) in parser.ParseDungeonRoom()) { - dungeons.Add(id, new DungeonRoomMetadata( - Id: dungeon.dungeonRoomID, - Level: dungeon.dungeonLevel, - PlayType: (DungeonPlayType) dungeon.playType, - GroupType: (DungeonGroupType) dungeon.groupType, - CooldownType: (DungeonCooldownType) dungeon.cooldownType, - CooldownValue: dungeon.cooldownType == Parser.Enum.DungeonCooldownType.dayOfWeeks ? dungeon.cooldownValue + 1 : dungeon.cooldownValue, // dayOfWeeks is 0-indexed - DurationTick: dungeon.durationTick, - LobbyFieldId: dungeon.lobbyFieldID, - FieldIds: dungeon.fieldIDs, - Reward: new DungeonRoomRewardMetadata( - AccountWide: dungeon.isAccountReward, - Count: dungeon.rewardCount, - SubRewardCount: dungeon.subRewardCount, - Exp: dungeon.rewardExp, - ExpRate: dungeon.rewardExpRate, - Meso: dungeon.rewardMeso, - LimitedDropBoxIds: dungeon.rewardLimitedDropBoxIds, - UnlimitedDropBoxIds: dungeon.rewardUnlimitedDropBoxIds, - UnionRewardId: dungeon.unionRewardID, - SeasonRankRewardId: dungeon.seasonRankRewardID, - ScoreBonusId: dungeon.scoreBonusId), - Limit: new DungeonRoomLimitMetadata( - MinUserCount: dungeon.minUserCount, - MaxUserCount: dungeon.maxUserCount, - GearScore: dungeon.gearScore, - MinLevel: dungeon.limitPlayerLevel, - RequiredAchievementId: dungeon.limitAchieveID, - VipOnly: dungeon.limitVIP, - DayOfWeeks: dungeon.limitDayOfWeeks.Length == 0 ? [] : dungeon.limitDayOfWeeks.Select(ParseDayOfWeek).ToArray(), - ClearDungeonIds: dungeon.limitClearDungeon, - Buffs: dungeon.limitAdditionalEffects, - DisableMeretRevival: dungeon.limitMeratRevival, - EquippedRecommendedWeapon: dungeon.limitRecommendWeapon, - PartyOnly: dungeon.isPartyOnly, - ChangeMaxUsers: dungeon.isChangeMaxUser, - DisableMesoRevival: dungeon.limitMesoRevival, - MaxRevivalCount: dungeon.defaultRevivalLimitCount), - PlayerCountFactorId: dungeon.playerCountFactorID, - CustomMonsterLevel: dungeon.customMonsterLevel, - HelperRequireClearCount: dungeon.dungeonHelperRequireClearCount, - DisabledFindHelper: dungeon.isDisableFindHelper, - RankTableId: dungeon.rankTableID, - RoundId: dungeon.roundID, - LeaveAfterCloseReward: dungeon.isLeaveAfterCloseReward, - PartyMissions: dungeon.partyMissions, - UserMissions: dungeon.userMissions, - MoveToBackupField: dungeon.isMoveOutToBackupField - )); - } - - return new DungeonRoomTable(dungeons); - - DayOfWeek ParseDayOfWeek(Maple2.File.Parser.Enum.DayOfWeek dayofWeek) { - return dayofWeek switch { - Maple2.File.Parser.Enum.DayOfWeek.sun => DayOfWeek.Sunday, - Maple2.File.Parser.Enum.DayOfWeek.mon => DayOfWeek.Monday, - Maple2.File.Parser.Enum.DayOfWeek.tue => DayOfWeek.Tuesday, - Maple2.File.Parser.Enum.DayOfWeek.wed => DayOfWeek.Wednesday, - Maple2.File.Parser.Enum.DayOfWeek.thu => DayOfWeek.Thursday, - Maple2.File.Parser.Enum.DayOfWeek.fri => DayOfWeek.Friday, - Maple2.File.Parser.Enum.DayOfWeek.sat => DayOfWeek.Saturday, - _ => DayOfWeek.Sunday, - }; - } - } - - private DungeonRankRewardTable ParseDungeonRankReward() { - var results = new Dictionary(); - foreach ((int id, DungeonRankReward reward) in parser.ParseDungeonRankReward()) { - List rewards = []; - foreach (DungeonRankRewardEntry item in reward.v) { - rewards.Add(new DungeonRankRewardTable.Entry.Item( - Rank: item.rank, - ItemId: item.itemID, - SystemMailId: item.systemMailID)); - } - - results.Add(id, new DungeonRankRewardTable.Entry( - Id: id, - Items: rewards.ToArray())); - } - - return new DungeonRankRewardTable(results); - } - - private DungeonConfigTable ParseDungeonConfigTable() { - var missionRankResults = new Dictionary(); - foreach (DungeonConfig config in parser.ParseDungeonConfig()) { - MissionRank missionRank = config.MissionRank.First(); - foreach (MissionRankGroup group in missionRank.group) { - var scores = new List(); - for (int i = 0; i < group.rank.Count; i++) { - scores.Add(new DungeonMissionRankMetadata.Score( - Grade: (DungeonMissionRank) (i + 1), // Ranking start at C - Value: group.rank[i].score)); - } - missionRankResults.Add(group.id, new DungeonMissionRankMetadata( - Id: group.id, - Description: group.desc, - MaxScore: group.maxScore, - Scores: scores.ToArray())); - } - break; // Break because there should only be one entry. - } - - var unitedWeeklyResults = new Dictionary(); - UnitedWeeklyReward reward = parser.ParseUnitedWeeklyReward().First(); - foreach (UnitedWeeklyRewardEntry item in reward.v) { - unitedWeeklyResults.Add(item.rewardCount, item.rewardID); - } - - return new DungeonConfigTable(unitedWeeklyResults, missionRankResults); - } - - private DungeonMissionTable ParseDungeonMissionTable() { - var results = new Dictionary(); - foreach ((int id, DungeonMission mission) in parser.ParseDungeonMission()) { - if (!Enum.TryParse(mission.type, out DungeonMissionType type)) { - Console.WriteLine($"Unknown Mission type: {mission.type}"); - continue; - } - results.Add(id, new DungeonMissionMetadata( - Id: id, - Type: type, - Value1: Array.ConvertAll(mission.value1, element => (long) element), - Value2: mission.value2, - MaxScore: (short) mission.maxScore, - ApplyCount: (short) mission.applyCount, - IsPenaltyType: mission.isPenaltyType)); - } - return new DungeonMissionTable(results); - } - - private RewardContentTable ParseRewardContentTable() { - var baseResults = new Dictionary(); - foreach ((int id, RewardContent rewardContent) in parser.ParseRewardContent()) { - baseResults.Add(id, new RewardContentTable.Base( - Id: id, - ExpTableId: rewardContent.expTableID, - MesoTableId: rewardContent.mesoTableID, - ExpFactor: rewardContent.expFactor, - MesoFactor: rewardContent.mesoFactor, - ItemTableId: rewardContent.itemTableID, - PrestigeExpTableId: rewardContent.adventureExpTableID)); - } - - var itemResults = new Dictionary(); - foreach ((int id, RewardContentItem content) in parser.ParseRewardContentItem()) { - List itemData = []; - foreach (RewardContentValue value in content.v) { - List items = []; - foreach (RewardContentItemEntry item in value.item) { - items.Add(new RewardItem(item.itemID, (short) item.grade, item.count)); - } - itemData.Add(new RewardContentTable.Item.Data( - MinLevel: value.minLevel, - MaxLevel: value.maxLevel, - RewardItems: items.ToArray())); - } - itemResults.Add(id, new RewardContentTable.Item( - Id: id, - ItemData: itemData.ToArray())); - } - - var mesoStaticResults = new Dictionary(); - foreach ((int id, RewardContentMesoStatic reward) in parser.ParseRewardContentMesoStatic()) { - mesoStaticResults.Add(id, reward.v.FirstOrDefault()?.meso ?? 0); - } - - var mesoResults = new Dictionary>(); - foreach ((int id, RewardContentMeso reward) in parser.ParseRewardContentMeso()) { - var entries = new Dictionary(); - foreach (RewardContentMesoValue value in reward.v) { - entries.Add(value.level, value.meso); - } - - mesoResults.Add(id, entries); - } - - var expStaticResults = new Dictionary(); - foreach ((int id, RewardContentExpStatic reward) in parser.ParseRewardContentExpStatic()) { - expStaticResults.Add(id, reward.@base.FirstOrDefault()?.exp ?? 0); - } - - return new RewardContentTable(baseResults, itemResults, mesoStaticResults, mesoResults, expStaticResults); - } - - private SeasonDataTable ParseSeasonDataTable() { - return new SeasonDataTable( - Arcade: ParseSeasonData(parser.ParseSeasonDataArcade()), - Boss: ParseSeasonData(parser.ParseSeasonDataBossColosseum()), - DarkDescent: ParseSeasonData(parser.ParseSeasonDataDarkStream()), - GuildPvp: ParseSeasonData(parser.ParseSeasonDataGuildPvp()), - Survival: ParseSeasonData(parser.ParseSeasonDataMapleSurvival()), - SurvivalSquad: ParseSeasonData(parser.ParseSeasonDataMapleSurvivalSquad()), - Pvp: ParseSeasonData(parser.ParseSeasonDataPvp()), - UgcMapCommendation: ParseSeasonData(parser.ParseSeasonDataUgcMapCommendation()), - WorldChampionship: ParseSeasonData(parser.ParseSeasonDataWorldChampion())); - - IReadOnlyDictionary ParseSeasonData(IEnumerable<(int, SeasonData)> seasonDataParser) { - var results = new Dictionary(); - - foreach ((int id, SeasonData seasonData) in seasonDataParser) { - results.Add(id, new SeasonDataTable.Entry( - Id: seasonData.seasonID, - StartTime: DateTime.TryParseExact(seasonData.eventStart, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime startTime) ? startTime : DateTime.MinValue, - EndTime: DateTime.TryParseExact(seasonData.eventEnd, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime endTime) ? endTime : DateTime.MaxValue, - Grades: [ - seasonData.grade1, - seasonData.grade2, - seasonData.grade3, - seasonData.grade4, - seasonData.grade5, - seasonData.grade6, - seasonData.grade7, - ])); - } - return results; - } - } - - private SmartPushTable ParseSmartPushTable() { - var results = new Dictionary(); - foreach ((int id, SmartPush smartPush) in parser.ParseSmartPush()) { - var requiredItem = new IngredientInfo(ItemTag.None, 0); - var requiredItemTag = ItemTag.None; - if (smartPush.requireItem.Length != 0) { - string[] requiredItemArray = smartPush.requireItem[0].Split(":"); - if (requiredItemArray.Length == 2) { - requiredItemTag = Enum.TryParse(requiredItemArray[1], out ItemTag tag) ? tag : ItemTag.None; - } - requiredItem = new IngredientInfo( - tag: requiredItemTag, - amount: int.Parse(smartPush.requireItem[2])); - } - - results.Add(id, new SmartPushMetadata( - Id: id, - Content: smartPush.content, - Type: Enum.TryParse(smartPush.actionType, out SmartPushType type) ? type : SmartPushType.none, - Value: smartPush.actionValue, - MeretCost: smartPush.requireMerat, - RequiredItem: requiredItem)); - } - return new SmartPushTable(results); - } - - private AutoActionTable ParseAutoActionTable() { - var results = new Dictionary>(); - IEnumerable> groups = parser.ParseAutoActionPricePackage() - .Select(entry => entry.Data) - .GroupBy(entry => entry.content); - foreach (IGrouping group in groups) { - var packages = new Dictionary(); - foreach (AutoActionPricePackage package in group) { - packages.Add(package.id, new AutoActionMetaData( - Content: package.content, - Id: package.id, - Duration: package.duration, - MeretCost: package.merat, - MesoCost: package.meso)); - } - results.Add(group.Key, packages); - } - return new AutoActionTable(results); - } -} +using System.Diagnostics; +using System.Globalization; +using System.Numerics; +using Maple2.Database.Extensions; +using Maple2.File.Ingest.Utils; +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Enum; +using Maple2.File.Parser.Xml; +using Maple2.File.Parser.Xml.Table; +using Maple2.Model.Common; +using Maple2.Model.Enum; +using Maple2.Model.Game; +using Maple2.Model.Metadata; +using Maple2.Tools.Extensions; +using DayOfWeek = System.DayOfWeek; +using ChatSticker = Maple2.File.Parser.Xml.Table.ChatSticker; +using DungeonCooldownType = Maple2.Model.Enum.DungeonCooldownType; +using DungeonGroupType = Maple2.Model.Enum.DungeonGroupType; +using DungeonPlayType = Maple2.Model.Enum.DungeonPlayType; +using ExpType = Maple2.Model.Enum.ExpType; +using GuildBuff = Maple2.File.Parser.Xml.Table.GuildBuff; +using GuildNpc = Maple2.File.Parser.Xml.Table.GuildNpc; +using GuildNpcType = Maple2.Model.Enum.GuildNpcType; +using InteractObject = Maple2.File.Parser.Xml.Table.InteractObject; +using ItemOptionConstant = Maple2.Model.Metadata.ItemOptionConstant; +using ItemSocket = Maple2.File.Parser.Xml.Table.ItemSocket; +using JobTable = Maple2.Model.Metadata.JobTable; +using MagicPath = Maple2.Model.Metadata.MagicPath; +using MasteryType = Maple2.Model.Enum.MasteryType; +using MeretMarketCategory = Maple2.File.Parser.Xml.Table.MeretMarketCategory; +using WeddingHall = Maple2.File.Parser.Xml.Table.WeddingHall; +using WeddingPackage = Maple2.Model.Metadata.WeddingPackage; +using WeddingReward = Maple2.Model.Metadata.WeddingReward; + +namespace Maple2.File.Ingest.Mapper; + +public class TableMapper : TypeMapper { + private readonly TableParser parser; + private readonly ItemOptionParser optionParser; + private readonly string language; + + public TableMapper(M2dReader xmlReader, string language) { + parser = new TableParser(xmlReader, language); + optionParser = new ItemOptionParser(xmlReader); + } + + protected override IEnumerable Map() { + yield return new TableMetadata { Name = TableNames.CHAT_EMOTICON, Table = ParseChatSticker() }; + yield return new TableMetadata { Name = TableNames.DEFAULT_ITEMS, Table = ParseDefaultItems() }; + yield return new TableMetadata { Name = TableNames.ITEM_BREAK_INGREDIENT, Table = ParseItemBreakIngredient() }; + yield return new TableMetadata { Name = TableNames.ITEM_GEMSTONE_UPGRADE, Table = ParseItemGemstoneUpgrade() }; + yield return new TableMetadata { Name = TableNames.ITEM_EXTRACTION, Table = ParseItemExtraction() }; + yield return new TableMetadata { Name = TableNames.JOB, Table = ParseJobTable() }; + yield return new TableMetadata { Name = TableNames.MAGIC_PATH, Table = ParseMagicPath() }; + yield return new TableMetadata { Name = TableNames.INSTRUMENT_CATEGORY_INFO, Table = ParseInstrument() }; + yield return new TableMetadata { Name = TableNames.INTERACT_OBJECT, Table = ParseInteractObject() }; + yield return new TableMetadata { Name = TableNames.ITEM_LAPENSHARD_UPGRADE, Table = ParseLapenshardUpgradeTable() }; + yield return new TableMetadata { Name = TableNames.ITEM_SOCKET, Table = ParseItemSocketTable() }; + yield return new TableMetadata { Name = TableNames.MASTERY_RECIPE, Table = ParseMasteryRecipe() }; + yield return new TableMetadata { Name = TableNames.MASTERY, Table = ParseMasteryReward() }; + yield return new TableMetadata { Name = TableNames.GUILD, Table = ParseGuildTable() }; + yield return new TableMetadata { Name = TableNames.VIP, Table = ParsePremiumClubTable() }; + yield return new TableMetadata { Name = TableNames.INDIVIDUAL_ITEM_DROP, Table = ParseIndividualItemDropTable() }; + yield return new TableMetadata { Name = TableNames.COLOR_PALETTE, Table = ParseColorPaletteTable() }; + yield return new TableMetadata { Name = TableNames.MERET_MARKET_CATEGORY, Table = ParseMeretMarketCategoryTable() }; + yield return new TableMetadata { Name = TableNames.SHOP_BEAUTY_COUPON, Table = ParseShopBeautyCouponTable() }; + yield return new TableMetadata { Name = TableNames.SHOP_FURNISHING, Table = ParseFurnishingShopTable() }; + yield return new TableMetadata { Name = TableNames.GACHA_INFO, Table = ParseGachaInfoTable() }; + yield return new TableMetadata { Name = TableNames.NAME_TAG_SYMBOL, Table = ParseInsigniaTable() }; + yield return new TableMetadata { Name = TableNames.EXP, Table = ParseExpTable() }; + yield return new TableMetadata { Name = TableNames.COMMON_EXP, Table = ParseCommonExpTable() }; + yield return new TableMetadata { Name = TableNames.UGC_DESIGN, Table = ParseUgcDesignTable() }; + yield return new TableMetadata { Name = TableNames.LEARNING_QUEST, Table = ParseLearningQuestTable() }; + yield return new TableMetadata { Name = TableNames.BLACK_MARKET_TABLE, Table = ParseBlackMarketTable() }; + yield return new TableMetadata { Name = TableNames.CHANGE_JOB, Table = ParseChangeJobTable() }; + yield return new TableMetadata { Name = TableNames.CHAPTER_BOOK, Table = ParseChapterBookTable() }; + yield return new TableMetadata { Name = TableNames.FIELD_MISSION, Table = ParseFieldMissionTable() }; + yield return new TableMetadata { Name = TableNames.WORLD_MAP, Table = ParseWorldMapTable() }; + yield return new TableMetadata { Name = TableNames.SURVIVAL_SKIN_INFO, Table = ParseSurvivalSkinTable() }; + yield return new TableMetadata { Name = TableNames.BANNER, Table = ParseBanner() }; + yield return new TableMetadata { Name = TableNames.MASTERY_UGC_HOUSING, Table = ParseMasteryUgcHousingTable() }; + yield return new TableMetadata { Name = TableNames.UGC_HOUSING_POINT_REWARD, Table = ParseUgcHousingPointRewardTable() }; + yield return new TableMetadata { Name = TableNames.REWARD_CONTENT, Table = ParseRewardContentTable() }; + yield return new TableMetadata { Name = TableNames.SEASON_DATA, Table = ParseSeasonDataTable() }; + yield return new TableMetadata { Name = TableNames.SMART_PUSH, Table = ParseSmartPushTable() }; + yield return new TableMetadata { Name = TableNames.AUTO_ACTION, Table = ParseAutoActionTable() }; + + // Marriage/Wedding + yield return new TableMetadata { Name = TableNames.WEDDING, Table = ParseWeddingTable() }; + + // Prestige + yield return new TableMetadata { Name = TableNames.PRESTIGE_LEVEL_ABILITY, Table = ParsePrestigeLevelAbilityTable() }; + yield return new TableMetadata { Name = TableNames.PRESTIGE_LEVEL_REWARD, Table = ParsePrestigeLevelRewardTable() }; + yield return new TableMetadata { Name = TableNames.PRESTIGE_MISSION, Table = ParsePrestigeMissionTable() }; + + yield return new TableMetadata { Name = TableNames.FISHING_ROD, Table = ParseFishingRod() }; + // Scroll + yield return new TableMetadata { Name = TableNames.ENCHANT_SCROLL, Table = ParseEnchantScrollTable() }; + yield return new TableMetadata { Name = TableNames.ITEM_REMAKE_SCROLL, Table = ParseItemRemakeScrollTable() }; + yield return new TableMetadata { Name = TableNames.ITEM_REPACKING_SCROLL, Table = ParseItemRepackingScrollTable() }; + yield return new TableMetadata { Name = TableNames.ITEM_SOCKET_SCROLL, Table = ParseItemSocketScrollTable() }; + yield return new TableMetadata { Name = TableNames.ITEM_EXCHANGE_SCROLL, Table = ParseItemExchangeScrollTable() }; + // ItemOption + yield return new TableMetadata { Name = TableNames.ITEM_OPTION_CONSTANT, Table = ParseItemOptionConstant() }; + yield return new TableMetadata { Name = TableNames.ITEM_OPTION_RANDOM, Table = ParseItemOptionRandom() }; + yield return new TableMetadata { Name = TableNames.ITEM_OPTION_STATIC, Table = ParseItemOptionStatic() }; + yield return new TableMetadata { Name = TableNames.ITEM_OPTION_PICK, Table = ParseItemOptionPick() }; + yield return new TableMetadata { Name = TableNames.ITEM_OPTION_VARIATION, Table = ParseItemVariation() }; + + foreach ((string type, ItemEquipVariationTable table) in ParseItemEquipVariation()) { + yield return new TableMetadata { Name = TableNames.ItemOptionVariationTableNames[type], Table = table }; + } + // SetItemOption + yield return new TableMetadata { Name = TableNames.SET_ITEM, Table = ParseSetItem() }; + + //Dungeon + yield return new TableMetadata { Name = TableNames.DUNGEON_ROOM, Table = ParseDungeonRoom() }; + yield return new TableMetadata { Name = TableNames.DUNGEON_RANK_REWARD, Table = ParseDungeonRankReward() }; + yield return new TableMetadata { Name = TableNames.DUNGEON_CONFIG, Table = ParseDungeonConfigTable() }; + yield return new TableMetadata { Name = TableNames.DUNGEON_MISSION, Table = ParseDungeonMissionTable() }; + } + + private ChatStickerTable ParseChatSticker() { + var results = new Dictionary(); + foreach ((int id, ChatSticker sticker) in parser.ParseChatSticker()) { + results[id] = new ChatStickerMetadata( + Id: id, + GroupId: sticker.group_id); + } + + return new ChatStickerTable(results); + } + + private DefaultItemsTable ParseDefaultItems() { + var common = new Dictionary(); + var job = new Dictionary>(); + + foreach (IGrouping Items)> groups in parser.ParseDefaultItems().GroupBy(entry => entry.JobCode)) { + var equips = new Dictionary(); + foreach ((_, string slot, IList items) in groups) { + var equipSlot = Enum.Parse(slot); + + List itemIds = items.Select(item => item.id).ToList(); + Dictionary dict = groups.Key == 0 ? common : equips; + if (dict.Remove(equipSlot, out int[]? existingIds)) { + itemIds.AddRange(existingIds); + } + dict.Add(equipSlot, itemIds.Distinct().Order().ToArray()); + } + + if (equips.Count > 0) { + job.Add((JobCode) groups.Key, equips); + } + } + + return new DefaultItemsTable(common, job); + } + + private ItemBreakTable ParseItemBreakIngredient() { + var results = new Dictionary>(); + foreach ((int itemId, ItemBreakIngredient item) in parser.ParseItemBreakIngredient()) { + var ingredients = new List(); + if (item.IngredientItemID1 > 0 && item.IngredientCount1 > 0) { + ingredients.Add(new ItemBreakTable.Ingredient(item.IngredientItemID1, item.IngredientCount1)); + } + if (item.IngredientItemID2 > 0 && item.IngredientCount2 > 0) { + ingredients.Add(new ItemBreakTable.Ingredient(item.IngredientItemID2, item.IngredientCount2)); + } + if (item.IngredientItemID3 > 0 && item.IngredientCount3 > 0) { + ingredients.Add(new ItemBreakTable.Ingredient(item.IngredientItemID3, item.IngredientCount3)); + } + + results.Add(itemId, ingredients); + } + + return new ItemBreakTable(results); + } + + private GemstoneUpgradeTable ParseItemGemstoneUpgrade() { + var results = new Dictionary(); + foreach ((int itemId, ItemGemstoneUpgrade upgrade) in parser.ParseItemGemstoneUpgrade()) { + var ingredients = new List(); + if (upgrade.IngredientCount1 > 0 && upgrade.IngredientItemID1?.Length > 1) { + ingredients.Add(new GemstoneUpgradeTable.Ingredient(Enum.Parse(upgrade.IngredientItemID1[1]), upgrade.IngredientCount1)); + } + if (upgrade.IngredientCount2 > 0 && upgrade.IngredientItemID2?.Length > 1) { + ingredients.Add(new GemstoneUpgradeTable.Ingredient(Enum.Parse(upgrade.IngredientItemID2[1]), upgrade.IngredientCount2)); + } + if (upgrade.IngredientCount3 > 0 && upgrade.IngredientItemID3?.Length > 1) { + ingredients.Add(new GemstoneUpgradeTable.Ingredient(Enum.Parse(upgrade.IngredientItemID3[1]), upgrade.IngredientCount3)); + } + if (upgrade.IngredientCount4 > 0 && upgrade.IngredientItemID4?.Length > 1) { + ingredients.Add(new GemstoneUpgradeTable.Ingredient(Enum.Parse(upgrade.IngredientItemID4[1]), upgrade.IngredientCount4)); + } + + results.Add(itemId, new GemstoneUpgradeTable.Entry(upgrade.GemLevel, upgrade.NextItemID, ingredients)); + } + + return new GemstoneUpgradeTable(results); + } + + private ItemExtractionTable ParseItemExtraction() { + var results = new Dictionary(); + foreach ((int targetItemId, ItemExtraction item) in parser.ParseItemExtraction()) { + results.Add(targetItemId, new ItemExtractionTable.Entry(item.TargetItemID, item.TryCount, item.ScrollCount, item.ResultItemID)); + } + + return new ItemExtractionTable(results); + } + + private JobTable ParseJobTable() { + var results = new Dictionary(); + foreach (Parser.Xml.Table.JobTable data in parser.ParseJobTable()) { + var skills = new Dictionary { + [SkillRank.Basic] = data.skills.skill + .Where(skill => skill.subJobCode <= data.code) // This is not actually correct, but works. + .Select(skill => new JobTable.Skill(skill.main, skill.sub, skill.maxLevel, skill.quickSlotPriority)) + .ToArray(), + [SkillRank.Awakening] = data.skills.skill + .Where(skill => skill.subJobCode > data.code) // This is not actually correct, but works. + .Select(skill => new JobTable.Skill(skill.main, skill.sub, skill.maxLevel, skill.quickSlotPriority)) + .ToArray(), + }; + + results[(JobCode) data.code] = new JobTable.Entry(Tutorial: new JobTable.Tutorial(StartField: data.startField, + SkipField: data.tutorialSkipField.Length > 0 ? data.tutorialSkipField[0] : 0, + SkipItem: data.tutorialSkipItem, + OpenMaps: data.tutorialClearOpenMaps, + OpenTaxis: data.tutorialClearOpenTaxis, + StartItem: data.startInvenItem.item.Select(item => new JobTable.Item(item.itemID, item.grade, item.count)).ToArray(), + Reward: data.reward.item.Select(item => new JobTable.Item(item.itemID, item.grade, 1)).ToArray()), + Skills: skills, + BaseSkills: data.learn.SelectMany(learn => learn.skill) + .SelectMany(skill => skill.sub.Append(skill.id)) + .OrderBy(id => id) + .ToArray()); + } + + return new JobTable(results); + } + + private MagicPathTable ParseMagicPath() { + var results = new Dictionary>(); + foreach ((long id, MagicType type) in parser.ParseMagicPath()) { + // Dropping duplicates for now (60073021, 50000303, 5009, 5101) + if (results.ContainsKey(id)) { + continue; + } + + List moves = type.move.Select(move => new MagicPath( + Align: move.align, + AlignHeight: move.alignCubeHeight, + Rotate: move.rotation, + IgnoreAdjust: move.ignoreAdjustCubePosition, + ExplosionByDestroy: move.explosionByDestroy, + CatmullRom: move.catmullrom != 0, + IgnorePhysXTestInitPosition: move.ignorePhysxTestInitPosition, + IgnoreCancelAtSpawnTime: move.ignoreCancelAtSpawnTime, + Direction: move.direction != default ? Vector3.Normalize(move.direction) : default, + FireOffset: move.fireOffsetPosition, + FireFixed: move.fireFixedPosition, + ControlValue0: move.controlValue0, + ControlValue1: move.controlValue1, + ControlEndOffsetValue: move.controlEndOffsetValue, + TraceTargetOffset: move.traceTargetOffsetPos, + TraceTargetDuration: move.traceTargetDuration, + Velocity: move.vel, + Distance: move.distance, + RotateZDegree: move.dirRotZDegree, + LifeTime: move.lifeTime, + DelayTime: move.delayTime, + SpawnTime: move.spawnTime, + DestroyTime: move.destroyTime, + ControlRate: move.controlRate, + LookAtType: move.lookAtType, + PiercingAttackInterval: move.piercingAttackInterval, + PiercingAttackMaxTargetCount: move.piercingAttackMaxTargetCount, + NonTargetMoveDistance: move.nonTargetMoveDistance, + MoveEndHoldDuration: move.moveEndHoldDuration + )).ToList(); + results[id] = moves; + } + + return new MagicPathTable(results); + } + + private InstrumentTable ParseInstrument() { + var categories = new Dictionary(); + foreach ((int _, InstrumentCategoryInfo info) in parser.ParseInstrumentCategoryInfo()) { + categories[info.id] = (info.GMId, info.percussionId); + } + + var results = new Dictionary(); + foreach ((int id, InstrumentInfo info) in parser.ParseInstrumentInfo()) { + if (!categories.ContainsKey(info.category)) { + Console.WriteLine($"Instrument {id} does not have a matching category: {info.category}"); + continue; + } + + (int midiId, int percussionId) = categories[info.category]; + results[id] = new InstrumentMetadata( + Id: info.id, + EquipId: info.equipItemId, + ScoreCount: info.soloRelayScoreCount, + Category: info.category, + MidiId: midiId, + PercussionId: percussionId); + } + + return new InstrumentTable(results); + } + + private InteractObjectTable ParseInteractObject() { + var results = new Dictionary(); + results = MergeInteractObjectTable(results, parser.ParseInteractObjectMastery()); + results = MergeInteractObjectTable(results, parser.ParseInteractObject().Select(entry => (entry.Id, entry.Info))); + return new InteractObjectTable(results); + } + private Dictionary MergeInteractObjectTable(Dictionary results, IEnumerable<(int Id, InteractObject Info)> parser) { + foreach ((int id, InteractObject info) in parser) { + var spawn = new InteractObjectMetadataSpawn[info.spawn.code.Length]; + for (int i = 0; i < spawn.Length; i++) { + spawn[i] = new InteractObjectMetadataSpawn( + Id: info.spawn.code[i], + Radius: info.spawn.radius[i], + Count: info.spawn.count[i], + Probability: info.spawn.prop[i], + LifeTime: info.spawn.lifeTime[i]); + } + + results[id] = new InteractObjectMetadata( + Id: info.id, + Type: (InteractType) info.type, + Collection: info.collection, + ReactCount: info.reactCount, + TargetPortalId: info.portal.targetPortalId, + GuildPosterId: info.guild.housePosterId, + WeaponItemId: info.weapon.weaponItemId, + Item: new InteractObjectMetadataItem(info.item.code, info.item.consume, info.item.rank, info.item.checkCount, info.gathering.receipeID), + Time: new InteractObjectMetadataTime(info.time.resetTime, info.time.reactTime, info.time.hideTime), + Drop: new InteractObjectMetadataDrop(info.drop.objectDropRank, info.drop.globalDropBoxId ?? [], info.drop.individualDropBoxId ?? [], info.drop.dropHeight, info.drop.dropDistance), + AdditionalEffect: new InteractObjectMetadataEffect( + Condition: ParseConditional(info.conditionAdditionalEffect), + Invoke: ParseInvoke(info.additionalEffect), + ModifyCode: info.additionalEffect.modify.code, + ModifyTime: info.additionalEffect.modify.modifyTime), + Spawn: spawn + ); + } + return results; + + InteractObjectMetadataEffect.ConditionEffect[] ParseConditional(InteractObject.ConditionAdditionalEffect additionalEffect) { + if (additionalEffect.id.Length == 0 || additionalEffect.id[0] == 0) { + return []; + } + + return additionalEffect.id.Zip(additionalEffect.level, (effectId, level) => + new InteractObjectMetadataEffect.ConditionEffect(effectId, level)).ToArray(); + } + + InteractObjectMetadataEffect.InvokeEffect[] ParseInvoke(InteractObject.AdditionalEffect additionalEffect) { + if (additionalEffect.invoke.code.Length == 0 || additionalEffect.invoke.code[0] == 0) { + return []; + } + + return additionalEffect.invoke.code + .Zip(additionalEffect.invoke.level, (effectId, level) => new { skillId = effectId, level }) + .Zip(additionalEffect.invoke.prop, (effect, prop) => + new InteractObjectMetadataEffect.InvokeEffect(effect.skillId, effect.level, prop)) + .ToArray(); + } + } + + private ItemOptionConstantTable ParseItemOptionConstant() { + var results = new Dictionary>(); + foreach (ItemOptionConstantData entry in optionParser.ParseConstant()) { + var statValues = new Dictionary(); + var statRates = new Dictionary(); + foreach (BasicAttribute attribute in Enum.GetValues()) { + int value = entry.StatValue((byte) attribute); + if (value != default) { + statValues[attribute] = value; + } + float rate = entry.StatRate((byte) attribute); + if (rate != default) { + statRates[attribute] = rate; + } + } + + var specialValues = new Dictionary(); + var specialRates = new Dictionary(); + foreach (SpecialAttribute attribute in Enum.GetValues()) { + byte index = attribute.OptionIndex(); + if (index == byte.MaxValue) continue; + + SpecialAttribute fixAttribute = attribute.SgiTarget(entry.sgi_target); + int value = entry.SpecialValue(index); + if (value != default) { + specialValues[fixAttribute] = value; + } + float rate = entry.SpecialRate(index); + if (rate != default) { + specialRates[fixAttribute] = rate; + } + } + + if (!results.ContainsKey(entry.code)) { + results[entry.code] = new Dictionary(); + } + + var option = new ItemOptionConstant( + Values: statValues, + Rates: statRates, + SpecialValues: specialValues, + SpecialRates: specialRates); + (results[entry.code] as Dictionary)!.Add(entry.grade, option); + } + + return new ItemOptionConstantTable(results); + } + + private ItemOptionRandomTable ParseItemOptionRandom() { + return new ItemOptionRandomTable(optionParser.ParseRandom().ToDictionary()); + } + + private ItemOptionStaticTable ParseItemOptionStatic() { + return new ItemOptionStaticTable(optionParser.ParseStatic().ToDictionary()); + } + + private ItemOptionPickTable ParseItemOptionPick() { + var results = new Dictionary>(); + foreach (ItemOptionPick entry in optionParser.ParsePick()) { + var constantValue = new Dictionary(); + for (int i = 0; i < entry.constant_value.Length; i += 2) { + if (string.IsNullOrWhiteSpace(entry.constant_value[i])) continue; + constantValue.Add(entry.constant_value[i].ToBasicAttribute(), int.Parse(entry.constant_value[i + 1])); + } + var constantRate = new Dictionary(); + for (int i = 0; i < entry.constant_rate.Length; i += 2) { + if (string.IsNullOrWhiteSpace(entry.constant_rate[i])) continue; + constantRate.Add(entry.constant_rate[i].ToBasicAttribute(), int.Parse(entry.constant_rate[i + 1])); + } + var staticValue = new Dictionary(); + for (int i = 0; i < entry.static_value.Length; i += 2) { + if (string.IsNullOrWhiteSpace(entry.static_value[i])) continue; + staticValue.Add(entry.static_value[i].ToBasicAttribute(), int.Parse(entry.static_value[i + 1])); + } + var staticRate = new Dictionary(); + for (int i = 0; i < entry.static_rate.Length; i += 2) { + if (string.IsNullOrWhiteSpace(entry.static_rate[i])) continue; + staticRate.Add(entry.static_rate[i].ToBasicAttribute(), int.Parse(entry.static_rate[i + 1])); + } + var randomValue = new Dictionary(); + for (int i = 0; i < entry.random_value.Length; i += 2) { + if (string.IsNullOrWhiteSpace(entry.random_value[i])) continue; + randomValue.Add(entry.random_value[i].ToBasicAttribute(), int.Parse(entry.random_value[i + 1])); + } + var randomRate = new Dictionary(); + for (int i = 0; i < entry.random_rate.Length; i += 2) { + if (string.IsNullOrWhiteSpace(entry.random_rate[i])) continue; + randomRate.Add(entry.random_rate[i].ToBasicAttribute(), int.Parse(entry.random_rate[i + 1])); + } + + if (!results.ContainsKey(entry.optionPickID)) { + results[entry.optionPickID] = new Dictionary(); + } + + var option = new ItemOptionPickTable.Option(constantValue, constantRate, staticValue, staticRate, randomValue, randomRate); + (results[entry.optionPickID] as Dictionary)!.Add(entry.itemGrade, option); + } + return new ItemOptionPickTable(results); + } + + private ItemVariationTable ParseItemVariation() { + var values = new Dictionary>>(); + var rates = new Dictionary>>(); + var specialValues = new Dictionary>>(); + var specialRates = new Dictionary>>(); + foreach (ItemOptionVariation.Option option in optionParser.ParseVariation()) { + string name = option.OptionName; + if (name.StartsWith("sid")) continue; // Don't know what stat this maps to. + + if (option.OptionValueVariation != 0) { + var variation = new ItemVariationTable.Range( + Min: option.OptionValueMin, + Max: option.OptionValueMax, + Variation: option.OptionValueVariation); + try { + if (values.ContainsKey(name.ToBasicAttribute())) { + values[name.ToBasicAttribute()].Add(variation); + } else { + values.Add(name.ToBasicAttribute(), [variation]); + } + } catch (ArgumentOutOfRangeException) { + if (specialValues.ContainsKey(name.ToSpecialAttribute())) { + specialValues[name.ToSpecialAttribute()].Add(variation); + } else { + specialValues.Add(name.ToSpecialAttribute(), [variation]); + } + } + } else if (option.OptionRateVariation != 0) { + if (name.EndsWith("_rate")) { + name = name[..^"_rate".Length]; // sanitize suffix + } + + var variation = new ItemVariationTable.Range( + Min: option.OptionRateMin, + Max: option.OptionRateMax, + Variation: option.OptionRateVariation); + try { + if (rates.ContainsKey(name.ToBasicAttribute())) { + rates[name.ToBasicAttribute()].Add(variation); + } else { + rates.Add(name.ToBasicAttribute(), [variation]); + } + } catch (ArgumentOutOfRangeException) { + if (specialRates.ContainsKey(name.ToSpecialAttribute())) { + specialRates[name.ToSpecialAttribute()].Add(variation); + } else { + specialRates.Add(name.ToSpecialAttribute(), [variation]); + } + } + } + } + + Dictionary[]> valuesArray = values.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToArray()); + Dictionary[]> ratesArray = rates.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToArray()); + Dictionary[]> specialValuesArray = specialValues.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToArray()); + Dictionary[]> specialRatesArray = specialRates.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToArray()); + + return new ItemVariationTable(valuesArray, ratesArray, specialValuesArray, specialRatesArray); + } + + private IEnumerable<(string Type, ItemEquipVariationTable Table)> ParseItemEquipVariation() { + foreach ((string type, List options) in optionParser.ParseVariationEquip()) { + var values = new Dictionary[]>(); + var rates = new Dictionary[]>(); + var specialValues = new Dictionary[]>(); + var specialRates = new Dictionary[]>(); + foreach (ItemOptionVariationEquip.Option option in options) { + string name = option.name.ToLower(); + if (name.EndsWith("value")) { + var entries = new ItemEquipVariationTable.Set[18]; + for (int i = 0; i < 18; i++) { + entries[i] = new ItemEquipVariationTable.Set( + Value: (int) option[i], + Weight: 1); // TODO: Weight + } + + name = name[..^"value".Length]; // Remove suffix + try { + values.Add(name.ToBasicAttribute(), entries); + } catch (ArgumentOutOfRangeException) { + specialValues.Add(name.ToSpecialAttribute(), entries); + } + + } else if (name.EndsWith("rate")) { + var entries = new ItemEquipVariationTable.Set[18]; + for (int i = 0; i < 18; i++) { + entries[i] = new ItemEquipVariationTable.Set( + Value: option[i], + Weight: 1); // TODO: Weight + } + + name = name[..^"rate".Length]; // Remove suffix + try { + rates.Add(name.ToBasicAttribute(), entries); + } catch (ArgumentOutOfRangeException) { + specialRates.Add(name.ToSpecialAttribute(), entries); + } + } else { + throw new ArgumentException($"Invalid option name: {option.name}"); + } + } + + yield return (type, new ItemEquipVariationTable(values, rates, specialValues, specialRates)); + } + } + + private SetItemTable ParseSetItem() { + var options = new Dictionary(); + foreach ((int id, SetItemOption option) in parser.ParseSetItemOption()) { + var parts = new List(); + foreach (SetItemOption.Part part in option.part) { + var values = new Dictionary(); + var rates = new Dictionary(); + var specialValues = new Dictionary(); + var specialRates = new Dictionary(); + + foreach (BasicAttribute attribute in Enum.GetValues()) { + values.AddIfNotDefault(attribute, part.StatValue((byte) attribute)); + rates.AddIfNotDefault(attribute, part.StatRate((byte) attribute)); + } + + // Since 4 is already "Boss" we can ignore sgi_boss_target + Debug.Assert(part.sgi_boss_target is 0 or 4); + foreach (SpecialAttribute attribute in Enum.GetValues()) { + byte attributeOption = attribute.OptionIndex(); + + if (attributeOption != byte.MaxValue) { + SpecialAttribute fixAttribute = attribute.SgiTarget(part.sgi_target); + specialValues.AddIfNotDefault(fixAttribute, part.SpecialValue(attributeOption)); + specialRates.AddIfNotDefault(fixAttribute, part.SpecialRate(attributeOption)); + } + } + + parts.Add(new SetBonusMetadata( + Count: part.count, + AdditionalEffects: part.additionalEffectID.Zip(part.additionalEffectLevel, + (skillId, level) => new SetBonusAdditionalEffect(skillId, level)).ToArray(), + Values: values, + Rates: rates, + SpecialValues: specialValues, + SpecialRates: specialRates)); + } + + options[id] = parts.ToArray(); + } + + var results = new Dictionary(); + foreach ((int id, string name, SetItemInfo info) in parser.ParseSetItemInfo()) { + Debug.Assert(options.ContainsKey(info.optionID)); + + results[id] = new SetItemTable.Entry( + Info: new SetItemInfoMetadata( + Id: id, + Name: name, + ItemIds: info.itemIDs, + OptionId: info.optionID), + Options: options[info.optionID]); + } + + return new SetItemTable(results); + } + + private LapenshardUpgradeTable ParseLapenshardUpgradeTable() { + var results = new Dictionary(); + foreach ((int itemId, ItemLapenshardUpgrade upgrade) in parser.ParseItemLapenshardUpgrade()) { + var ingredients = new List(); + if (upgrade.IngredientCount1 > 0 && upgrade.IngredientItemID1?.Length > 1) { + ingredients.Add(new LapenshardUpgradeTable.Ingredient(Enum.Parse(upgrade.IngredientItemID1[1]), upgrade.IngredientCount1)); + } + if (upgrade.IngredientCount2 > 0 && upgrade.IngredientItemID2?.Length > 1) { + ingredients.Add(new LapenshardUpgradeTable.Ingredient(Enum.Parse(upgrade.IngredientItemID2[1]), upgrade.IngredientCount2)); + } + if (upgrade.IngredientCount3 > 0 && upgrade.IngredientItemID3?.Length > 1) { + ingredients.Add(new LapenshardUpgradeTable.Ingredient(Enum.Parse(upgrade.IngredientItemID3[1]), upgrade.IngredientCount3)); + } + + results.Add(itemId, new LapenshardUpgradeTable.Entry( + Level: upgrade.LapenLevel, + GroupId: upgrade.LapenGroupID, + NextItemId: upgrade.NextItemID, + RequireCount: upgrade.GroupLapenshardMinCount, + Ingredients: ingredients, + Meso: upgrade.meso)); + } + + return new LapenshardUpgradeTable(results); + } + + private ItemSocketTable ParseItemSocketTable() { + var results = new Dictionary>(); + IEnumerable> groups = parser.ParseItemSocket() + .Select(entry => entry.Socket) + .GroupBy(entry => entry.id); + foreach (IGrouping group in groups) { + var idResults = new Dictionary(); + foreach (ItemSocket socket in group) { + idResults.Add(socket.grade, new ItemSocketMetadata( + MaxCount: socket.maxCount, + OpenCount: socket.fixOpenCount)); + } + results.Add(group.Key, idResults); + } + + return new ItemSocketTable(results); + } + + private MasteryRecipeTable ParseMasteryRecipe() { + var results = new Dictionary(); + foreach ((long id, MasteryRecipe recipe) in parser.ParseMasteryRecipe()) { + var requiredItems = new List(); + ItemComponent? requiredItem1 = ParseMasteryIngredient(recipe.requireItem1); + if (requiredItem1 != null) requiredItems.Add(requiredItem1); + ItemComponent? requiredItem2 = ParseMasteryIngredient(recipe.requireItem2); + if (requiredItem2 != null) requiredItems.Add(requiredItem2); + ItemComponent? requiredItem3 = ParseMasteryIngredient(recipe.requireItem3); + if (requiredItem3 != null) requiredItems.Add(requiredItem3); + ItemComponent? requiredItem4 = ParseMasteryIngredient(recipe.requireItem4); + if (requiredItem4 != null) requiredItems.Add(requiredItem4); + ItemComponent? requiredItem5 = ParseMasteryIngredient(recipe.requireItem5); + if (requiredItem5 != null) requiredItems.Add(requiredItem5); + + var rewardItems = new List(); + ItemComponent? rewardItem1 = ParseMasteryIngredient(recipe.rewardItem1); + if (rewardItem1 != null) rewardItems.Add(rewardItem1); + ItemComponent? rewardItem2 = ParseMasteryIngredient(recipe.rewardItem2); + if (rewardItem2 != null) rewardItems.Add(rewardItem2); + ItemComponent? rewardItem3 = ParseMasteryIngredient(recipe.rewardItem3); + if (rewardItem3 != null) rewardItems.Add(rewardItem3); + ItemComponent? rewardItem4 = ParseMasteryIngredient(recipe.rewardItem4); + if (rewardItem4 != null) rewardItems.Add(rewardItem4); + ItemComponent? rewardItem5 = ParseMasteryIngredient(recipe.rewardItem5); + if (rewardItem5 != null) rewardItems.Add(rewardItem5); + + var entry = new MasteryRecipeTable.Entry( + Id: (int) id, + Type: (MasteryType) recipe.masteryType, + NoRewardExp: recipe.exceptRewardExp, + RequiredMastery: recipe.requireMastery, + RequiredMeso: recipe.requireMeso, + RequiredQuests: recipe.requireQuest, + RewardExp: recipe.rewardExp, + RewardMastery: recipe.rewardMastery, + HighRateLimitCount: recipe.highPropLimitCount, + NormalRateLimitCount: recipe.normalPropLimitCount, + RequiredItems: requiredItems, + HabitatMapId: recipe.habitatMapId, + RewardItems: rewardItems); + + results.Add((int) id, entry); + } + + return new MasteryRecipeTable(results); + } + + private static ItemComponent? ParseMasteryIngredient(IReadOnlyList ingredientArray) { + if (ingredientArray.Count == 0 || ingredientArray[0] == "0") { + return null; + } + + string[] idAndTag = ingredientArray[0].Split(":"); + int id = int.Parse(idAndTag[0]); + string tag = idAndTag.Length > 1 ? idAndTag[1] : string.Empty; + if (!short.TryParse(ingredientArray[1], out short rarity)) { + rarity = 1; + } + if (!int.TryParse(ingredientArray[2], out int amount)) { + amount = 1; + } + + return new ItemComponent( + ItemId: id, + Rarity: rarity, + Amount: amount, + Tag: string.IsNullOrWhiteSpace(tag) ? ItemTag.None : Enum.Parse(tag)); + } + + private static ItemComponent? ParseMasteryIngredient(IReadOnlyList ingredientArray) { + if (ingredientArray.Count == 0 || ingredientArray[0] == 0) { + return null; + } + + return new ItemComponent( + ItemId: ingredientArray[0], + Rarity: (short) ingredientArray[1], + Amount: ingredientArray[2], + Tag: ItemTag.None); + } + + private MasteryRewardTable ParseMasteryReward() { + var results = new Dictionary>(); + foreach ((Parser.Enum.MasteryType type, MasteryReward reward) in parser.ParseMasteryReward()) { + var masteryLevelDictionary = new Dictionary(); + foreach (MasteryLevel level in reward.v) { + masteryLevelDictionary.Add(level.grade, new MasteryRewardTable.Entry( + Value: level.value, + ItemId: level.rewardJobItemID, + ItemRarity: level.rewardJobItemRank, + ItemAmount: level.rewardJobItemCount)); + } + results.Add((MasteryType) type, masteryLevelDictionary); + } + return new MasteryRewardTable(results); + } + + private GuildTable ParseGuildTable() { + // Dictionary expTable = parser.ParseGuildExp() + // .ToDictionary(entry => (short) entry.Id, entry => entry.Item.value); + + var guildBuffs = new Dictionary>(); + foreach ((int id, IEnumerable buffs) in parser.ParseGuildBuff()) { + var buffLevels = new Dictionary(); + foreach (GuildBuff buff in buffs) { + buffLevels[buff.level] = new GuildTable.Buff( + Id: buff.additionalEffectId, + Level: buff.additionalEffectLevel, + RequireLevel: buff.requireLevel, + Cost: buff.cost, + UpgradeCost: buff.upgradeCost, + Duration: buff.duration); + } + guildBuffs.Add(id, buffLevels); + } + + var guildHouses = new Dictionary>(); + foreach ((int rank, IEnumerable houses) in parser.ParseGuildHouse()) { + var themes = new Dictionary(); + foreach (GuildHouse house in houses) { + themes.Add(house.theme, new GuildTable.House( + MapId: house.fieldID, + RequireLevel: house.upgradeReqGuildLevel, + UpgradeCost: house.upgradeCost, + ReThemeCost: house.rethemeCost, + Facilities: house.facility)); + } + guildHouses.Add(rank, themes); + } + + var guildNpcs = new Dictionary>(); + foreach ((Parser.Enum.GuildNpcType type, IEnumerable npcs) in parser.ParseGuildNpc()) { + var levels = new Dictionary(); + foreach (GuildNpc npc in npcs) { + levels.Add(npc.level, new GuildTable.Npc( + Type: (GuildNpcType) type, + Level: npc.level, + RequireGuildLevel: npc.requireGuildLevel, + RequireHouseLevel: npc.requireHouseLevel, + UpgradeCost: npc.upgradeCost)); + } + guildNpcs.Add((GuildNpcType) type, levels); + } + + var guildProperties = new SortedDictionary(); + foreach ((int level, GuildProperty property) in parser.ParseGuildProperty()) { + var entry = new GuildTable.Property( + Level: property.level, + Experience: property.accumExp, + Capacity: property.capacity, + FundMax: property.fundMax, + DonateMax: property.donationMax, + CheckInExp: property.attendGuildExp, + WinMiniGameExp: property.winMiniGameGuildExp, + LoseMiniGameExp: property.loseMiniGameGuildExp, + RaidExp: property.raidGuildExp, + CheckInFund: property.attendGuildFund, + WinMiniGameFund: property.winMiniGameGuildFund, + LoseMiniGameFund: property.loseMiniGameGuildFund, + RaidFund: property.raidGuildFund, + CheckInPlayerExpRate: property.attendUserExpFactor, + DonatePlayerExpRate: property.donationUserExpFactor, + CheckInCoin: property.attendGuildCoin, + DonateCoin: property.donateGuildCoin, + WinMiniGameCoin: property.winMiniGameGuildCoin, + LoseMiniGameCoin: property.loseMiniGameGuildCoin); + guildProperties.Add((short) level, entry); + } + + return new GuildTable( + Buffs: guildBuffs, + Houses: guildHouses, + Npcs: guildNpcs, + Properties: guildProperties); + } + + private FishingRodTable ParseFishingRod() { + var results = new Dictionary(); + foreach ((int id, FishingRod rod) in parser.ParseFishingRod()) { + var entry = new FishingRodTable.Entry( + ItemId: rod.itemCode, + MinMastery: rod.fishMasteryLimit, + AddMastery: rod.addFishMastery, + ReduceTime: rod.reduceFishingTime); + results.Add(id, entry); + } + return new FishingRodTable(results); + } + + private EnchantScrollTable ParseEnchantScrollTable() { + var results = new Dictionary(); + foreach ((int id, EnchantScroll scroll) in parser.ParseEnchantScroll()) { + var metadata = new EnchantScrollMetadata( + Type: (EnchantScrollType) scroll.scrollType, + MinLevel: scroll.minLv, + MaxLevel: scroll.maxLv, + Enchants: scroll.grade, + ItemTypes: scroll.slot, + Rarities: scroll.rank); + Array.Sort(metadata.Enchants); // Just in case + results.Add(id, metadata); + } + + return new EnchantScrollTable(results); + } + + private ItemRemakeScrollTable ParseItemRemakeScrollTable() { + var results = new Dictionary(); + foreach ((int id, ItemRemakeScroll scroll) in parser.ParseItemRemakeScroll()) { + results.Add(id, new ItemRemakeScrollMetadata( + MinLevel: scroll.minLv, + MaxLevel: scroll.maxLv, + ItemTypes: scroll.slot, + Rarities: scroll.rank, + RollAttribute: scroll.addOpKind == 1, + RollValueType: (RollValueType) scroll.addOpValue, + OnlyPet: scroll.onlyPet)); + } + + return new ItemRemakeScrollTable(results); + } + + private ItemRepackingScrollTable ParseItemRepackingScrollTable() { + var results = new Dictionary(); + foreach ((int id, ItemRepackingScroll scroll) in parser.ParseItemRepackingScroll()) { + results.Add(id, new ItemRepackingScrollMetadata( + MinLevel: scroll.minLv, + MaxLevel: scroll.maxLv, + ItemTypes: scroll.slot, + Rarities: scroll.rank, + IsPet: scroll.petType)); + } + + return new ItemRepackingScrollTable(results); + } + + private ItemSocketScrollTable ParseItemSocketScrollTable() { + // SELECT GROUP_CONCAT(Name), JSON_EXTRACT(`Function`, '$.Parameters') as param + // FROM item + // WHERE JSON_EXTRACT(`Function`, '$.Name')='ItemSocketScroll' + // GROUP BY param; + var socketCount = new Dictionary { + {10000001, 1}, {10000002, 2}, {10000003, 3}, + {10000011, 1}, {10000012, 2}, + {10000013, 1}, {10000014, 2}, + {10000015, 1}, + {10000016, 1}, + {10000017, 1}, + {10000018, 1}, + {10000019, 1}, + {10000020, 1}, + {10000021, 1}, + {10000022, 1}, {10000023, 2}, + {10000024, 1}, {10000025, 2}, + {10000026, 1}, {10000027, 2}, + {10000028, 1}, {10000029, 2}, + {10000030, 1}, + {10000031, 1}, {10000032, 2}, + {10000033, 1}, {10000034, 2}, + {10000035, 1}, {10000036, 2}, + }; + + var results = new Dictionary(); + foreach ((int id, ItemSocketScroll scroll) in parser.ParseItemSocketScroll()) { + results.Add(id, new ItemSocketScrollMetadata( + MinLevel: scroll.minLv, + MaxLevel: scroll.maxLv, + ItemTypes: scroll.slot, + Rarities: scroll.rank, + SocketCount: socketCount[id], + TradableCountDeduction: scroll.tradableCountDeduction)); + } + + return new ItemSocketScrollTable(results); + } + + private ItemExchangeScrollTable ParseItemExchangeScrollTable() { + var results = new Dictionary(); + foreach ((int id, ItemExchangeScroll scroll) in parser.ParseItemExchangeScroll()) { + var requiredItems = new List(); + foreach (ItemExchangeScroll.Item item in scroll.require.item) { + string[] idAndTag = item.id[0].Split(":"); + int requiredItemId = int.Parse(idAndTag[0]); + string requiredItemTag = idAndTag.Length > 1 ? idAndTag[1] : string.Empty; + if (!short.TryParse(item.id[1], out short rarity)) { + rarity = 1; + } + if (!int.TryParse(item.id[2], out int amount)) { + amount = 1; + } + requiredItems.Add(new ItemComponent( + ItemId: requiredItemId, + Tag: string.IsNullOrWhiteSpace(requiredItemTag) ? ItemTag.None : Enum.Parse(requiredItemTag), + Rarity: rarity, + Amount: amount)); + } + + results.Add(id, new ItemExchangeScrollMetadata( + RecipeScroll: new ItemComponent( + ItemId: scroll.receipe.id, + Rarity: (short) scroll.receipe.rank, + Amount: scroll.receipe.count, + Tag: ItemTag.None), + RewardItem: new ItemComponent( + ItemId: scroll.exchange.id, + Rarity: (short) scroll.exchange.rank, + Amount: scroll.exchange.count, + Tag: ItemTag.None), + TradeCountDeduction: scroll.tradableCountDeduction, + RequiredMeso: scroll.require.meso, + RequiredItems: requiredItems)); + } + return new ItemExchangeScrollTable(results); + } + + private PremiumClubTable ParsePremiumClubTable() { + var premiumClubBuffs = new Dictionary(); + foreach ((int id, PremiumClubEffect buff) in parser.ParsePremiumClubEffect()) { + premiumClubBuffs.Add(id, new PremiumClubTable.Buff( + Id: buff.effectID, + Level: buff.effectLevel)); + } + + var premiumClubItems = new Dictionary(); + foreach ((int id, PremiumClubItem item) in parser.ParsePremiumClubItem()) { + premiumClubItems.Add(id, new PremiumClubTable.Item( + Id: item.itemID, + Amount: item.itemCount, + Rarity: item.itemRank, + Period: 0)); + } + + var premiumClubPackages = new Dictionary(); + foreach ((int id, PremiumClubPackage package) in parser.ParsePremiumClubPackage()) { + DateTime startTime = DateTime.TryParseExact(package.salesStartDate, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out startTime) ? startTime : DateTime.MinValue; + DateTime endTime = DateTime.TryParseExact(package.salesEndDate, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out endTime) ? endTime : DateTime.MinValue; + var items = new List(); + for (int item = 0; item < package.bonusItemID.Length; item++) { + items.Add(new PremiumClubTable.Item( + Id: package.bonusItemID[item], + Amount: package.bonusItemCount[item], + Rarity: package.bonusItemRank[item], + Period: package.bonusItemPeriod[item])); + } + premiumClubPackages.Add(id, new PremiumClubTable.Package( + Disabled: package.disable, + StartDate: startTime.ToEpochSeconds(), + EndDate: endTime.ToEpochSeconds(), + Period: package.vipPeriod, + Price: package.price < package.salePrice ? package.salePrice : package.price, + BonusItems: items)); + } + + return new PremiumClubTable(premiumClubBuffs, premiumClubItems, premiumClubPackages); + } + + private IndividualItemDropTable ParseIndividualItemDropTable() { + var results = new Dictionary>>(); + results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDrop()); + results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropCharge()); + results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropEvent()); + results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropGacha()); + results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropPet()); + results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemGearBox()); + results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropEventNpc()); + results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropNewGacha()); + results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropQuestMob()); + results = MergeIndividualItemDropTable(results, parser.ParseIndividualItemDropQuestObj()); + + return new IndividualItemDropTable(results); + } + + private Dictionary>> MergeIndividualItemDropTable(Dictionary>> results, IEnumerable<(int Id, IDictionary>)> parser) { + foreach ((int id, IDictionary> dict) in parser) { + foreach ((byte dropGroup, List drops) in dict) { + foreach (IndividualItemDrop drop in drops) { + var itemIds = new List { + drop.item, + }; + if (drop.item2 > 0) { + itemIds.Add(drop.item2); + } + + float minCount = drop.minCount; + float maxCount = drop.maxCount; + if (drop.item == 90000008) { // Experience Orb + minCount *= 10000; + maxCount *= 10000; + } + + var entry = new IndividualItemDropTable.Entry( + ItemIds: itemIds.ToArray(), + SmartGender: drop.isApplySmartGenderDrop, + SmartDropRate: drop.smartDropRate, + Rarity: drop.PackageUIShowGrade, + EnchantLevel: drop.enchantLevel, + ReduceTradeCount: drop.tradableCountDeduction, + ReduceRepackLimit: drop.rePackingLimitCountDeduction, + Bind: drop.isBindCharacter, + MinCount: (int) minCount, + MaxCount: (int) maxCount); + + if (!results.ContainsKey(id)) { + results.Add(id, new Dictionary> { + {drop.dropGroup, new List { + entry, + }}, + }); + } else if (!results[id].ContainsKey(dropGroup)) { + results[id].Add(drop.dropGroup, new List() { + entry, + }); + } else { + results[id][dropGroup].Add(entry); + } + } + } + } + return results; + } + + private ColorPaletteTable ParseColorPaletteTable() { + var results = new Dictionary>(); + foreach ((int id, ColorPalette palette) in parser.ParseColorPalette()) { + foreach (ColorPalette.Color color in palette.color) { + var entry = new ColorPaletteTable.Entry( + Primary: ParseColor(color.ch0), + Secondary: ParseColor(color.ch1), + Tertiary: ParseColor(color.ch2), + AchieveId: color.achieveID, + AchieveGrade: color.achieveGrade); + if (!results.ContainsKey(id)) { + results.Add(id, new Dictionary { + {color.colorSN, entry}, + }); + } else { + (results[id] as Dictionary)!.Add(color.colorSN, entry); + } + } + } + return new ColorPaletteTable(results); + } + + private Color ParseColor(System.Drawing.Color color) { + return new Color(color.B, color.G, color.R, color.A); + } + + private MeretMarketCategoryTable ParseMeretMarketCategoryTable() { + var results = new Dictionary>(); + foreach ((int id, MeretMarketCategory category) in parser.ParseMeretMarketCategory()) { + foreach (MeretMarketCategory.Tab tab in category.tab) { + var subTabIds = new List(); + foreach (MeretMarketCategory.Tab subTab in tab.tab) { + var subTabEntry = new MeretMarketCategoryTable.Tab( + Categories: subTab.category, + SortGender: subTab.sortGender, + SortJob: subTab.sortJob, + SubTabIds: []); + subTabIds.Add(subTab.id); + if (!results.ContainsKey(id)) { + results.Add(id, new Dictionary { + {subTab.id, subTabEntry}, + }); + } else { + (results[id] as Dictionary)!.Add(subTab.id, subTabEntry); + } + } + var tabEntry = new MeretMarketCategoryTable.Tab( + Categories: tab.category, + SortGender: tab.sortGender, + SortJob: tab.sortJob, + SubTabIds: subTabIds.ToArray()); + + if (!results.ContainsKey(id)) { + results.Add(id, new Dictionary { + {tab.id, tabEntry}, + }); + } else { + (results[id] as Dictionary)!.Add(tab.id, tabEntry); + } + } + } + return new MeretMarketCategoryTable(results); + } + + private ShopBeautyCouponTable ParseShopBeautyCouponTable() { + var results = new Dictionary>(); + foreach ((int id, ShopBeautyCoupon coupon) in parser.ParseShopBeautyCoupon()) { + results.Add(id, new List(coupon.item.Select(item => item.id))); + } + + return new ShopBeautyCouponTable(results); + } + private GachaInfoTable ParseGachaInfoTable() { + var results = new Dictionary(); + foreach ((int randomBoxId, GachaInfo gachaInfo) in parser.ParseGachaInfo()) { + results.Add(randomBoxId, new GachaInfoTable.Entry( + RandomBoxGroup: gachaInfo.randomBoxGroup, + DropBoxId: gachaInfo.individualDropBoxID, + ShopId: gachaInfo.shopID, + CoinItemId: gachaInfo.coinItemID, + CoinItemAmount: gachaInfo.coinItemAmount)); + } + + return new GachaInfoTable(results); + } + + private FurnishingShopTable ParseFurnishingShopTable() { + var results = new Dictionary(); + foreach ((int id, ShopFurnishing shop) in parser.ParseFurnishingShopUgcAll().Concat(parser.ParseFurnishingShopMaid())) { + results.Add(id, new FurnishingShopTable.Entry( + ItemId: shop!.id, + Buyable: shop.ugcHousingBuy, + FurnishingTokenType: (FurnishingCurrencyType) shop.ugcHousingMoneyType, + Price: shop.ugcHousingDefaultPrice + )); + } + + return new FurnishingShopTable(results); + } + + private InsigniaTable ParseInsigniaTable() { + var results = new Dictionary(); + foreach ((int id, NameTagSymbol symbol) in parser.ParseNameTagSymbol()) { + results.Add(id, new InsigniaTable.Entry( + Type: (InsigniaConditionType) symbol.conditionType, + Code: symbol.code, + BuffId: symbol.buffID, + BuffLevel: symbol.buffLv)); + } + + return new InsigniaTable(results); + } + + private ExpTable ParseExpTable() { + var baseResults = new Dictionary>(); + foreach ((int tableId, ExpBaseTable table) in parser.ParseExpBaseTable()) { + foreach (ExpBaseTable.Base tableBase in table.@base) { + if (!baseResults.ContainsKey(tableId)) { + baseResults.Add(tableId, new Dictionary{ + {tableBase.level, tableBase.exp}, + }); + } else { + (baseResults[tableId] as Dictionary)!.Add(tableBase.level, tableBase.exp); + } + } + } + + var nextExpResults = new Dictionary(); + foreach ((int level, NextExp entry) in parser.ParseNextExp()) { + nextExpResults.Add(entry.level, entry.value); + } + return new ExpTable(baseResults, nextExpResults); + } + + private CommonExpTable ParseCommonExpTable() { + var results = new Dictionary(); + foreach ((CommonExpType type, CommonExp exp) in parser.ParseCommonExp()) { + results.Add(ToExpType(type), new CommonExpTable.Entry(ExpTableId: exp.expTableID, Factor: exp.factor)); + } + return new CommonExpTable(results); + } + + private static ExpType ToExpType(CommonExpType commonExpType) { + if (Enum.TryParse(commonExpType.ToString(), out ExpType expType)) { + return expType; + } + return ExpType.none; + } + + private UgcDesignTable ParseUgcDesignTable() { + var results = new Dictionary(); + foreach ((int id, UgcDesign design) in parser.ParseUgcDesign()) { + results.Add(id, new UgcDesignTable.Entry( + ItemRarity: design.itemGrade, + CurrencyType: (MeretMarketCurrencyType) design.priceType, + CreatePrice: design.salePrice < design.price ? design.salePrice : design.price, + MarketMinPrice: design.marketMinPrice, + MarketMaxPrice: design.marketMaxPrice)); + } + return new UgcDesignTable(results); + } + + private LearningQuestTable ParseLearningQuestTable() { + var results = new Dictionary(); + foreach ((int id, LearningQuest quest) in parser.ParseLearningQuest()) { + results.Add(id, new LearningQuestTable.Entry( + Category: quest.category, + RequiredLevel: quest.reqLevel, + QuestId: quest.reqQuest, + RequiredMapId: quest.reqField, + GoToMapId: quest.gotoField, + GoToPortalId: quest.gotoPortal)); + } + return new LearningQuestTable(results); + } + + private PrestigeLevelAbilityTable ParsePrestigeLevelAbilityTable() { + var results = new Dictionary(); + foreach ((int id, AdventureLevelAbility ability) in parser.ParseAdventureLevelAbility()) { + results.Add(id, new PrestigeLevelAbilityMetadata( + Id: id, + RequiredLevel: ability.requireLevel, + Interval: ability.interval, + MaxCount: ability.maxCount, + BuffId: ability.additionalEffectId, + StartValue: ability.startValue, + AddValue: ability.addValue)); + } + return new PrestigeLevelAbilityTable(results); + } + + private PrestigeLevelRewardTable ParsePrestigeLevelRewardTable() { + var results = new Dictionary(); + foreach ((int id, AdventureLevelReward reward) in parser.ParseAdventureLevelReward()) { + results.Add(id, new PrestigeLevelRewardMetadata( + Id: reward.id, + Level: reward.level, + Type: Enum.TryParse(reward.type, out PrestigeAwardType type) ? type : PrestigeAwardType.none, + Rarity: reward.rank, + Value: reward.value + )); + } + return new PrestigeLevelRewardTable(results); + } + + private PrestigeMissionTable ParsePrestigeMissionTable() { + var results = new Dictionary(); + foreach ((int id, AdventureLevelMission mission) in parser.ParseAdventureLevelMission()) { + results.Add(id, new PrestigeMissionMetadata( + Id: mission.missionId, + Count: mission.missionCount, + Item: new ItemComponent( + ItemId: mission.itemId, + Rarity: mission.itemRank, + mission.itemCount, + Tag: ItemTag.None))); + } + return new PrestigeMissionTable(results); + } + + private BlackMarketTable ParseBlackMarketTable() { + var results = new Dictionary(); + (int id, BlackMarketCategory blackMarket) category = parser.ParseBlackMarketCategory(); + foreach (BlackMarketCategory.BlackMarketTab item in category.blackMarket.tab) { + ParseBlackMarketTab(item, results); + } + + return new BlackMarketTable(results); + } + + private void ParseBlackMarketTab(BlackMarketCategory.BlackMarketTab tab, Dictionary results) { + results.Add(tab.id, tab.category); + if (tab.tab.Count > 0) { + foreach (BlackMarketCategory.BlackMarketTab subTab in tab.tab) { + ParseBlackMarketTab(subTab, results); + } + } + } + + private ChangeJobTable ParseChangeJobTable() { + var results = new Dictionary(); + foreach ((int jobId, ChangeJob job) in parser.ParseChangeJob()) { + results.Add((Job) jobId, new ChangeJobMetadata( + Job: (Job) job.subJobCode, + ChangeJob: (Job) job.changeSubJobCode, + StartQuestId: job.startquestid, + EndQuestId: job.endquestid + )); + } + return new ChangeJobTable(results); + } + + private ChapterBookTable ParseChapterBookTable() { + var results = new Dictionary(); + foreach ((int id, ChapterBook book) in parser.ParseChapterBook()) { + var items = new List(); + var skillpoints = new List(); + int statPoints = 0; + switch (book.rewardType1) { + case QuestRewardType.skillPoint: + skillpoints.Add(ParseSkillPoint(book.rewardValue1)); + break; + case QuestRewardType.item: + items.Add(ParseItem(book.rewardValue1)); + break; + case QuestRewardType.statPoint: + statPoints += int.Parse(book.rewardValue1[0]); + break; + default: + break; + } + + switch (book.rewardType2) { + case QuestRewardType.skillPoint: + skillpoints.Add(ParseSkillPoint(book.rewardValue2)); + break; + case QuestRewardType.item: + items.Add(ParseItem(book.rewardValue2)); + break; + case QuestRewardType.statPoint: + statPoints += int.Parse(book.rewardValue2[0]); + break; + default: + break; + } + results.Add(id, new ChapterBookTable.Entry( + Id: id, + BeginQuestId: book.prologue, + EndQuestId: book.epilogue, + SkillPoints: skillpoints.ToArray(), + StatPoints: statPoints, + Items: items.ToArray())); + } + + return new ChapterBookTable(results); + + ChapterBookTable.Entry.SkillPoint ParseSkillPoint(string[] rewardValue) { + return new ChapterBookTable.Entry.SkillPoint( + Amount: int.Parse(rewardValue[0]), + Rank: short.Parse(rewardValue[1])); + } + + ItemComponent ParseItem(string[] rewardValue) { + return new ItemComponent( + ItemId: int.Parse(rewardValue[0]), + Amount: short.Parse(rewardValue[1]), + Rarity: int.Parse(rewardValue[2]), + Tag: ItemTag.None); + } + } + + private FieldMissionTable ParseFieldMissionTable() { + var results = new Dictionary(); + foreach ((int id, FieldMission mission) in parser.ParseFieldMission()) { + switch (mission.type) { + case QuestRewardType.item: + results.Add(id, new FieldMissionTable.Entry( + MissionCount: mission.mission, + StatPoints: 0, + Item: new ItemComponent( + ItemId: mission.value[0], + Rarity: mission.value[1], + Amount: mission.value[2], + Tag: ItemTag.None))); + continue; + case QuestRewardType.statPoint: + results.Add(id, new FieldMissionTable.Entry( + MissionCount: mission.mission, + StatPoints: mission.value[0], + Item: null)); + continue; + } + } + return new FieldMissionTable(results); + } + + private WorldMapTable ParseWorldMapTable() { + var mapList = new List(); + foreach ((string feature, var maps) in parser.ParseWorldMap()) { + if (feature != "Kritias_2018_12") { + continue; + } + foreach (var map in maps) { + if (!map.@public) { + continue; + } + + mapList.Add(new WorldMapTable.Map(map.code, map.x, map.y, map.z, map.size)); + } + } + + if (mapList.Count == 0) { + throw new InvalidOperationException("No maps ingested for WorldMapTable"); + } + return new WorldMapTable(mapList); + } + + private SurvivalSkinInfoTable ParseSurvivalSkinTable() { + var results = new Dictionary(); + foreach ((int id, MapleSurvivalSkinInfo skin) in parser.ParseMapleSurvivalSkinInfo()) { + MedalType type = skin.type switch { + SurvivalSkinType.gliding => MedalType.Gliding, + SurvivalSkinType.riding => MedalType.Riding, + SurvivalSkinType.effectTail => MedalType.Tail, + _ => throw new InvalidOperationException("Unknown SurvivalSkinType"), + }; + results.Add(id, type); + } + + return new SurvivalSkinInfoTable(results); + } + + private BannerTable ParseBanner() { + List results = []; + foreach ((int id, Banner banner) in parser.ParseBanner()) { + results.Add(new BannerTable.Entry( + Id: id, + MapId: banner.field, + Price: banner.price.ToList() + )); + } + return new BannerTable(results); + } + + private MasteryUgcHousingTable ParseMasteryUgcHousingTable() { + var results = new Dictionary(); + foreach ((int id, MasteryUgcHousing ugcHousing) in parser.ParseMasteryUgcHousing()) { + results.Add(id, new MasteryUgcHousingTable.Entry( + Level: ugcHousing.grade, + Exp: ugcHousing.value, + RewardJobItemId: ugcHousing.rewardJobItemID)); + } + return new MasteryUgcHousingTable(results); + } + + private UgcHousingPointRewardTable ParseUgcHousingPointRewardTable() { + var results = new Dictionary(); + foreach ((int id, UgcHousingPointReward ugcHousing) in parser.ParseUgcHousingPointReward()) { + results.Add(id, new UgcHousingPointRewardTable.Entry( + DecorationScore: ugcHousing.housingPoint, + IndividualDropBoxId: ugcHousing.individualDropBoxId)); + } + return new UgcHousingPointRewardTable(results); + } + + private WeddingTable ParseWeddingTable() { + var rewardsResults = new Dictionary(); + foreach ((WeddingRewardType type, Parser.Xml.Table.WeddingReward reward) in parser.ParseWeddingReward()) { + rewardsResults.Add((MarriageExpType) type, new WeddingReward( + Type: (MarriageExpType) type, + Amount: reward.rewardExp, + Limit: (MarriageExpLimit) reward.rewardLimit)); + } + + var packageResults = new Dictionary(); + foreach ((int id, Parser.Xml.Table.WeddingPackage package) in parser.ParseWeddingPackage()) { + var hallDataDic = new Dictionary(); + foreach (WeddingHall hall in package.weddingHall) { + List hallItems = []; + foreach (WeddingItem item in hall.weddingItem) { + hallItems.Add(new WeddingPackage.HallData.Item( + ItemId: item.itemID, + Amount: item.count, + Rarity: item.grade, + NightOnly: item.nightReward)); + } + + List completeHallItems = []; + foreach (WeddingItem item in hall.weddingCompleteItem) { + completeHallItems.Add(new WeddingPackage.HallData.Item( + ItemId: item.itemID, + Amount: item.count, + Rarity: item.grade, + NightOnly: item.nightReward)); + } + + hallDataDic.Add(hall.id, new WeddingPackage.HallData( + Id: hall.id, + MapId: hall.fieldID, + NightMapId: hall.nightFieldID, + Tier: hall.grade, + MeretCost: hall.merat, + Items: hallItems, + CompleteItems: completeHallItems)); + } + packageResults.Add(id, new WeddingPackage( + Id: id, + PlannerId: package.planner, + Halls: hallDataDic)); + } + return new WeddingTable(rewardsResults, packageResults); + } + + private DungeonRoomTable ParseDungeonRoom() { + var dungeons = new Dictionary(); + foreach ((int id, DungeonRoom dungeon) in parser.ParseDungeonRoom()) { + dungeons.Add(id, new DungeonRoomMetadata( + Id: dungeon.dungeonRoomID, + Level: dungeon.dungeonLevel, + PlayType: (DungeonPlayType) dungeon.playType, + GroupType: (DungeonGroupType) dungeon.groupType, + CooldownType: (DungeonCooldownType) dungeon.cooldownType, + CooldownValue: dungeon.cooldownType == Parser.Enum.DungeonCooldownType.dayOfWeeks ? dungeon.cooldownValue + 1 : dungeon.cooldownValue, // dayOfWeeks is 0-indexed + DurationTick: dungeon.durationTick, + LobbyFieldId: dungeon.lobbyFieldID, + FieldIds: dungeon.fieldIDs, + Reward: new DungeonRoomRewardMetadata( + AccountWide: dungeon.isAccountReward, + Count: dungeon.rewardCount, + SubRewardCount: dungeon.subRewardCount, + Exp: dungeon.rewardExp, + ExpRate: dungeon.rewardExpRate, + Meso: dungeon.rewardMeso, + LimitedDropBoxIds: dungeon.rewardLimitedDropBoxIds, + UnlimitedDropBoxIds: dungeon.rewardUnlimitedDropBoxIds, + UnionRewardId: dungeon.unionRewardID, + SeasonRankRewardId: dungeon.seasonRankRewardID, + ScoreBonusId: dungeon.scoreBonusId), + Limit: new DungeonRoomLimitMetadata( + MinUserCount: dungeon.minUserCount, + MaxUserCount: dungeon.maxUserCount, + GearScore: dungeon.gearScore, + MinLevel: dungeon.limitPlayerLevel, + RequiredAchievementId: dungeon.limitAchieveID, + VipOnly: dungeon.limitVIP, + DayOfWeeks: dungeon.limitDayOfWeeks.Length == 0 ? [] : dungeon.limitDayOfWeeks.Select(ParseDayOfWeek).ToArray(), + ClearDungeonIds: dungeon.limitClearDungeon, + Buffs: dungeon.limitAdditionalEffects, + DisableMeretRevival: dungeon.limitMeratRevival, + EquippedRecommendedWeapon: dungeon.limitRecommendWeapon, + PartyOnly: dungeon.isPartyOnly, + ChangeMaxUsers: dungeon.isChangeMaxUser, + DisableMesoRevival: dungeon.limitMesoRevival, + MaxRevivalCount: dungeon.defaultRevivalLimitCount), + PlayerCountFactorId: dungeon.playerCountFactorID, + CustomMonsterLevel: dungeon.customMonsterLevel, + HelperRequireClearCount: dungeon.dungeonHelperRequireClearCount, + DisabledFindHelper: dungeon.isDisableFindHelper, + RankTableId: dungeon.rankTableID, + RoundId: dungeon.roundID, + LeaveAfterCloseReward: dungeon.isLeaveAfterCloseReward, + PartyMissions: dungeon.partyMissions, + UserMissions: dungeon.userMissions, + MoveToBackupField: dungeon.isMoveOutToBackupField + )); + } + + return new DungeonRoomTable(dungeons); + + DayOfWeek ParseDayOfWeek(Maple2.File.Parser.Enum.DayOfWeek dayofWeek) { + return dayofWeek switch { + Maple2.File.Parser.Enum.DayOfWeek.sun => DayOfWeek.Sunday, + Maple2.File.Parser.Enum.DayOfWeek.mon => DayOfWeek.Monday, + Maple2.File.Parser.Enum.DayOfWeek.tue => DayOfWeek.Tuesday, + Maple2.File.Parser.Enum.DayOfWeek.wed => DayOfWeek.Wednesday, + Maple2.File.Parser.Enum.DayOfWeek.thu => DayOfWeek.Thursday, + Maple2.File.Parser.Enum.DayOfWeek.fri => DayOfWeek.Friday, + Maple2.File.Parser.Enum.DayOfWeek.sat => DayOfWeek.Saturday, + _ => DayOfWeek.Sunday, + }; + } + } + + private DungeonRankRewardTable ParseDungeonRankReward() { + var results = new Dictionary(); + foreach ((int id, DungeonRankReward reward) in parser.ParseDungeonRankReward()) { + List rewards = []; + foreach (DungeonRankRewardEntry item in reward.v) { + rewards.Add(new DungeonRankRewardTable.Entry.Item( + Rank: item.rank, + ItemId: item.itemID, + SystemMailId: item.systemMailID)); + } + + results.Add(id, new DungeonRankRewardTable.Entry( + Id: id, + Items: rewards.ToArray())); + } + + return new DungeonRankRewardTable(results); + } + + private DungeonConfigTable ParseDungeonConfigTable() { + var missionRankResults = new Dictionary(); + foreach (DungeonConfig config in parser.ParseDungeonConfig()) { + MissionRank missionRank = config.MissionRank.First(); + foreach (MissionRankGroup group in missionRank.group) { + var scores = new List(); + for (int i = 0; i < group.rank.Count; i++) { + scores.Add(new DungeonMissionRankMetadata.Score( + Grade: (DungeonMissionRank) (i + 1), // Ranking start at C + Value: group.rank[i].score)); + } + missionRankResults.Add(group.id, new DungeonMissionRankMetadata( + Id: group.id, + Description: group.desc, + MaxScore: group.maxScore, + Scores: scores.ToArray())); + } + break; // Break because there should only be one entry. + } + + var unitedWeeklyResults = new Dictionary(); + UnitedWeeklyReward reward = parser.ParseUnitedWeeklyReward().First(); + foreach (UnitedWeeklyRewardEntry item in reward.v) { + unitedWeeklyResults.Add(item.rewardCount, item.rewardID); + } + + return new DungeonConfigTable(unitedWeeklyResults, missionRankResults); + } + + private DungeonMissionTable ParseDungeonMissionTable() { + var results = new Dictionary(); + foreach ((int id, DungeonMission mission) in parser.ParseDungeonMission()) { + if (!Enum.TryParse(mission.type, out DungeonMissionType type)) { + Console.WriteLine($"Unknown Mission type: {mission.type}"); + continue; + } + results.Add(id, new DungeonMissionMetadata( + Id: id, + Type: type, + Value1: Array.ConvertAll(mission.value1, element => (long) element), + Value2: mission.value2, + MaxScore: (short) mission.maxScore, + ApplyCount: (short) mission.applyCount, + IsPenaltyType: mission.isPenaltyType)); + } + return new DungeonMissionTable(results); + } + + private RewardContentTable ParseRewardContentTable() { + var baseResults = new Dictionary(); + foreach ((int id, RewardContent rewardContent) in parser.ParseRewardContent()) { + baseResults.Add(id, new RewardContentTable.Base( + Id: id, + ExpTableId: rewardContent.expTableID, + MesoTableId: rewardContent.mesoTableID, + ExpFactor: rewardContent.expFactor, + MesoFactor: rewardContent.mesoFactor, + ItemTableId: rewardContent.itemTableID, + PrestigeExpTableId: rewardContent.adventureExpTableID)); + } + + var itemResults = new Dictionary(); + foreach ((int id, RewardContentItem content) in parser.ParseRewardContentItem()) { + List itemData = []; + foreach (RewardContentValue value in content.v) { + List items = []; + foreach (RewardContentItemEntry item in value.item) { + items.Add(new RewardItem(item.itemID, (short) item.grade, item.count)); + } + itemData.Add(new RewardContentTable.Item.Data( + MinLevel: value.minLevel, + MaxLevel: value.maxLevel, + RewardItems: items.ToArray())); + } + itemResults.Add(id, new RewardContentTable.Item( + Id: id, + ItemData: itemData.ToArray())); + } + + var mesoStaticResults = new Dictionary(); + foreach ((int id, RewardContentMesoStatic reward) in parser.ParseRewardContentMesoStatic()) { + mesoStaticResults.Add(id, reward.v.FirstOrDefault()?.meso ?? 0); + } + + var mesoResults = new Dictionary>(); + foreach ((int id, RewardContentMeso reward) in parser.ParseRewardContentMeso()) { + var entries = new Dictionary(); + foreach (RewardContentMesoValue value in reward.v) { + entries.Add(value.level, value.meso); + } + + mesoResults.Add(id, entries); + } + + var expStaticResults = new Dictionary(); + foreach ((int id, RewardContentExpStatic reward) in parser.ParseRewardContentExpStatic()) { + expStaticResults.Add(id, reward.@base.FirstOrDefault()?.exp ?? 0); + } + + return new RewardContentTable(baseResults, itemResults, mesoStaticResults, mesoResults, expStaticResults); + } + + private SeasonDataTable ParseSeasonDataTable() { + return new SeasonDataTable( + Arcade: ParseSeasonData(parser.ParseSeasonDataArcade()), + Boss: ParseSeasonData(parser.ParseSeasonDataBossColosseum()), + DarkDescent: ParseSeasonData(parser.ParseSeasonDataDarkStream()), + GuildPvp: ParseSeasonData(parser.ParseSeasonDataGuildPvp()), + Survival: ParseSeasonData(parser.ParseSeasonDataMapleSurvival()), + SurvivalSquad: ParseSeasonData(parser.ParseSeasonDataMapleSurvivalSquad()), + Pvp: ParseSeasonData(parser.ParseSeasonDataPvp()), + UgcMapCommendation: ParseSeasonData(parser.ParseSeasonDataUgcMapCommendation()), + WorldChampionship: ParseSeasonData(parser.ParseSeasonDataWorldChampion())); + + IReadOnlyDictionary ParseSeasonData(IEnumerable<(int, SeasonData)> seasonDataParser) { + var results = new Dictionary(); + + foreach ((int id, SeasonData seasonData) in seasonDataParser) { + results.Add(id, new SeasonDataTable.Entry( + Id: seasonData.seasonID, + StartTime: DateTime.TryParseExact(seasonData.eventStart, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime startTime) ? startTime : DateTime.MinValue, + EndTime: DateTime.TryParseExact(seasonData.eventEnd, "yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime endTime) ? endTime : DateTime.MaxValue, + Grades: [ + seasonData.grade1, + seasonData.grade2, + seasonData.grade3, + seasonData.grade4, + seasonData.grade5, + seasonData.grade6, + seasonData.grade7, + ])); + } + return results; + } + } + + private SmartPushTable ParseSmartPushTable() { + var results = new Dictionary(); + foreach ((int id, SmartPush smartPush) in parser.ParseSmartPush()) { + var requiredItem = new IngredientInfo(ItemTag.None, 0); + var requiredItemTag = ItemTag.None; + if (smartPush.requireItem.Length != 0) { + string[] requiredItemArray = smartPush.requireItem[0].Split(":"); + if (requiredItemArray.Length == 2) { + requiredItemTag = Enum.TryParse(requiredItemArray[1], out ItemTag tag) ? tag : ItemTag.None; + } + requiredItem = new IngredientInfo( + tag: requiredItemTag, + amount: int.Parse(smartPush.requireItem[2])); + } + + results.Add(id, new SmartPushMetadata( + Id: id, + Content: smartPush.content, + Type: Enum.TryParse(smartPush.actionType, out SmartPushType type) ? type : SmartPushType.none, + Value: smartPush.actionValue, + MeretCost: smartPush.requireMerat, + RequiredItem: requiredItem)); + } + return new SmartPushTable(results); + } + + private AutoActionTable ParseAutoActionTable() { + var results = new Dictionary>(); + IEnumerable> groups = parser.ParseAutoActionPricePackage() + .Select(entry => entry.Data) + .GroupBy(entry => entry.content); + foreach (IGrouping group in groups) { + var packages = new Dictionary(); + foreach (AutoActionPricePackage package in group) { + packages.Add(package.id, new AutoActionMetaData( + Content: package.content, + Id: package.id, + Duration: package.duration, + MeretCost: package.merat, + MesoCost: package.meso)); + } + results.Add(group.Key, packages); + } + return new AutoActionTable(results); + } +} diff --git a/Maple2.File.Ingest/Mapper/TriggerMapper.cs b/Maple2.File.Ingest/Mapper/TriggerMapper.cs index c12da62c8..c7b894e9b 100644 --- a/Maple2.File.Ingest/Mapper/TriggerMapper.cs +++ b/Maple2.File.Ingest/Mapper/TriggerMapper.cs @@ -1,261 +1,261 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Text; -using System.Xml; -using M2dXmlGenerator; -using Maple2.File.Ingest.Utils; -using Maple2.File.IO; -using Maple2.File.IO.Crypto.Common; -using Maple2.Model.Metadata; -using Maple2.Tools; -using static System.Char; - - -namespace Maple2.File.Ingest.Mapper; - -public class TriggerMapper : TypeMapper { - private readonly M2dReader reader; - - public TriggerMapper(M2dReader reader) { - this.reader = reader; - } - - protected override IEnumerable Map() { - IEnumerable triggers = reader.Files.Where(file => file.Name.StartsWith("trigger/")); - foreach (PackFileEntry file in triggers) { - // get the folder name from the file path after "trigger/" - string[] filePath = file.Name["trigger/".Length..].Split('/'); - string folderName = filePath[0]; - string triggerName = filePath[1].Split(".")[0]; // remove the file extension - string xml = NormalizeTriggerXmlNames(reader.GetXmlDocument(file)); - - var trigger = new TriggerMetadata(folderName, triggerName, xml); - - if (Constant.DebugTriggers) { // for debugging purposes - string filePathName = Path.Combine(Paths.DEBUG_TRIGGERS_DIR, folderName, $"{triggerName}.xml"); - Directory.CreateDirectory(Path.GetDirectoryName(filePathName)!); - - var formattedXml = new XmlDocument(); - formattedXml.LoadXml(xml); - var settings = new XmlWriterSettings { - Indent = true, - NewLineOnAttributes = false, - OmitXmlDeclaration = true, - }; - - using var writer = XmlWriter.Create(filePathName, settings); - formattedXml.Save(writer); - } - yield return trigger; - } - } - - private static readonly Dictionary SubStart = new() { - { "1st", "First" }, - { "2nd", "Second" }, - { "3rd", "Third" }, - { "4th", "Fourth" }, - { "5th", "Fifth" }, - { "6th", "Sixth" }, - { "7th", "Seventh" }, - }; - - public static string NormalizeTriggerXmlNames(XmlDocument xml) { - // check for state nodes with feature attributes and remove disabled ones - List nodesToRemove = new List(); - foreach (XmlNode node in xml.SelectNodes("//state")!) { - XmlAttribute? featureAttr = node.Attributes?["feature"]; - if (featureAttr != null && !FeatureLocaleFilter.FeatureEnabled(featureAttr.Value)) { - nodesToRemove.Add(node); - } - } - - // Check for action nodes with feature attributes and remove disabled ones - foreach (XmlNode node in xml.SelectNodes("//action")!) { - XmlAttribute? featureAttr = node.Attributes?["feature"]; - if (featureAttr != null && !FeatureLocaleFilter.FeatureEnabled(featureAttr.Value)) { - nodesToRemove.Add(node); - } - } - - // Check for condition nodes with feature attributes and remove disabled ones - foreach (XmlNode node in xml.SelectNodes("//condition")!) { - XmlAttribute? featureAttr = node.Attributes?["feature"]; - if (featureAttr != null && !FeatureLocaleFilter.FeatureEnabled(featureAttr.Value)) { - nodesToRemove.Add(node); - } - } - - // Remove disabled feature nodes - foreach (XmlNode node in nodesToRemove) { - node.ParentNode?.RemoveChild(node); - } - - // Continue with the existing normalization logic - foreach (XmlNode node in xml.SelectNodes("//state")!) { - XmlAttribute? attr = node.Attributes?["name"]; - Debug.Assert(attr?.Value != null, "Unable to find name param"); - attr.Value = FixClassName(attr.Value); - } - - foreach (XmlNode node in xml.SelectNodes("//transition")!) { - XmlAttribute? attr = node.Attributes?["state"]; - Debug.Assert(attr?.Value != null, "Unable to find state param"); - attr.Value = FixClassName(attr.Value); - } - - foreach (XmlNode node in xml.SelectNodes("//action")!) { - string actionName = string.Empty; - List nodeParams = []; - foreach (XmlAttribute? attribute in node.Attributes!) { - if (attribute is null) continue; - if (attribute.Name is "name") { - attribute.Value = Translate(attribute.Value, TriggerTranslate.TranslateAction); - actionName = attribute.Value; - continue; - } - - nodeParams.Add(attribute); - } - - if (!TriggerDefinitionOverride.ActionOverride.TryGetValue(actionName, out TriggerDefinitionOverride? overrideValue)) continue; - - if (overrideValue.FunctionSplitter is not null) { - XmlAttribute? attributeSplitter = nodeParams.FirstOrDefault(x => x.Name == overrideValue.FunctionSplitter); - if (attributeSplitter is not null) { - overrideValue.FunctionLookup.TryGetValue(attributeSplitter.Value, out overrideValue); - Debug.Assert(overrideValue is not null, $"Unable to find override for {attributeSplitter.Value}"); - } else { - string? valueDefault = overrideValue.Types.FirstOrDefault().Value; - Debug.Assert(valueDefault is not null, $"Unable to find default value for {overrideValue.Name}"); - overrideValue.FunctionLookup.TryGetValue(valueDefault, out overrideValue); - Debug.Assert(overrideValue is not null, $"Unable to find override for {valueDefault}"); - } - node.Attributes["name"]!.Value = overrideValue.Name; - } - - foreach (XmlAttribute xmlAttribute in nodeParams) { - overrideValue.Names.TryGetValue(TriggerTranslate.ToSnakeCase(xmlAttribute.Name), out string? newName); - if (newName is null) { - if (xmlAttribute.Name != TriggerTranslate.ToSnakeCase(xmlAttribute.Name)) { - newName = TriggerTranslate.ToSnakeCase(xmlAttribute.Name); - } else { - continue; - } - } - - node.Attributes.Remove(xmlAttribute); - XmlAttribute newAttribute = xml.CreateAttribute(newName); - newAttribute.Value = xmlAttribute.Value; - node.Attributes.Append(newAttribute); - } - } - - foreach (XmlNode node in xml.SelectNodes("//condition")!) { - string conditionName = string.Empty; - List nodeParams = []; - foreach (XmlAttribute? attribute in node.Attributes!) { - if (attribute is null) continue; - if (attribute.Name is "name") { - conditionName = attribute.Value; - continue; - } - - nodeParams.Add(attribute); - } - - if (conditionName.StartsWith('!')) { - XmlAttribute negateAttribute = xml.CreateAttribute("negate"); - negateAttribute.Value = "true"; - node.Attributes.Append(negateAttribute); - } - - conditionName = conditionName.TrimStart('!'); - node.Attributes["name"]!.Value = Translate(conditionName, TriggerTranslate.TranslateCondition); - - if (!TriggerDefinitionOverride.ConditionOverride.TryGetValue(node.Attributes["name"]!.Value, out TriggerDefinitionOverride? overrideValue)) continue; - if (overrideValue.Name != node.Attributes["name"]!.Value) { - node.Attributes["name"]!.Value = overrideValue.Name; - } - foreach (XmlAttribute xmlAttribute in nodeParams) { - overrideValue.Names.TryGetValue(TriggerTranslate.ToSnakeCase(xmlAttribute.Name), out string? newName); - if (newName is null) { - if (xmlAttribute.Name != TriggerTranslate.ToSnakeCase(xmlAttribute.Name)) { - newName = TriggerTranslate.ToSnakeCase(xmlAttribute.Name); - } else { - continue; - } - } - - node.Attributes.Remove(xmlAttribute); - XmlAttribute newAttribute = xml.CreateAttribute(newName); - newAttribute.Value = xmlAttribute.Value; - node.Attributes.Append(newAttribute); - } - } - - return xml.OuterXml; - } - - [return: NotNullIfNotNull(nameof(name))] - private static string? FixClassName(string? name) { - if (name == null) { - return null; - } - if (string.IsNullOrWhiteSpace(name)) { - return "State"; - } - - // Reserved Keywords - switch (name) { - case "None": - return "StateNone"; - case "True": - return "StateTrue"; - case "False": - return "StateFalse"; - case "del": - return "StateDelete"; - } - - name = name.Replace("-", "To").Replace(" ", "_").Replace(".", "_"); - foreach ((string key, string value) in SubStart) { - if (name.StartsWith(key)) { - name = name.Replace(key, value); - break; - } - } - - string prefix = ""; - while (name.Length > 0 && !IsLetter(name[0])) { - if (name[0] != '_') { - prefix += name[0]; - } - name = name[1..]; - } - - // name is already valid - if (prefix.Length == 0) { - return name; - } - if (name.Length == 0) { - return $"State{prefix}"; - } - - return !IsLetter(name[^1]) ? $"{name}_{prefix}" : $"{name}{prefix}"; - } - - [return: NotNullIfNotNull(nameof(name))] - private static string? Translate(string? name, Func translator) { - if (name == null) { - return null; - } - - var builder = new StringBuilder(); - foreach (string split in name.Split('_', ' ')) { - builder.Append(translator(split)); - } - - return TriggerTranslate.ToSnakeCase(builder.ToString()); - } -} +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Xml; +using M2dXmlGenerator; +using Maple2.File.Ingest.Utils; +using Maple2.File.IO; +using Maple2.File.IO.Crypto.Common; +using Maple2.Model.Metadata; +using Maple2.Tools; +using static System.Char; + + +namespace Maple2.File.Ingest.Mapper; + +public class TriggerMapper : TypeMapper { + private readonly M2dReader reader; + + public TriggerMapper(M2dReader reader) { + this.reader = reader; + } + + protected override IEnumerable Map() { + IEnumerable triggers = reader.Files.Where(file => file.Name.StartsWith("trigger/")); + foreach (PackFileEntry file in triggers) { + // get the folder name from the file path after "trigger/" + string[] filePath = file.Name["trigger/".Length..].Split('/'); + string folderName = filePath[0]; + string triggerName = filePath[1].Split(".")[0]; // remove the file extension + string xml = NormalizeTriggerXmlNames(reader.GetXmlDocument(file)); + + var trigger = new TriggerMetadata(folderName, triggerName, xml); + + if (Constant.DebugTriggers) { // for debugging purposes + string filePathName = Path.Combine(Paths.DEBUG_TRIGGERS_DIR, folderName, $"{triggerName}.xml"); + Directory.CreateDirectory(Path.GetDirectoryName(filePathName)!); + + var formattedXml = new XmlDocument(); + formattedXml.LoadXml(xml); + var settings = new XmlWriterSettings { + Indent = true, + NewLineOnAttributes = false, + OmitXmlDeclaration = true, + }; + + using var writer = XmlWriter.Create(filePathName, settings); + formattedXml.Save(writer); + } + yield return trigger; + } + } + + private static readonly Dictionary SubStart = new() { + { "1st", "First" }, + { "2nd", "Second" }, + { "3rd", "Third" }, + { "4th", "Fourth" }, + { "5th", "Fifth" }, + { "6th", "Sixth" }, + { "7th", "Seventh" }, + }; + + public static string NormalizeTriggerXmlNames(XmlDocument xml) { + // check for state nodes with feature attributes and remove disabled ones + List nodesToRemove = new List(); + foreach (XmlNode node in xml.SelectNodes("//state")!) { + XmlAttribute? featureAttr = node.Attributes?["feature"]; + if (featureAttr != null && !FeatureLocaleFilter.FeatureEnabled(featureAttr.Value)) { + nodesToRemove.Add(node); + } + } + + // Check for action nodes with feature attributes and remove disabled ones + foreach (XmlNode node in xml.SelectNodes("//action")!) { + XmlAttribute? featureAttr = node.Attributes?["feature"]; + if (featureAttr != null && !FeatureLocaleFilter.FeatureEnabled(featureAttr.Value)) { + nodesToRemove.Add(node); + } + } + + // Check for condition nodes with feature attributes and remove disabled ones + foreach (XmlNode node in xml.SelectNodes("//condition")!) { + XmlAttribute? featureAttr = node.Attributes?["feature"]; + if (featureAttr != null && !FeatureLocaleFilter.FeatureEnabled(featureAttr.Value)) { + nodesToRemove.Add(node); + } + } + + // Remove disabled feature nodes + foreach (XmlNode node in nodesToRemove) { + node.ParentNode?.RemoveChild(node); + } + + // Continue with the existing normalization logic + foreach (XmlNode node in xml.SelectNodes("//state")!) { + XmlAttribute? attr = node.Attributes?["name"]; + Debug.Assert(attr?.Value != null, "Unable to find name param"); + attr.Value = FixClassName(attr.Value); + } + + foreach (XmlNode node in xml.SelectNodes("//transition")!) { + XmlAttribute? attr = node.Attributes?["state"]; + Debug.Assert(attr?.Value != null, "Unable to find state param"); + attr.Value = FixClassName(attr.Value); + } + + foreach (XmlNode node in xml.SelectNodes("//action")!) { + string actionName = string.Empty; + List nodeParams = []; + foreach (XmlAttribute? attribute in node.Attributes!) { + if (attribute is null) continue; + if (attribute.Name is "name") { + attribute.Value = Translate(attribute.Value, TriggerTranslate.TranslateAction); + actionName = attribute.Value; + continue; + } + + nodeParams.Add(attribute); + } + + if (!TriggerDefinitionOverride.ActionOverride.TryGetValue(actionName, out TriggerDefinitionOverride? overrideValue)) continue; + + if (overrideValue.FunctionSplitter is not null) { + XmlAttribute? attributeSplitter = nodeParams.FirstOrDefault(x => x.Name == overrideValue.FunctionSplitter); + if (attributeSplitter is not null) { + overrideValue.FunctionLookup.TryGetValue(attributeSplitter.Value, out overrideValue); + Debug.Assert(overrideValue is not null, $"Unable to find override for {attributeSplitter.Value}"); + } else { + string? valueDefault = overrideValue.Types.FirstOrDefault().Value; + Debug.Assert(valueDefault is not null, $"Unable to find default value for {overrideValue.Name}"); + overrideValue.FunctionLookup.TryGetValue(valueDefault, out overrideValue); + Debug.Assert(overrideValue is not null, $"Unable to find override for {valueDefault}"); + } + node.Attributes["name"]!.Value = overrideValue.Name; + } + + foreach (XmlAttribute xmlAttribute in nodeParams) { + overrideValue.Names.TryGetValue(TriggerTranslate.ToSnakeCase(xmlAttribute.Name), out string? newName); + if (newName is null) { + if (xmlAttribute.Name != TriggerTranslate.ToSnakeCase(xmlAttribute.Name)) { + newName = TriggerTranslate.ToSnakeCase(xmlAttribute.Name); + } else { + continue; + } + } + + node.Attributes.Remove(xmlAttribute); + XmlAttribute newAttribute = xml.CreateAttribute(newName); + newAttribute.Value = xmlAttribute.Value; + node.Attributes.Append(newAttribute); + } + } + + foreach (XmlNode node in xml.SelectNodes("//condition")!) { + string conditionName = string.Empty; + List nodeParams = []; + foreach (XmlAttribute? attribute in node.Attributes!) { + if (attribute is null) continue; + if (attribute.Name is "name") { + conditionName = attribute.Value; + continue; + } + + nodeParams.Add(attribute); + } + + if (conditionName.StartsWith('!')) { + XmlAttribute negateAttribute = xml.CreateAttribute("negate"); + negateAttribute.Value = "true"; + node.Attributes.Append(negateAttribute); + } + + conditionName = conditionName.TrimStart('!'); + node.Attributes["name"]!.Value = Translate(conditionName, TriggerTranslate.TranslateCondition); + + if (!TriggerDefinitionOverride.ConditionOverride.TryGetValue(node.Attributes["name"]!.Value, out TriggerDefinitionOverride? overrideValue)) continue; + if (overrideValue.Name != node.Attributes["name"]!.Value) { + node.Attributes["name"]!.Value = overrideValue.Name; + } + foreach (XmlAttribute xmlAttribute in nodeParams) { + overrideValue.Names.TryGetValue(TriggerTranslate.ToSnakeCase(xmlAttribute.Name), out string? newName); + if (newName is null) { + if (xmlAttribute.Name != TriggerTranslate.ToSnakeCase(xmlAttribute.Name)) { + newName = TriggerTranslate.ToSnakeCase(xmlAttribute.Name); + } else { + continue; + } + } + + node.Attributes.Remove(xmlAttribute); + XmlAttribute newAttribute = xml.CreateAttribute(newName); + newAttribute.Value = xmlAttribute.Value; + node.Attributes.Append(newAttribute); + } + } + + return xml.OuterXml; + } + + [return: NotNullIfNotNull(nameof(name))] + private static string? FixClassName(string? name) { + if (name == null) { + return null; + } + if (string.IsNullOrWhiteSpace(name)) { + return "State"; + } + + // Reserved Keywords + switch (name) { + case "None": + return "StateNone"; + case "True": + return "StateTrue"; + case "False": + return "StateFalse"; + case "del": + return "StateDelete"; + } + + name = name.Replace("-", "To").Replace(" ", "_").Replace(".", "_"); + foreach ((string key, string value) in SubStart) { + if (name.StartsWith(key)) { + name = name.Replace(key, value); + break; + } + } + + string prefix = ""; + while (name.Length > 0 && !IsLetter(name[0])) { + if (name[0] != '_') { + prefix += name[0]; + } + name = name[1..]; + } + + // name is already valid + if (prefix.Length == 0) { + return name; + } + if (name.Length == 0) { + return $"State{prefix}"; + } + + return !IsLetter(name[^1]) ? $"{name}_{prefix}" : $"{name}{prefix}"; + } + + [return: NotNullIfNotNull(nameof(name))] + private static string? Translate(string? name, Func translator) { + if (name == null) { + return null; + } + + var builder = new StringBuilder(); + foreach (string split in name.Split('_', ' ')) { + builder.Append(translator(split)); + } + + return TriggerTranslate.ToSnakeCase(builder.ToString()); + } +} diff --git a/Maple2.File.Ingest/Mapper/TypeMapper.cs b/Maple2.File.Ingest/Mapper/TypeMapper.cs index 6c98cdb07..0831df041 100644 --- a/Maple2.File.Ingest/Mapper/TypeMapper.cs +++ b/Maple2.File.Ingest/Mapper/TypeMapper.cs @@ -1,41 +1,41 @@ -using System.Diagnostics; -using System.Text; -using System.Text.Json; -using Force.Crc32; - -namespace Maple2.File.Ingest.Mapper; - -public abstract class TypeMapper where T : class { - private readonly Stopwatch stopwatch; - private readonly List results; - - public bool Complete { get; private set; } - - public IReadOnlyCollection Results => Complete ? results : Array.Empty(); - - public long ElapsedMilliseconds => stopwatch.ElapsedMilliseconds; - - protected TypeMapper() { - stopwatch = new Stopwatch(); - results = []; - } - - public uint Process() { - if (Complete) { - throw new InvalidOperationException($"{typeof(T)} has already been mapped."); - } - - uint crc32C = 0; - stopwatch.Start(); - foreach (T result in Map()) { - crc32C = Crc32CAlgorithm.Append(crc32C, Encoding.UTF8.GetBytes(JsonSerializer.Serialize(result))); - results.Add(result); - } - stopwatch.Stop(); - Complete = true; - - return crc32C; - } - - protected abstract IEnumerable Map(); -} +using System.Diagnostics; +using System.Text; +using System.Text.Json; +using Force.Crc32; + +namespace Maple2.File.Ingest.Mapper; + +public abstract class TypeMapper where T : class { + private readonly Stopwatch stopwatch; + private readonly List results; + + public bool Complete { get; private set; } + + public IReadOnlyCollection Results => Complete ? results : Array.Empty(); + + public long ElapsedMilliseconds => stopwatch.ElapsedMilliseconds; + + protected TypeMapper() { + stopwatch = new Stopwatch(); + results = []; + } + + public uint Process() { + if (Complete) { + throw new InvalidOperationException($"{typeof(T)} has already been mapped."); + } + + uint crc32C = 0; + stopwatch.Start(); + foreach (T result in Map()) { + crc32C = Crc32CAlgorithm.Append(crc32C, Encoding.UTF8.GetBytes(JsonSerializer.Serialize(result))); + results.Add(result); + } + stopwatch.Stop(); + Complete = true; + + return crc32C; + } + + protected abstract IEnumerable Map(); +} diff --git a/Maple2.File.Ingest/Mapper/UgcMapMapper.cs b/Maple2.File.Ingest/Mapper/UgcMapMapper.cs index dba2741f1..a2208067c 100644 --- a/Maple2.File.Ingest/Mapper/UgcMapMapper.cs +++ b/Maple2.File.Ingest/Mapper/UgcMapMapper.cs @@ -1,69 +1,69 @@ -using Maple2.File.IO; -using Maple2.File.Parser; -using Maple2.File.Parser.Xml; -using Maple2.Model.Common; -using Maple2.Model.Metadata; - -namespace Maple2.File.Ingest.Mapper; - -public class UgcMapMapper : TypeMapper { - private readonly UgcMapParser parser; - - public UgcMapMapper(M2dReader xmlReader) { - parser = new UgcMapParser(xmlReader); - } - - protected override IEnumerable Map() { - foreach ((int id, UgcMap data) in parser.Parse()) { - yield return new UgcMapMetadata( - Id: id, - Plots: data.group.ToDictionary( - group => group.no, - group => new UgcMapGroup( - Number: group.no, - ApartmentNumber: 0, - Type: group.builingType, - ContractCost: new UgcMapGroup.Cost( - Amount: group.contractPrice, - ItemId: group.contractPriceItemCode, - Days: group.ugcHomeContractDate), - ExtensionCost: new UgcMapGroup.Cost( - Amount: group.extensionPrice, - ItemId: group.extensionPriceItemCode, - Days: group.ugcHomeExtensionDate), - Limit: new UgcMapGroup.Limits( - Height: group.heightLimit, - Area: group.area, - Maid: group.maidCount, - Trigger: group.triggerCount, - InstallNpc: group.installNpcCount, - InstallBuilding: group.installableBuildingCount) - ) - ) - ); - } - } -} - -public class ExportedUgcMapMapper : TypeMapper { - private readonly UgcMapParser parser; - - public ExportedUgcMapMapper(M2dReader xmlReader) { - parser = new UgcMapParser(xmlReader); - } - - protected override IEnumerable Map() { - foreach ((string id, ExportedUgcMap data) in parser.ParseExported()) { - yield return new ExportedUgcMapMetadata( - Id: id, - BaseCubePosition: new Vector3B(data.baseCubePoint3[0], data.baseCubePoint3[1], data.baseCubePoint3[2]), - IndoorSize: data.indoorSizeType.Select(x => (byte) x).ToArray(), - Cubes: data.cube.Select(x => - new ExportedUgcMapMetadata.Cube(ItemId: x.itemID, - OffsetPosition: new Vector3B(x.offsetCubePoint3[0], x.offsetCubePoint3[1], x.offsetCubePoint3[2]), - Rotation: x.rotation, - WallDirection: (byte) x.wallDir)).ToList() - ); - } - } -} +using Maple2.File.IO; +using Maple2.File.Parser; +using Maple2.File.Parser.Xml; +using Maple2.Model.Common; +using Maple2.Model.Metadata; + +namespace Maple2.File.Ingest.Mapper; + +public class UgcMapMapper : TypeMapper { + private readonly UgcMapParser parser; + + public UgcMapMapper(M2dReader xmlReader) { + parser = new UgcMapParser(xmlReader); + } + + protected override IEnumerable Map() { + foreach ((int id, UgcMap data) in parser.Parse()) { + yield return new UgcMapMetadata( + Id: id, + Plots: data.group.ToDictionary( + group => group.no, + group => new UgcMapGroup( + Number: group.no, + ApartmentNumber: 0, + Type: group.builingType, + ContractCost: new UgcMapGroup.Cost( + Amount: group.contractPrice, + ItemId: group.contractPriceItemCode, + Days: group.ugcHomeContractDate), + ExtensionCost: new UgcMapGroup.Cost( + Amount: group.extensionPrice, + ItemId: group.extensionPriceItemCode, + Days: group.ugcHomeExtensionDate), + Limit: new UgcMapGroup.Limits( + Height: group.heightLimit, + Area: group.area, + Maid: group.maidCount, + Trigger: group.triggerCount, + InstallNpc: group.installNpcCount, + InstallBuilding: group.installableBuildingCount) + ) + ) + ); + } + } +} + +public class ExportedUgcMapMapper : TypeMapper { + private readonly UgcMapParser parser; + + public ExportedUgcMapMapper(M2dReader xmlReader) { + parser = new UgcMapParser(xmlReader); + } + + protected override IEnumerable Map() { + foreach ((string id, ExportedUgcMap data) in parser.ParseExported()) { + yield return new ExportedUgcMapMetadata( + Id: id, + BaseCubePosition: new Vector3B(data.baseCubePoint3[0], data.baseCubePoint3[1], data.baseCubePoint3[2]), + IndoorSize: data.indoorSizeType.Select(x => (byte) x).ToArray(), + Cubes: data.cube.Select(x => + new ExportedUgcMapMetadata.Cube(ItemId: x.itemID, + OffsetPosition: new Vector3B(x.offsetCubePoint3[0], x.offsetCubePoint3[1], x.offsetCubePoint3[2]), + Rotation: x.rotation, + WallDirection: (byte) x.wallDir)).ToList() + ); + } + } +} diff --git a/Maple2.File.Ingest/MapperExtensions.cs b/Maple2.File.Ingest/MapperExtensions.cs index 026da63e1..027905c7f 100644 --- a/Maple2.File.Ingest/MapperExtensions.cs +++ b/Maple2.File.Ingest/MapperExtensions.cs @@ -1,538 +1,538 @@ -using System.ComponentModel; -using System.Diagnostics; -using System.Reflection; -using Maple2.File.Ingest.Utils; -using Maple2.File.Parser.Xml; -using Maple2.File.Parser.Xml.Common; -using Maple2.File.Parser.Xml.Skill; -using Maple2.Model.Enum; -using Maple2.Model.Game; -using Maple2.Model.Metadata; -using BeginCondition = Maple2.Model.Metadata.BeginCondition; -using ItemOption = Maple2.Model.Metadata.ItemOption; - -namespace Maple2.File.Ingest; - -public static class MapperExtensions { - public static Dictionary ToDictionary(this StatValue values) { - var result = new Dictionary(); - foreach (BasicAttribute attribute in Enum.GetValues()) { - long value = values[(byte) attribute]; - if (value != default) { - result[attribute] = value; - } - } - - return result; - } - - public static Dictionary ToDictionary(this StatRate rates) { - var result = new Dictionary(); - foreach (BasicAttribute attribute in Enum.GetValues()) { - float rate = rates[(byte) attribute]; - if (rate != default) { - result[attribute] = rate; - } - } - - return result; - } - - public static byte OptionIndex(this SpecialAttribute attribute) { - return attribute switch { - SpecialAttribute.Experience => 0, - SpecialAttribute.Meso => 1, - SpecialAttribute.SwimSpeed => 2, - SpecialAttribute.DashDistance => 3, - // MoveSpeed = 4 - // "sid" = 5? - SpecialAttribute.TotalDamage => 6, - SpecialAttribute.CriticalDamage => 7, - SpecialAttribute.NormalNpcDamage => 8, - SpecialAttribute.LeaderNpcDamage => 9, - SpecialAttribute.EliteNpcDamage => 10, - SpecialAttribute.BossNpcDamage => 11, - SpecialAttribute.HpOnKill => 12, - SpecialAttribute.SpiritOnKill => 13, - SpecialAttribute.StaminaOnKill => 14, - SpecialAttribute.RecoveryBonus => 15, - SpecialAttribute.BonusRecoveryFromAlly => 16, - SpecialAttribute.IceDamage => 17, - SpecialAttribute.FireDamage => 18, - SpecialAttribute.DarkDamage => 19, - SpecialAttribute.HolyDamage => 20, - SpecialAttribute.PoisonDamage => 21, - SpecialAttribute.ElectricDamage => 22, - SpecialAttribute.MeleeDamage => 23, - SpecialAttribute.RangedDamage => 24, - SpecialAttribute.PhysicalPiercing => 25, - SpecialAttribute.MagicalPiercing => 26, - SpecialAttribute.ReduceIceDamage => 27, - SpecialAttribute.ReduceFireDamage => 28, - SpecialAttribute.ReduceDarkDamage => 29, - SpecialAttribute.ReduceHolyDamage => 30, - SpecialAttribute.ReducePoisonDamage => 31, - SpecialAttribute.ReduceElectricDamage => 32, - SpecialAttribute.ReduceStun => 33, - SpecialAttribute.ReduceDebuff => 34, - SpecialAttribute.ReduceCooldown => 35, - SpecialAttribute.ReduceMeleeDamage => 36, - SpecialAttribute.ReduceRangedDamage => 37, - SpecialAttribute.ReduceKnockBack => 38, - SpecialAttribute.MeleeStun => 39, - SpecialAttribute.RangedStun => 40, - SpecialAttribute.MeleeKnockBack => 41, - SpecialAttribute.RangedKnockBack => 42, - SpecialAttribute.MeleeImmobilize => 43, - SpecialAttribute.RangedImmobilize => 44, - SpecialAttribute.MeleeSplashDamage => 45, - SpecialAttribute.RangedSplashDamage => 46, - SpecialAttribute.DropRate => 47, - SpecialAttribute.QuestExp => 48, - SpecialAttribute.QuestMeso => 49, - SpecialAttribute.FishingExp => 50, - SpecialAttribute.ArcadeExp => 51, - SpecialAttribute.PlayInstrumentExp => 52, - SpecialAttribute.InvokeEffect1 => 53, - SpecialAttribute.InvokeEffect2 => 54, - SpecialAttribute.InvokeEffect3 => 55, - SpecialAttribute.PvpDamage => 56, - SpecialAttribute.ReducePvpDamage => 57, - SpecialAttribute.GuildExp => 58, - SpecialAttribute.GuildCoin => 59, - SpecialAttribute.MassiveEventExpBall => 60, - SpecialAttribute.ReduceMesoTradeFee => 61, - SpecialAttribute.ReduceEnchantMaterialFee => 62, - SpecialAttribute.ReduceMeretRevivalFee => 63, - SpecialAttribute.MiningRewardItem => 64, - SpecialAttribute.BreedingRewardItem => 65, - SpecialAttribute.SmithingRewardMastery => 66, - SpecialAttribute.EngravingRewardMastery => 67, - SpecialAttribute.GatheringRewardItem => 68, - SpecialAttribute.FarmingRewardItem => 69, - SpecialAttribute.AlchemistRewardMastery => 70, - SpecialAttribute.CookingRewardMastery => 71, - SpecialAttribute.AcquireGatheringExp => 72, - SpecialAttribute.SkillLevelUpTier1 => 73, - SpecialAttribute.SkillLevelUpTier2 => 74, - SpecialAttribute.SkillLevelUpTier3 => 75, - SpecialAttribute.SkillLevelUpTier4 => 76, - SpecialAttribute.SkillLevelUpTier5 => 77, - SpecialAttribute.SkillLevelUpTier6 => 78, - SpecialAttribute.SkillLevelUpTier7 => 79, - SpecialAttribute.SkillLevelUpTier8 => 80, - SpecialAttribute.SkillLevelUpTier9 => 81, - SpecialAttribute.SkillLevelUpTier10 => 82, - SpecialAttribute.SkillLevelUpTier11 => 83, - SpecialAttribute.SkillLevelUpTier12 => 84, - SpecialAttribute.SkillLevelUpTier13 => 85, - SpecialAttribute.SkillLevelUpTier14 => 86, - SpecialAttribute.MassiveOxExp => 87, - SpecialAttribute.MassiveTrapMasterExp => 88, - SpecialAttribute.MassiveFinalSurvivalExp => 89, - SpecialAttribute.MassiveCrazyRunnerExp => 90, - SpecialAttribute.MassiveShCrazyRunnerExp => 91, - SpecialAttribute.MassiveEscapeExp => 92, - SpecialAttribute.MassiveSpringBeachExp => 93, - SpecialAttribute.MassiveDanceDanceExp => 94, - SpecialAttribute.MassiveOxSpeed => 95, - SpecialAttribute.MassiveTrapMasterSpeed => 96, - SpecialAttribute.MassiveFinalSurvivalSpeed => 97, - SpecialAttribute.MassiveCrazyRunnerSpeed => 98, - SpecialAttribute.MassiveShCrazyRunnerSpeed => 99, - SpecialAttribute.MassiveEscapeSpeed => 100, - SpecialAttribute.MassiveSpringBeachSpeed => 101, - SpecialAttribute.MassiveDanceDanceSpeed => 102, - SpecialAttribute.NpcHitRewardSpBall => 103, - SpecialAttribute.NpcHitRewardEpBall => 104, - SpecialAttribute.HonorToken => 105, - SpecialAttribute.PvpExp => 106, - SpecialAttribute.DarkStreamDamage => 107, - SpecialAttribute.ReduceDarkStreamReceiveDamage => 108, - SpecialAttribute.DarkStreamEvp => 109, - SpecialAttribute.FishingDoubleMastery => 110, - SpecialAttribute.PlayInstrumentDoubleMastery => 111, - SpecialAttribute.CompleteFieldMissionSpeed => 112, - SpecialAttribute.GlideVerticalVelocity => 113, - SpecialAttribute.AdditionalEffect95000018 => 114, - SpecialAttribute.AdditionalEffect95000012 => 115, - SpecialAttribute.AdditionalEffect95000014 => 116, - SpecialAttribute.AdditionalEffect95000020 => 117, - SpecialAttribute.AdditionalEffect95000021 => 118, - SpecialAttribute.AdditionalEffect95000022 => 119, - SpecialAttribute.AdditionalEffect95000023 => 120, - SpecialAttribute.AdditionalEffect95000024 => 121, - SpecialAttribute.AdditionalEffect95000025 => 122, - SpecialAttribute.AdditionalEffect95000026 => 123, - SpecialAttribute.AdditionalEffect95000027 => 124, - SpecialAttribute.AdditionalEffect95000028 => 125, - SpecialAttribute.AdditionalEffect95000029 => 126, - SpecialAttribute.ReduceRecoveryEpInv => 127, - SpecialAttribute.MaxWeaponAttack => 128, - SpecialAttribute.MiningDoubleReward => 129, - SpecialAttribute.BreedingDoubleReward => 130, - SpecialAttribute.GatheringDoubleReward => 131, - SpecialAttribute.FarmingDoubleReward => 132, - SpecialAttribute.SmithingDoubleReward => 133, - SpecialAttribute.EngravingDoubleReward => 134, - SpecialAttribute.AlchemistDoubleReward => 135, - SpecialAttribute.CookingDoubleReward => 136, - SpecialAttribute.MiningDoubleMastery => 137, - SpecialAttribute.BreedingDoubleMastery => 138, - SpecialAttribute.GatheringDoubleMastery => 139, - SpecialAttribute.FarmingDoubleMastery => 140, - SpecialAttribute.SmithingDoubleMastery => 141, - SpecialAttribute.EngravingDoubleMastery => 142, - SpecialAttribute.AlchemistDoubleMastery => 143, - SpecialAttribute.CookingDoubleMastery => 144, - SpecialAttribute.ChaosRaidAttack => 145, - SpecialAttribute.ChaosRaidAttackSpeed => 146, - SpecialAttribute.ChaosRaidAccuracy => 147, - SpecialAttribute.ChaosRaidHp => 148, - SpecialAttribute.RecoveryBall => 149, - SpecialAttribute.FieldBossExp => 150, - SpecialAttribute.FieldBossDropRate => 151, - SpecialAttribute.ReduceFieldBossReceiveDamage => 152, - SpecialAttribute.AdditionalEffect95000016 => 153, - SpecialAttribute.PetTrapReward => 154, - SpecialAttribute.MiningEfficiency => 155, - SpecialAttribute.BreedingEfficiency => 156, - SpecialAttribute.GatheringEfficiency => 157, - SpecialAttribute.FarmingEfficiency => 158, - SpecialAttribute.ReduceDamageByTargetMaxHp => 159, - SpecialAttribute.ReduceMesoRevivalFee => 160, - SpecialAttribute.RidingRunSpeed => 161, - SpecialAttribute.DungeonRewardMeso => 162, - SpecialAttribute.ShopBuyingMeso => 163, - SpecialAttribute.ItemBoxRewardMeso => 164, - SpecialAttribute.ReduceRemakeOptionFee => 165, - SpecialAttribute.ReduceAirTaxiFee => 166, - SpecialAttribute.SocketUnlockProbability => 167, - SpecialAttribute.ReduceGemstoneUpgradeFee => 168, - SpecialAttribute.ReducePetRemakeOptionFee => 169, - SpecialAttribute.RidingSpeed => 170, - // SurvivalKillExp = 171 - // SurvivalTimeExp = 172 - // PhysicalDamage = 173 - // MagicalDamage = 174 - SpecialAttribute.ReduceGameItemSocketUnlockFee => 175, - - // Not mappable - // SpecialAttribute.TonicDropRate => 4, - // SpecialAttribute.GearDropRate => 5, - // SpecialAttribute.MaidExp => 62, - // SpecialAttribute.ReduceMaidRecipe => 63, - // SpecialAttribute.AcquireManufacturingExp => 76, - _ => byte.MaxValue, - }; - } - - public static SkillEffectMetadata Convert(this TriggerSkill trigger) { - SkillEffectMetadataCondition? condition = null; - SkillEffectMetadataSplash? splash = null; - if (trigger.splash) { - splash = new SkillEffectMetadataSplash( - Interval: trigger.interval, - Delay: trigger.delay > int.MaxValue ? int.MaxValue : (int) trigger.delay, - RemoveDelay: trigger.removeDelay, - UseDirection: trigger.useDirection, - ImmediateActive: trigger.immediateActive, - NonTargetActive: trigger.nonTargetActive, - OnlySensingActive: trigger.onlySensingActive, - DependOnCasterState: trigger.dependOnCasterState, - Independent: trigger.independent, - Chain: trigger.chain ? new SkillEffectMetadataChain(trigger.chainDistance) : null); - } else { - var owner = SkillTargetType.Owner; - if (trigger.skillOwner > 0 && Enum.IsDefined((SkillTargetType) trigger.skillOwner)) { - owner = (SkillTargetType) trigger.skillOwner; - } - condition = new SkillEffectMetadataCondition( - Condition: trigger.beginCondition.Convert(), - Owner: owner, - Target: (SkillTargetType) trigger.skillTarget, - OverlapCount: trigger.overlapCount, - RandomCast: trigger.randomCast); - } - - SkillEffectMetadata.Skill[] skills; - if (trigger.linkSkillID.Length > 0) { - skills = trigger.skillID - .Zip(trigger.level, (skillId, level) => new { skillId, level }) - .Zip(trigger.linkSkillID, (skill, linkSkillId) => new SkillEffectMetadata.Skill(skill.skillId, skill.level, linkSkillId)) - .ToArray(); - } else { - skills = trigger.skillID - .Zip(trigger.level, (skillId, level) => new SkillEffectMetadata.Skill(skillId, level)) - .ToArray(); - } - - return new SkillEffectMetadata( - FireCount: trigger.fireCount, - Skills: skills, - Condition: condition, - Splash: splash); - } - - public static IReadOnlyDictionary CollectAttackPoints(this SkillMotionData motion) { - var attackPoints = new Dictionary(); - - for (byte i = 0; i < motion.attack.Count; ++i) { - if (!attackPoints.ContainsKey(motion.attack[i].point)) { - attackPoints.Add(motion.attack[i].point, i); - - continue; - } - - attackPoints[motion.attack[i].point] = 0xFF; // multiple points have the name, cannot use look up table - } - - return attackPoints; - } - - public static SkillMetadataChange Convert(this ChangeSkill change) { - return new SkillMetadataChange( - Origin: new SkillMetadataChange.Skill( - Id: change.originSkillID, - Level: change.originSkillLevel), - Effects: change.changeSkillCheckEffectID - .Zip(change.changeSkillCheckEffectLevel, (effectId, effectLevel) => new { - effectId, - effectLevel, - }) - .Zip(change.changeSkillCheckEffectOverlapCount, (effect, overlapCount) => new SkillMetadataChange.Effect(effect.effectId, effect.effectLevel, overlapCount)) - .ToArray(), - Skills: change.changeSkillID - .Zip(change.changeSkillLevel, (skillId, level) => new SkillMetadataChange.Skill(skillId, level)) - .ToArray() - ); - } - - public static SkillMetadataAutoTargeting Convert(this AutoTargeting autoTargeting) { - return new SkillMetadataAutoTargeting( - MaxDegree: autoTargeting.autoTargetingMaxDegree, - MaxDistance: autoTargeting.autoTargetingMaxDistance, - MaxHeight: autoTargeting.autoTargetingMaxHeight, - UseMove: autoTargeting.autoTargetUseMove); - } - - public static BeginCondition Convert(this Maple2.File.Parser.Xml.Skill.BeginCondition beginCondition) { - return new BeginCondition( - Level: beginCondition.level, - Gender: (Gender) beginCondition.gender, - Mesos: beginCondition.money, - Stat: beginCondition.stat.ToDictionary(), - Maps: beginCondition.requireMapCodes.Select(mapCodes => mapCodes.code).ToArray(), - MapTypes: beginCondition.requireMapCategoryCodes.Select(mapType => (MapType) mapType.code).ToArray(), - Continents: beginCondition.requireMapContinentCodes.Select(continent => (Continent) continent.code).ToArray(), - ActiveSkill: beginCondition.requireSkillCodes.Select(skill => skill.code).ToArray(), - JobCode: beginCondition.job.Select(job => (JobCode) job.code).ToArray(), - Probability: beginCondition.probability, - CooldownTime: beginCondition.cooldownTime, - DurationWithoutMoving: (int) TimeSpan.FromSeconds(beginCondition.requireDurationWithoutMove).TotalMilliseconds, - DurationWithoutDamage: (int) TimeSpan.FromSeconds(beginCondition.requireDurationWithoutDamage).TotalMilliseconds, - OnlyShadowWorld: beginCondition.onlyShadowWorld || beginCondition.isShadowWorld, - OnlyFlyableMap: beginCondition.onlyFlyableMap, - AllowDead: beginCondition.allowDeadState, - AllowOnBattleMount: beginCondition.allowBattleRidingState, - OnlyOnBattleMount: beginCondition.onlyBattleRidingState, - AllowOnSurvival: beginCondition.allowMapleSurvival, - DungeonGroupType: beginCondition.requireDungeonRoomGroupTypes - .Where(type => Enum.TryParse(type.type, true, out DungeonGroupType _)) - .Select(type => Enum.Parse(type.type, true)) - .ToArray(), - Weapon: beginCondition.weapon.Select(weapon => new BeginConditionWeapon( - new ItemType(1, (byte) weapon.lh), - new ItemType(1, (byte) weapon.rh))).ToArray(), - Target: Convert(beginCondition.skillTarget), - Owner: Convert(beginCondition.skillOwner), - Caster: Convert(beginCondition.skillCaster)); - } - - // We use this default to avoid writing useless checks - private static readonly BeginConditionTarget DefaultBeginConditionTarget = new([], new BeginConditionTarget.EventCondition(EventConditionType.Activate, false, [], []), [], [], [], new Dictionary(), [], []); - private static BeginConditionTarget? Convert(SubConditionTarget? target) { - if (target == null) { - return null; - } - - var result = new BeginConditionTarget( - Buff: ParseBuffs(target), - Event: ParseEvent(target), - Stat: ParseStat(target), - States: target.requireStates - .Select(state => Enum.GetValues() - .FirstOrDefault(enumValue => - enumValue.GetType() - .GetField(enumValue.ToString()) - ?.GetCustomAttribute() - ?.Description == state)) - .Where(state => state != ActorState.None) - .ToArray(), - SubStates: target.requireSubStates - .Select(state => Enum.GetValues() - .FirstOrDefault(enumValue => - enumValue.GetType() - .GetField(enumValue.ToString()) - ?.GetCustomAttribute() - ?.Description == state)) - .Where(state => state != ActorSubState.None) - .ToArray(), - Masteries: target.requireMasteryTypes - .Where(type => Enum.TryParse(type, true, out MasteryType _)) - .Select(type => Enum.Parse(type, true)) - .Zip(target.requireMasteryValues, (type, value) => (Type: type, Value: value)) - .ToDictionary(pair => pair.Type, pair => pair.Value), - NpcIds: target.NpcIDs, - HasNotBuffIds: target.hasNotBuffID); - - return DefaultBeginConditionTarget.Equals(result) ? null : result; - - BeginConditionTarget.HasBuff[] ParseBuffs(SubConditionTarget data) { - if (data.hasBuffID.Length == 0 || data.hasBuffID[0] == 0) { - return []; - } - - var hasBuff = new BeginConditionTarget.HasBuff[data.hasBuffID.Length]; - for (int i = 0; i < hasBuff.Length; i++) { - hasBuff[i] = new BeginConditionTarget.HasBuff( - Id: data.hasBuffID[i], - Level: data.hasBuffLevel.Length > i ? data.hasBuffLevel[i] : (short) 0, - Owned: data.hasBuffOwner.Length > i && data.hasBuffOwner[i], - Count: data.hasBuffCount.Length > i ? data.hasBuffCount[i] : 0, - Compare: data.hasBuffCountCompare.Length > i ? Enum.Parse(data.hasBuffCountCompare[i]) : CompareType.Equals); - } - - return hasBuff; - } - - // Seems to only be used for test skills. - // BeginConditionTarget.HasSkill? ParseSkill(SubConditionTarget data) { - // return data.hasSkillID > 0 ? new BeginConditionTarget.HasSkill(data.hasSkillID, data.hasSkillLevel) : null; - // } - - BeginConditionTarget.EventCondition ParseEvent(SubConditionTarget data) { - return new BeginConditionTarget.EventCondition( - Type: (EventConditionType) data.eventCondition, - IgnoreOwner: data.ignoreOwnerEvent != 0, - SkillIds: data.eventSkillID, - BuffIds: data.eventEffectID); - } - - BeginConditionTarget.BeginConditionStat[] ParseStat(SubConditionTarget data) { - if (data.compareStat.Count == 0) { - return []; - } - - var stats = new BeginConditionTarget.BeginConditionStat[data.compareStat.Count]; - for (int i = 0; i < stats.Length; i++) { - foreach (BasicAttribute attribute in Enum.GetValues()) { - float value = data.compareStat[i][(byte) attribute]; - if (value != default) { - stats[i] = new BeginConditionTarget.BeginConditionStat( - Attribute: attribute, - Value: value, - Compare: data.compareStat.Count > i ? Enum.Parse(data.compareStat[i].func) : CompareType.Equals, - ValueType: (CompareStatValueType) data.compareStat[i].type); - break; - } - } - } - return stats; - } - } - - public static Dictionary> ToDictionary(this IEnumerable entries) { - var results = new Dictionary>(); - foreach (ItemOptionData entry in entries) { - var optionEntries = new List(); - foreach (BasicAttribute attribute in Enum.GetValues()) { - int[] value = entry.StatValue((byte) attribute); - if (value.Length > 0) { - Debug.Assert(value.Length is 1 or 2); - var valueRange = new ItemOption.Range(value[0], value.Length > 1 ? value[1] : value[0]); - optionEntries.Add(new ItemOption.Entry(BasicAttribute: attribute, Values: valueRange)); - } - float[] rate = entry.StatRate((byte) attribute); - if (rate.Length > 0) { - Debug.Assert(rate.Length is 1 or 2); - var rateRange = new ItemOption.Range(rate[0], rate.Length > 1 ? rate[1] : rate[0]); - optionEntries.Add(new ItemOption.Entry(BasicAttribute: attribute, Rates: rateRange)); - } - } - - foreach (SpecialAttribute attribute in Enum.GetValues()) { - byte index = attribute.OptionIndex(); - if (index == byte.MaxValue) continue; - - SpecialAttribute fixAttribute = attribute.SgiTarget(entry.sgi_target); - int[] value = entry.SpecialValue(index); - if (value.Length > 0) { - Debug.Assert(value.Length is 1 or 2); - var valueRange = new ItemOption.Range(value[0], value.Length > 1 ? value[1] : value[0]); - optionEntries.Add(new ItemOption.Entry(SpecialAttribute: fixAttribute, Values: valueRange)); - } - float[] rate = entry.SpecialRate(index); - if (rate.Length > 0) { - Debug.Assert(rate.Length is 1 or 2); - var rateRange = new ItemOption.Range(rate[0], rate.Length > 1 ? rate[1] : rate[0]); - optionEntries.Add(new ItemOption.Entry(SpecialAttribute: fixAttribute, Rates: rateRange)); - } - } - - if (!results.ContainsKey(entry.code)) { - results[entry.code] = new Dictionary(); - } - - // these entries are useless because they cannot be used. - if (entry.optionNumPick.Length == 0 || (entry.optionNumPick[0] == 0 && entry.optionNumPick[1] == 0)) { - continue; - } - - var option = new ItemOption( - MultiplyFactor: entry.multiply_factor == 0 ? 1 : entry.multiply_factor, - NumPick: new ItemOption.Range(entry.optionNumPick[0], entry.optionNumPick[1]), - Entries: optionEntries.ToArray()); - if (results[entry.code].ContainsKey(entry.grade)) { - Console.WriteLine($"{entry.code} already has grade {entry.grade}"); - } - - (results[entry.code] as Dictionary)!.Add(entry.grade, option); - } - - return results; - } - - public static ConditionMetadata.Parameters? ConvertCodes(this string[] codes) { - if (codes.Length == 0) { - return null; - } - - if (codes.Length > 1) { - var integers = new List(); - var strings = new List(); - foreach (string code in codes) { - if (int.TryParse(code, out int intCode)) { - integers.Add(intCode); - } else { - strings.Add(code); - } - } - - return new ConditionMetadata.Parameters( - Strings: strings.Count == 0 ? null : strings.ToArray(), - Integers: integers.Count == 0 ? null : integers.ToArray()); - } - - string[] split = codes[0].Split('-'); - if (split.Length > 1) { - return new ConditionMetadata.Parameters( - Range: new ConditionMetadata.Range(int.Parse(split[0]), int.Parse(split[1]))); - } - - if (int.TryParse(codes[0], out int integerResult)) { - return new ConditionMetadata.Parameters(Integers: [integerResult]); - } - return new ConditionMetadata.Parameters(Strings: [codes[0]]); - } -} +using System.ComponentModel; +using System.Diagnostics; +using System.Reflection; +using Maple2.File.Ingest.Utils; +using Maple2.File.Parser.Xml; +using Maple2.File.Parser.Xml.Common; +using Maple2.File.Parser.Xml.Skill; +using Maple2.Model.Enum; +using Maple2.Model.Game; +using Maple2.Model.Metadata; +using BeginCondition = Maple2.Model.Metadata.BeginCondition; +using ItemOption = Maple2.Model.Metadata.ItemOption; + +namespace Maple2.File.Ingest; + +public static class MapperExtensions { + public static Dictionary ToDictionary(this StatValue values) { + var result = new Dictionary(); + foreach (BasicAttribute attribute in Enum.GetValues()) { + long value = values[(byte) attribute]; + if (value != default) { + result[attribute] = value; + } + } + + return result; + } + + public static Dictionary ToDictionary(this StatRate rates) { + var result = new Dictionary(); + foreach (BasicAttribute attribute in Enum.GetValues()) { + float rate = rates[(byte) attribute]; + if (rate != default) { + result[attribute] = rate; + } + } + + return result; + } + + public static byte OptionIndex(this SpecialAttribute attribute) { + return attribute switch { + SpecialAttribute.Experience => 0, + SpecialAttribute.Meso => 1, + SpecialAttribute.SwimSpeed => 2, + SpecialAttribute.DashDistance => 3, + // MoveSpeed = 4 + // "sid" = 5? + SpecialAttribute.TotalDamage => 6, + SpecialAttribute.CriticalDamage => 7, + SpecialAttribute.NormalNpcDamage => 8, + SpecialAttribute.LeaderNpcDamage => 9, + SpecialAttribute.EliteNpcDamage => 10, + SpecialAttribute.BossNpcDamage => 11, + SpecialAttribute.HpOnKill => 12, + SpecialAttribute.SpiritOnKill => 13, + SpecialAttribute.StaminaOnKill => 14, + SpecialAttribute.RecoveryBonus => 15, + SpecialAttribute.BonusRecoveryFromAlly => 16, + SpecialAttribute.IceDamage => 17, + SpecialAttribute.FireDamage => 18, + SpecialAttribute.DarkDamage => 19, + SpecialAttribute.HolyDamage => 20, + SpecialAttribute.PoisonDamage => 21, + SpecialAttribute.ElectricDamage => 22, + SpecialAttribute.MeleeDamage => 23, + SpecialAttribute.RangedDamage => 24, + SpecialAttribute.PhysicalPiercing => 25, + SpecialAttribute.MagicalPiercing => 26, + SpecialAttribute.ReduceIceDamage => 27, + SpecialAttribute.ReduceFireDamage => 28, + SpecialAttribute.ReduceDarkDamage => 29, + SpecialAttribute.ReduceHolyDamage => 30, + SpecialAttribute.ReducePoisonDamage => 31, + SpecialAttribute.ReduceElectricDamage => 32, + SpecialAttribute.ReduceStun => 33, + SpecialAttribute.ReduceDebuff => 34, + SpecialAttribute.ReduceCooldown => 35, + SpecialAttribute.ReduceMeleeDamage => 36, + SpecialAttribute.ReduceRangedDamage => 37, + SpecialAttribute.ReduceKnockBack => 38, + SpecialAttribute.MeleeStun => 39, + SpecialAttribute.RangedStun => 40, + SpecialAttribute.MeleeKnockBack => 41, + SpecialAttribute.RangedKnockBack => 42, + SpecialAttribute.MeleeImmobilize => 43, + SpecialAttribute.RangedImmobilize => 44, + SpecialAttribute.MeleeSplashDamage => 45, + SpecialAttribute.RangedSplashDamage => 46, + SpecialAttribute.DropRate => 47, + SpecialAttribute.QuestExp => 48, + SpecialAttribute.QuestMeso => 49, + SpecialAttribute.FishingExp => 50, + SpecialAttribute.ArcadeExp => 51, + SpecialAttribute.PlayInstrumentExp => 52, + SpecialAttribute.InvokeEffect1 => 53, + SpecialAttribute.InvokeEffect2 => 54, + SpecialAttribute.InvokeEffect3 => 55, + SpecialAttribute.PvpDamage => 56, + SpecialAttribute.ReducePvpDamage => 57, + SpecialAttribute.GuildExp => 58, + SpecialAttribute.GuildCoin => 59, + SpecialAttribute.MassiveEventExpBall => 60, + SpecialAttribute.ReduceMesoTradeFee => 61, + SpecialAttribute.ReduceEnchantMaterialFee => 62, + SpecialAttribute.ReduceMeretRevivalFee => 63, + SpecialAttribute.MiningRewardItem => 64, + SpecialAttribute.BreedingRewardItem => 65, + SpecialAttribute.SmithingRewardMastery => 66, + SpecialAttribute.EngravingRewardMastery => 67, + SpecialAttribute.GatheringRewardItem => 68, + SpecialAttribute.FarmingRewardItem => 69, + SpecialAttribute.AlchemistRewardMastery => 70, + SpecialAttribute.CookingRewardMastery => 71, + SpecialAttribute.AcquireGatheringExp => 72, + SpecialAttribute.SkillLevelUpTier1 => 73, + SpecialAttribute.SkillLevelUpTier2 => 74, + SpecialAttribute.SkillLevelUpTier3 => 75, + SpecialAttribute.SkillLevelUpTier4 => 76, + SpecialAttribute.SkillLevelUpTier5 => 77, + SpecialAttribute.SkillLevelUpTier6 => 78, + SpecialAttribute.SkillLevelUpTier7 => 79, + SpecialAttribute.SkillLevelUpTier8 => 80, + SpecialAttribute.SkillLevelUpTier9 => 81, + SpecialAttribute.SkillLevelUpTier10 => 82, + SpecialAttribute.SkillLevelUpTier11 => 83, + SpecialAttribute.SkillLevelUpTier12 => 84, + SpecialAttribute.SkillLevelUpTier13 => 85, + SpecialAttribute.SkillLevelUpTier14 => 86, + SpecialAttribute.MassiveOxExp => 87, + SpecialAttribute.MassiveTrapMasterExp => 88, + SpecialAttribute.MassiveFinalSurvivalExp => 89, + SpecialAttribute.MassiveCrazyRunnerExp => 90, + SpecialAttribute.MassiveShCrazyRunnerExp => 91, + SpecialAttribute.MassiveEscapeExp => 92, + SpecialAttribute.MassiveSpringBeachExp => 93, + SpecialAttribute.MassiveDanceDanceExp => 94, + SpecialAttribute.MassiveOxSpeed => 95, + SpecialAttribute.MassiveTrapMasterSpeed => 96, + SpecialAttribute.MassiveFinalSurvivalSpeed => 97, + SpecialAttribute.MassiveCrazyRunnerSpeed => 98, + SpecialAttribute.MassiveShCrazyRunnerSpeed => 99, + SpecialAttribute.MassiveEscapeSpeed => 100, + SpecialAttribute.MassiveSpringBeachSpeed => 101, + SpecialAttribute.MassiveDanceDanceSpeed => 102, + SpecialAttribute.NpcHitRewardSpBall => 103, + SpecialAttribute.NpcHitRewardEpBall => 104, + SpecialAttribute.HonorToken => 105, + SpecialAttribute.PvpExp => 106, + SpecialAttribute.DarkStreamDamage => 107, + SpecialAttribute.ReduceDarkStreamReceiveDamage => 108, + SpecialAttribute.DarkStreamEvp => 109, + SpecialAttribute.FishingDoubleMastery => 110, + SpecialAttribute.PlayInstrumentDoubleMastery => 111, + SpecialAttribute.CompleteFieldMissionSpeed => 112, + SpecialAttribute.GlideVerticalVelocity => 113, + SpecialAttribute.AdditionalEffect95000018 => 114, + SpecialAttribute.AdditionalEffect95000012 => 115, + SpecialAttribute.AdditionalEffect95000014 => 116, + SpecialAttribute.AdditionalEffect95000020 => 117, + SpecialAttribute.AdditionalEffect95000021 => 118, + SpecialAttribute.AdditionalEffect95000022 => 119, + SpecialAttribute.AdditionalEffect95000023 => 120, + SpecialAttribute.AdditionalEffect95000024 => 121, + SpecialAttribute.AdditionalEffect95000025 => 122, + SpecialAttribute.AdditionalEffect95000026 => 123, + SpecialAttribute.AdditionalEffect95000027 => 124, + SpecialAttribute.AdditionalEffect95000028 => 125, + SpecialAttribute.AdditionalEffect95000029 => 126, + SpecialAttribute.ReduceRecoveryEpInv => 127, + SpecialAttribute.MaxWeaponAttack => 128, + SpecialAttribute.MiningDoubleReward => 129, + SpecialAttribute.BreedingDoubleReward => 130, + SpecialAttribute.GatheringDoubleReward => 131, + SpecialAttribute.FarmingDoubleReward => 132, + SpecialAttribute.SmithingDoubleReward => 133, + SpecialAttribute.EngravingDoubleReward => 134, + SpecialAttribute.AlchemistDoubleReward => 135, + SpecialAttribute.CookingDoubleReward => 136, + SpecialAttribute.MiningDoubleMastery => 137, + SpecialAttribute.BreedingDoubleMastery => 138, + SpecialAttribute.GatheringDoubleMastery => 139, + SpecialAttribute.FarmingDoubleMastery => 140, + SpecialAttribute.SmithingDoubleMastery => 141, + SpecialAttribute.EngravingDoubleMastery => 142, + SpecialAttribute.AlchemistDoubleMastery => 143, + SpecialAttribute.CookingDoubleMastery => 144, + SpecialAttribute.ChaosRaidAttack => 145, + SpecialAttribute.ChaosRaidAttackSpeed => 146, + SpecialAttribute.ChaosRaidAccuracy => 147, + SpecialAttribute.ChaosRaidHp => 148, + SpecialAttribute.RecoveryBall => 149, + SpecialAttribute.FieldBossExp => 150, + SpecialAttribute.FieldBossDropRate => 151, + SpecialAttribute.ReduceFieldBossReceiveDamage => 152, + SpecialAttribute.AdditionalEffect95000016 => 153, + SpecialAttribute.PetTrapReward => 154, + SpecialAttribute.MiningEfficiency => 155, + SpecialAttribute.BreedingEfficiency => 156, + SpecialAttribute.GatheringEfficiency => 157, + SpecialAttribute.FarmingEfficiency => 158, + SpecialAttribute.ReduceDamageByTargetMaxHp => 159, + SpecialAttribute.ReduceMesoRevivalFee => 160, + SpecialAttribute.RidingRunSpeed => 161, + SpecialAttribute.DungeonRewardMeso => 162, + SpecialAttribute.ShopBuyingMeso => 163, + SpecialAttribute.ItemBoxRewardMeso => 164, + SpecialAttribute.ReduceRemakeOptionFee => 165, + SpecialAttribute.ReduceAirTaxiFee => 166, + SpecialAttribute.SocketUnlockProbability => 167, + SpecialAttribute.ReduceGemstoneUpgradeFee => 168, + SpecialAttribute.ReducePetRemakeOptionFee => 169, + SpecialAttribute.RidingSpeed => 170, + // SurvivalKillExp = 171 + // SurvivalTimeExp = 172 + // PhysicalDamage = 173 + // MagicalDamage = 174 + SpecialAttribute.ReduceGameItemSocketUnlockFee => 175, + + // Not mappable + // SpecialAttribute.TonicDropRate => 4, + // SpecialAttribute.GearDropRate => 5, + // SpecialAttribute.MaidExp => 62, + // SpecialAttribute.ReduceMaidRecipe => 63, + // SpecialAttribute.AcquireManufacturingExp => 76, + _ => byte.MaxValue, + }; + } + + public static SkillEffectMetadata Convert(this TriggerSkill trigger) { + SkillEffectMetadataCondition? condition = null; + SkillEffectMetadataSplash? splash = null; + if (trigger.splash) { + splash = new SkillEffectMetadataSplash( + Interval: trigger.interval, + Delay: trigger.delay > int.MaxValue ? int.MaxValue : (int) trigger.delay, + RemoveDelay: trigger.removeDelay, + UseDirection: trigger.useDirection, + ImmediateActive: trigger.immediateActive, + NonTargetActive: trigger.nonTargetActive, + OnlySensingActive: trigger.onlySensingActive, + DependOnCasterState: trigger.dependOnCasterState, + Independent: trigger.independent, + Chain: trigger.chain ? new SkillEffectMetadataChain(trigger.chainDistance) : null); + } else { + var owner = SkillTargetType.Owner; + if (trigger.skillOwner > 0 && Enum.IsDefined((SkillTargetType) trigger.skillOwner)) { + owner = (SkillTargetType) trigger.skillOwner; + } + condition = new SkillEffectMetadataCondition( + Condition: trigger.beginCondition.Convert(), + Owner: owner, + Target: (SkillTargetType) trigger.skillTarget, + OverlapCount: trigger.overlapCount, + RandomCast: trigger.randomCast); + } + + SkillEffectMetadata.Skill[] skills; + if (trigger.linkSkillID.Length > 0) { + skills = trigger.skillID + .Zip(trigger.level, (skillId, level) => new { skillId, level }) + .Zip(trigger.linkSkillID, (skill, linkSkillId) => new SkillEffectMetadata.Skill(skill.skillId, skill.level, linkSkillId)) + .ToArray(); + } else { + skills = trigger.skillID + .Zip(trigger.level, (skillId, level) => new SkillEffectMetadata.Skill(skillId, level)) + .ToArray(); + } + + return new SkillEffectMetadata( + FireCount: trigger.fireCount, + Skills: skills, + Condition: condition, + Splash: splash); + } + + public static IReadOnlyDictionary CollectAttackPoints(this SkillMotionData motion) { + var attackPoints = new Dictionary(); + + for (byte i = 0; i < motion.attack.Count; ++i) { + if (!attackPoints.ContainsKey(motion.attack[i].point)) { + attackPoints.Add(motion.attack[i].point, i); + + continue; + } + + attackPoints[motion.attack[i].point] = 0xFF; // multiple points have the name, cannot use look up table + } + + return attackPoints; + } + + public static SkillMetadataChange Convert(this ChangeSkill change) { + return new SkillMetadataChange( + Origin: new SkillMetadataChange.Skill( + Id: change.originSkillID, + Level: change.originSkillLevel), + Effects: change.changeSkillCheckEffectID + .Zip(change.changeSkillCheckEffectLevel, (effectId, effectLevel) => new { + effectId, + effectLevel, + }) + .Zip(change.changeSkillCheckEffectOverlapCount, (effect, overlapCount) => new SkillMetadataChange.Effect(effect.effectId, effect.effectLevel, overlapCount)) + .ToArray(), + Skills: change.changeSkillID + .Zip(change.changeSkillLevel, (skillId, level) => new SkillMetadataChange.Skill(skillId, level)) + .ToArray() + ); + } + + public static SkillMetadataAutoTargeting Convert(this AutoTargeting autoTargeting) { + return new SkillMetadataAutoTargeting( + MaxDegree: autoTargeting.autoTargetingMaxDegree, + MaxDistance: autoTargeting.autoTargetingMaxDistance, + MaxHeight: autoTargeting.autoTargetingMaxHeight, + UseMove: autoTargeting.autoTargetUseMove); + } + + public static BeginCondition Convert(this Maple2.File.Parser.Xml.Skill.BeginCondition beginCondition) { + return new BeginCondition( + Level: beginCondition.level, + Gender: (Gender) beginCondition.gender, + Mesos: beginCondition.money, + Stat: beginCondition.stat.ToDictionary(), + Maps: beginCondition.requireMapCodes.Select(mapCodes => mapCodes.code).ToArray(), + MapTypes: beginCondition.requireMapCategoryCodes.Select(mapType => (MapType) mapType.code).ToArray(), + Continents: beginCondition.requireMapContinentCodes.Select(continent => (Continent) continent.code).ToArray(), + ActiveSkill: beginCondition.requireSkillCodes.Select(skill => skill.code).ToArray(), + JobCode: beginCondition.job.Select(job => (JobCode) job.code).ToArray(), + Probability: beginCondition.probability, + CooldownTime: beginCondition.cooldownTime, + DurationWithoutMoving: (int) TimeSpan.FromSeconds(beginCondition.requireDurationWithoutMove).TotalMilliseconds, + DurationWithoutDamage: (int) TimeSpan.FromSeconds(beginCondition.requireDurationWithoutDamage).TotalMilliseconds, + OnlyShadowWorld: beginCondition.onlyShadowWorld || beginCondition.isShadowWorld, + OnlyFlyableMap: beginCondition.onlyFlyableMap, + AllowDead: beginCondition.allowDeadState, + AllowOnBattleMount: beginCondition.allowBattleRidingState, + OnlyOnBattleMount: beginCondition.onlyBattleRidingState, + AllowOnSurvival: beginCondition.allowMapleSurvival, + DungeonGroupType: beginCondition.requireDungeonRoomGroupTypes + .Where(type => Enum.TryParse(type.type, true, out DungeonGroupType _)) + .Select(type => Enum.Parse(type.type, true)) + .ToArray(), + Weapon: beginCondition.weapon.Select(weapon => new BeginConditionWeapon( + new ItemType(1, (byte) weapon.lh), + new ItemType(1, (byte) weapon.rh))).ToArray(), + Target: Convert(beginCondition.skillTarget), + Owner: Convert(beginCondition.skillOwner), + Caster: Convert(beginCondition.skillCaster)); + } + + // We use this default to avoid writing useless checks + private static readonly BeginConditionTarget DefaultBeginConditionTarget = new([], new BeginConditionTarget.EventCondition(EventConditionType.Activate, false, [], []), [], [], [], new Dictionary(), [], []); + private static BeginConditionTarget? Convert(SubConditionTarget? target) { + if (target == null) { + return null; + } + + var result = new BeginConditionTarget( + Buff: ParseBuffs(target), + Event: ParseEvent(target), + Stat: ParseStat(target), + States: target.requireStates + .Select(state => Enum.GetValues() + .FirstOrDefault(enumValue => + enumValue.GetType() + .GetField(enumValue.ToString()) + ?.GetCustomAttribute() + ?.Description == state)) + .Where(state => state != ActorState.None) + .ToArray(), + SubStates: target.requireSubStates + .Select(state => Enum.GetValues() + .FirstOrDefault(enumValue => + enumValue.GetType() + .GetField(enumValue.ToString()) + ?.GetCustomAttribute() + ?.Description == state)) + .Where(state => state != ActorSubState.None) + .ToArray(), + Masteries: target.requireMasteryTypes + .Where(type => Enum.TryParse(type, true, out MasteryType _)) + .Select(type => Enum.Parse(type, true)) + .Zip(target.requireMasteryValues, (type, value) => (Type: type, Value: value)) + .ToDictionary(pair => pair.Type, pair => pair.Value), + NpcIds: target.NpcIDs, + HasNotBuffIds: target.hasNotBuffID); + + return DefaultBeginConditionTarget.Equals(result) ? null : result; + + BeginConditionTarget.HasBuff[] ParseBuffs(SubConditionTarget data) { + if (data.hasBuffID.Length == 0 || data.hasBuffID[0] == 0) { + return []; + } + + var hasBuff = new BeginConditionTarget.HasBuff[data.hasBuffID.Length]; + for (int i = 0; i < hasBuff.Length; i++) { + hasBuff[i] = new BeginConditionTarget.HasBuff( + Id: data.hasBuffID[i], + Level: data.hasBuffLevel.Length > i ? data.hasBuffLevel[i] : (short) 0, + Owned: data.hasBuffOwner.Length > i && data.hasBuffOwner[i], + Count: data.hasBuffCount.Length > i ? data.hasBuffCount[i] : 0, + Compare: data.hasBuffCountCompare.Length > i ? Enum.Parse(data.hasBuffCountCompare[i]) : CompareType.Equals); + } + + return hasBuff; + } + + // Seems to only be used for test skills. + // BeginConditionTarget.HasSkill? ParseSkill(SubConditionTarget data) { + // return data.hasSkillID > 0 ? new BeginConditionTarget.HasSkill(data.hasSkillID, data.hasSkillLevel) : null; + // } + + BeginConditionTarget.EventCondition ParseEvent(SubConditionTarget data) { + return new BeginConditionTarget.EventCondition( + Type: (EventConditionType) data.eventCondition, + IgnoreOwner: data.ignoreOwnerEvent != 0, + SkillIds: data.eventSkillID, + BuffIds: data.eventEffectID); + } + + BeginConditionTarget.BeginConditionStat[] ParseStat(SubConditionTarget data) { + if (data.compareStat.Count == 0) { + return []; + } + + var stats = new BeginConditionTarget.BeginConditionStat[data.compareStat.Count]; + for (int i = 0; i < stats.Length; i++) { + foreach (BasicAttribute attribute in Enum.GetValues()) { + float value = data.compareStat[i][(byte) attribute]; + if (value != default) { + stats[i] = new BeginConditionTarget.BeginConditionStat( + Attribute: attribute, + Value: value, + Compare: data.compareStat.Count > i ? Enum.Parse(data.compareStat[i].func) : CompareType.Equals, + ValueType: (CompareStatValueType) data.compareStat[i].type); + break; + } + } + } + return stats; + } + } + + public static Dictionary> ToDictionary(this IEnumerable entries) { + var results = new Dictionary>(); + foreach (ItemOptionData entry in entries) { + var optionEntries = new List(); + foreach (BasicAttribute attribute in Enum.GetValues()) { + int[] value = entry.StatValue((byte) attribute); + if (value.Length > 0) { + Debug.Assert(value.Length is 1 or 2); + var valueRange = new ItemOption.Range(value[0], value.Length > 1 ? value[1] : value[0]); + optionEntries.Add(new ItemOption.Entry(BasicAttribute: attribute, Values: valueRange)); + } + float[] rate = entry.StatRate((byte) attribute); + if (rate.Length > 0) { + Debug.Assert(rate.Length is 1 or 2); + var rateRange = new ItemOption.Range(rate[0], rate.Length > 1 ? rate[1] : rate[0]); + optionEntries.Add(new ItemOption.Entry(BasicAttribute: attribute, Rates: rateRange)); + } + } + + foreach (SpecialAttribute attribute in Enum.GetValues()) { + byte index = attribute.OptionIndex(); + if (index == byte.MaxValue) continue; + + SpecialAttribute fixAttribute = attribute.SgiTarget(entry.sgi_target); + int[] value = entry.SpecialValue(index); + if (value.Length > 0) { + Debug.Assert(value.Length is 1 or 2); + var valueRange = new ItemOption.Range(value[0], value.Length > 1 ? value[1] : value[0]); + optionEntries.Add(new ItemOption.Entry(SpecialAttribute: fixAttribute, Values: valueRange)); + } + float[] rate = entry.SpecialRate(index); + if (rate.Length > 0) { + Debug.Assert(rate.Length is 1 or 2); + var rateRange = new ItemOption.Range(rate[0], rate.Length > 1 ? rate[1] : rate[0]); + optionEntries.Add(new ItemOption.Entry(SpecialAttribute: fixAttribute, Rates: rateRange)); + } + } + + if (!results.ContainsKey(entry.code)) { + results[entry.code] = new Dictionary(); + } + + // these entries are useless because they cannot be used. + if (entry.optionNumPick.Length == 0 || (entry.optionNumPick[0] == 0 && entry.optionNumPick[1] == 0)) { + continue; + } + + var option = new ItemOption( + MultiplyFactor: entry.multiply_factor == 0 ? 1 : entry.multiply_factor, + NumPick: new ItemOption.Range(entry.optionNumPick[0], entry.optionNumPick[1]), + Entries: optionEntries.ToArray()); + if (results[entry.code].ContainsKey(entry.grade)) { + Console.WriteLine($"{entry.code} already has grade {entry.grade}"); + } + + (results[entry.code] as Dictionary)!.Add(entry.grade, option); + } + + return results; + } + + public static ConditionMetadata.Parameters? ConvertCodes(this string[] codes) { + if (codes.Length == 0) { + return null; + } + + if (codes.Length > 1) { + var integers = new List(); + var strings = new List(); + foreach (string code in codes) { + if (int.TryParse(code, out int intCode)) { + integers.Add(intCode); + } else { + strings.Add(code); + } + } + + return new ConditionMetadata.Parameters( + Strings: strings.Count == 0 ? null : strings.ToArray(), + Integers: integers.Count == 0 ? null : integers.ToArray()); + } + + string[] split = codes[0].Split('-'); + if (split.Length > 1) { + return new ConditionMetadata.Parameters( + Range: new ConditionMetadata.Range(int.Parse(split[0]), int.Parse(split[1]))); + } + + if (int.TryParse(codes[0], out int integerResult)) { + return new ConditionMetadata.Parameters(Integers: [integerResult]); + } + return new ConditionMetadata.Parameters(Strings: [codes[0]]); + } +} diff --git a/Maple2.File.Ingest/Program.cs b/Maple2.File.Ingest/Program.cs index 9af669bcd..7437ca53c 100644 --- a/Maple2.File.Ingest/Program.cs +++ b/Maple2.File.Ingest/Program.cs @@ -1,283 +1,283 @@ -using System.Diagnostics; -using System.Globalization; -using System.Runtime.InteropServices; -using Maple2.Database.Context; -using Maple2.Database.Extensions; -using Maple2.Database.Model.Metadata; -using Maple2.File.Ingest; -using Maple2.File.Ingest.Helpers; -using Maple2.File.Ingest.Mapper; -using Maple2.File.IO; -using Maple2.File.IO.Nif; -using Maple2.File.Parser.Flat; -using Maple2.File.Parser.MapXBlock; -using Maple2.File.Parser.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; -using Microsoft.EntityFrameworkCore; - -const string locale = "NA"; -string language = "en"; -const string env = "Live"; - -Console.OutputEncoding = System.Text.Encoding.UTF8; - -bool runNavmesh = false; -bool dropData = false; - -foreach (string arg in args) { - switch (arg) { - case "--run-navmesh": - runNavmesh = true; - break; - case "--drop-data": - dropData = true; - break; - } -} - -// Force Globalization to en-US because we use periods instead of commas for decimals -CultureInfo.CurrentCulture = new CultureInfo("en-US"); - -DotEnv.Load(); - -string? languageEnv = Environment.GetEnvironmentVariable("LANGUAGE"); -if (languageEnv == null) { - throw new ArgumentException("LANGUAGE environment variable was not set"); -} -language = languageEnv.ToLower(); - -string? ms2Root = Environment.GetEnvironmentVariable("MS2_DATA_FOLDER"); -if (ms2Root == null) { - throw new ArgumentException("MS2_DATA_FOLDER environment variable was not set"); -} - -string xmlPath = Path.Combine(ms2Root, "Xml.m2d"); -string exportedPath = Path.Combine(ms2Root, "Resource/Exported.m2d"); -string serverPath = Path.Combine(ms2Root, "Server.m2d"); - -if (!File.Exists(xmlPath)) { - throw new FileNotFoundException($"Could not find Xml.m2d file at path: {xmlPath}"); -} - -if (!File.Exists(exportedPath)) { - throw new FileNotFoundException($"Could not find Exported.m2d file at path: {exportedPath}"); -} - -if (!File.Exists(serverPath)) { - throw new FileNotFoundException($"Could not find Server.m2d file at path: {serverPath}\n" + - "You can download this file from here: https://github.com/Zintixx/MapleStory2-XML/releases/latest"); -} - -string? server = Environment.GetEnvironmentVariable("DB_IP"); -string? port = Environment.GetEnvironmentVariable("DB_PORT"); -string? database = Environment.GetEnvironmentVariable("DATA_DB_NAME"); -string? user = Environment.GetEnvironmentVariable("DB_USER"); -string? password = Environment.GetEnvironmentVariable("DB_PASSWORD"); - -if (server == null || port == null || database == null || user == null || password == null) { - throw new ArgumentException("Database connection information was not set"); -} - -string worldServerDir = Path.Combine(Paths.SOLUTION_DIR, "Maple2.Server.World"); - -bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); -bool isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); -bool isMac = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); - -// check if dotnet ef is installed -Process processCheck; -if (isWindows) { - processCheck = Process.Start("CMD.exe", "/C dotnet ef"); -} else if (isLinux || isMac) { - processCheck = Process.Start("bash", "-c \"dotnet ef\""); -} else { - throw new PlatformNotSupportedException("Unsupported OS platform"); -} -processCheck.WaitForExit(); - -if (processCheck.ExitCode != 0) { - - Process installEf; - if (isWindows) { - installEf = Process.Start("CMD.exe", "/C dotnet tool install --global dotnet-ef"); - } else if (isLinux || isMac) { - installEf = Process.Start("bash", "-c \"dotnet tool install --global dotnet-ef\""); - } else { - throw new PlatformNotSupportedException("Unsupported OS platform"); - } - installEf.WaitForExit(); - if (installEf.ExitCode != 0) { - throw new Exception("Failed to install dotnet-ef. Please install it manually by running 'dotnet tool install --global dotnet-ef'"); - } - - if (isWindows) { - string dotnetToolsPath = Environment.GetEnvironmentVariable("USERPROFILE") + "/.dotnet/tools"; - string currentPath = Environment.GetEnvironmentVariable("PATH") ?? ""; - Environment.SetEnvironmentVariable("PATH", currentPath + ";" + dotnetToolsPath); - Console.WriteLine($"Updated PATH to include {dotnetToolsPath}"); - } else if (isLinux || isMac) { - string dotnetToolsPath = Environment.GetEnvironmentVariable("HOME") + "/.dotnet/tools"; - string currentPath = Environment.GetEnvironmentVariable("PATH") ?? ""; - Environment.SetEnvironmentVariable("PATH", currentPath + ":" + dotnetToolsPath); - Console.WriteLine($"Updated PATH to include {dotnetToolsPath}"); - } else { - throw new PlatformNotSupportedException("Unsupported OS platform"); - } -} - -string cmdCommand = "cd " + worldServerDir + " && dotnet ef database update"; - -Console.WriteLine("Migrating game database..."); - -Process process; -if (isWindows) { - process = Process.Start("CMD.exe", "/C " + cmdCommand); -} else if (isLinux || isMac) { - process = Process.Start("bash", "-c \"" + cmdCommand + "\""); -} else { - throw new PlatformNotSupportedException("Unsupported OS platform"); -} - -process.WaitForExit(); - -if (process.ExitCode != 0) { - throw new Exception("Failed to migrate game database."); -} - -Console.WriteLine("Game Migration complete!"); - -using var xmlReader = new M2dReader(xmlPath); -using var exportedReader = new M2dReader(exportedPath); -using var serverReader = new M2dReader(serverPath); - -string dataDbConnection = $"Server={server};Port={port};Database={database};User={user};Password={password};oldguids=true"; - -DbContextOptions options = new DbContextOptionsBuilder() - .UseMySql(dataDbConnection, ServerVersion.AutoDetect(dataDbConnection)).Options; - -Console.WriteLine("Connecting to metadata database..."); -using var metadataContext = new MetadataContext(options); - -bool schemaChanged = SchemaVersionManager.ShouldRecreateDatabase(metadataContext); - -if (dropData || schemaChanged) { - Console.WriteLine("Dropping metadata database..."); - metadataContext.Database.EnsureDeleted(); - metadataContext.ChangeTracker.Clear(); -} -Console.WriteLine("Ensuring metadata database is created..."); -metadataContext.Database.EnsureCreated(); -metadataContext.Database.ExecuteSqlRaw(@"SET GLOBAL max_allowed_packet=268435456"); // 256MB - -// Store schema version after creation -SchemaVersionManager.StoreSchemaVersion(metadataContext); - -Console.WriteLine("Starting data ingestion..."); - -// Filter Xml results based on feature settings. -Filter.Load(xmlReader, locale, env); - -// new TriggerGenerator(xmlReader).Generate(); - -var modelReaders = new List { - new PrefixedM2dReader("/library/", Path.Combine(ms2Root, "Resource/Library.m2d")), - new PrefixedM2dReader("/model/map/", Path.Combine(ms2Root, "Resource/Model/Map.m2d")), - new PrefixedM2dReader("/model/effect/", Path.Combine(ms2Root, "Resource/Model/Effect.m2d")), - new PrefixedM2dReader("/model/camera/", Path.Combine(ms2Root, "Resource/Model/Camera.m2d")), - new PrefixedM2dReader("/model/tool/", Path.Combine(ms2Root, "Resource/Model/Tool.m2d")), - new PrefixedM2dReader("/model/item/", Path.Combine(ms2Root, "Resource/Model/Item.m2d")), - new PrefixedM2dReader("/model/npc/", Path.Combine(ms2Root, "Resource/Model/Npc.m2d")), - new PrefixedM2dReader("/model/path/", Path.Combine(ms2Root, "Resource/Model/Path.m2d")), - new PrefixedM2dReader("/model/character/", Path.Combine(ms2Root, "Resource/Model/Character.m2d")), - new PrefixedM2dReader("/model/textures/", Path.Combine(ms2Root, "Resource/Model/Textures.m2d")), -}; - -UpdateDatabase(metadataContext, new TriggerMapper(xmlReader)); - -UpdateDatabase(metadataContext, new ItemMapper(xmlReader, language, false)); -UpdateDatabase(metadataContext, new NpcMapper(xmlReader, language)); - -UpdateDatabase(metadataContext, new ServerTableMapper(serverReader)); -UpdateDatabase(metadataContext, new AiMapper(serverReader)); - -UpdateDatabase(metadataContext, new AdditionalEffectMapper(xmlReader)); -UpdateDatabase(metadataContext, new AnimationMapper(xmlReader)); -UpdateDatabase(metadataContext, new PetMapper(xmlReader)); -UpdateDatabase(metadataContext, new MapMapper(xmlReader, language)); -UpdateDatabase(metadataContext, new UgcMapMapper(xmlReader)); -UpdateDatabase(metadataContext, new ExportedUgcMapMapper(xmlReader)); -UpdateDatabase(metadataContext, new QuestMapper(xmlReader, language)); -UpdateDatabase(metadataContext, new RideMapper(xmlReader)); -UpdateDatabase(metadataContext, new ScriptMapper(xmlReader, language)); -UpdateDatabase(metadataContext, new SkillMapper(xmlReader, language)); -UpdateDatabase(metadataContext, new TableMapper(xmlReader, language)); -UpdateDatabase(metadataContext, new AchievementMapper(xmlReader)); -UpdateDatabase(metadataContext, new FunctionCubeMapper(xmlReader)); - -NifParserHelper.ParseNif(modelReaders); - -UpdateDatabase(metadataContext, new NifMapper()); -UpdateDatabase(metadataContext, new NxsMeshMapper()); - -var index = new FlatTypeIndex(exportedReader); - -XBlockParser parser = new XBlockParser(exportedReader, index); - -UpdateDatabase(metadataContext, new MapEntityMapper(metadataContext, parser)); - -MapDataMapper mapDataMapper = new MapDataMapper(metadataContext, parser); - -UpdateDatabase(metadataContext, mapDataMapper); - -mapDataMapper.ReportStats(); - -if (runNavmesh) { - _ = new NavMeshMapper(metadataContext, exportedReader); -} - -Console.WriteLine("Done!".ColorGreen()); - -void UpdateDatabase(DbContext context, TypeMapper mapper) where T : class { - string? tableName = context.GetTableName(); - Debug.Assert(!string.IsNullOrEmpty(tableName), $"Invalid table name: {tableName}"); - - Console.Write($"Processing {tableName}... "); - uint crc32C = mapper.Process(); - Console.Write($"Finished in {mapper.ElapsedMilliseconds}ms"); - Console.WriteLine(); - - var checksum = context.Find(tableName); - if (checksum != null) { - if (checksum.Crc32C == crc32C) { - Console.WriteLine($"Table {tableName} is up-to-date".ColorGreen()); - return; - } - - checksum.Crc32C = crc32C; - Console.WriteLine($"Table {tableName} outdated".ColorRed()); - int result = context.Database.ExecuteSqlRaw(@$"DELETE FROM `{tableName}`"); - Console.WriteLine($"Removed table {tableName} rows: {result}"); - } - - Stopwatch stopwatch = Stopwatch.StartNew(); - // Write entries to table - foreach (T result in mapper.Results) { - context.Add(result); - } - - // Write checksum to table - if (checksum == null) { - context.Add(new TableChecksum { - TableName = tableName, - Crc32C = crc32C, - }); - } else { - context.Update(checksum); - } - - context.SaveChanges(); - - stopwatch.Stop(); - Console.WriteLine($"Wrote {mapper.Results.Count} entries to {tableName} in {stopwatch.ElapsedMilliseconds}ms"); -} +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using Maple2.Database.Context; +using Maple2.Database.Extensions; +using Maple2.Database.Model.Metadata; +using Maple2.File.Ingest; +using Maple2.File.Ingest.Helpers; +using Maple2.File.Ingest.Mapper; +using Maple2.File.IO; +using Maple2.File.IO.Nif; +using Maple2.File.Parser.Flat; +using Maple2.File.Parser.MapXBlock; +using Maple2.File.Parser.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; +using Microsoft.EntityFrameworkCore; + +const string locale = "NA"; +string language = "en"; +const string env = "Live"; + +Console.OutputEncoding = System.Text.Encoding.UTF8; + +bool runNavmesh = false; +bool dropData = false; + +foreach (string arg in args) { + switch (arg) { + case "--run-navmesh": + runNavmesh = true; + break; + case "--drop-data": + dropData = true; + break; + } +} + +// Force Globalization to en-US because we use periods instead of commas for decimals +CultureInfo.CurrentCulture = new CultureInfo("en-US"); + +DotEnv.Load(); + +string? languageEnv = Environment.GetEnvironmentVariable("LANGUAGE"); +if (languageEnv == null) { + throw new ArgumentException("LANGUAGE environment variable was not set"); +} +language = languageEnv.ToLower(); + +string? ms2Root = Environment.GetEnvironmentVariable("MS2_DATA_FOLDER"); +if (ms2Root == null) { + throw new ArgumentException("MS2_DATA_FOLDER environment variable was not set"); +} + +string xmlPath = Path.Combine(ms2Root, "Xml.m2d"); +string exportedPath = Path.Combine(ms2Root, "Resource/Exported.m2d"); +string serverPath = Path.Combine(ms2Root, "Server.m2d"); + +if (!File.Exists(xmlPath)) { + throw new FileNotFoundException($"Could not find Xml.m2d file at path: {xmlPath}"); +} + +if (!File.Exists(exportedPath)) { + throw new FileNotFoundException($"Could not find Exported.m2d file at path: {exportedPath}"); +} + +if (!File.Exists(serverPath)) { + throw new FileNotFoundException($"Could not find Server.m2d file at path: {serverPath}\n" + + "You can download this file from here: https://github.com/Zintixx/MapleStory2-XML/releases/latest"); +} + +string? server = Environment.GetEnvironmentVariable("DB_IP"); +string? port = Environment.GetEnvironmentVariable("DB_PORT"); +string? database = Environment.GetEnvironmentVariable("DATA_DB_NAME"); +string? user = Environment.GetEnvironmentVariable("DB_USER"); +string? password = Environment.GetEnvironmentVariable("DB_PASSWORD"); + +if (server == null || port == null || database == null || user == null || password == null) { + throw new ArgumentException("Database connection information was not set"); +} + +string worldServerDir = Path.Combine(Paths.SOLUTION_DIR, "Maple2.Server.World"); + +bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); +bool isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); +bool isMac = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + +// check if dotnet ef is installed +Process processCheck; +if (isWindows) { + processCheck = Process.Start("CMD.exe", "/C dotnet ef"); +} else if (isLinux || isMac) { + processCheck = Process.Start("bash", "-c \"dotnet ef\""); +} else { + throw new PlatformNotSupportedException("Unsupported OS platform"); +} +processCheck.WaitForExit(); + +if (processCheck.ExitCode != 0) { + + Process installEf; + if (isWindows) { + installEf = Process.Start("CMD.exe", "/C dotnet tool install --global dotnet-ef"); + } else if (isLinux || isMac) { + installEf = Process.Start("bash", "-c \"dotnet tool install --global dotnet-ef\""); + } else { + throw new PlatformNotSupportedException("Unsupported OS platform"); + } + installEf.WaitForExit(); + if (installEf.ExitCode != 0) { + throw new Exception("Failed to install dotnet-ef. Please install it manually by running 'dotnet tool install --global dotnet-ef'"); + } + + if (isWindows) { + string dotnetToolsPath = Environment.GetEnvironmentVariable("USERPROFILE") + "/.dotnet/tools"; + string currentPath = Environment.GetEnvironmentVariable("PATH") ?? ""; + Environment.SetEnvironmentVariable("PATH", currentPath + ";" + dotnetToolsPath); + Console.WriteLine($"Updated PATH to include {dotnetToolsPath}"); + } else if (isLinux || isMac) { + string dotnetToolsPath = Environment.GetEnvironmentVariable("HOME") + "/.dotnet/tools"; + string currentPath = Environment.GetEnvironmentVariable("PATH") ?? ""; + Environment.SetEnvironmentVariable("PATH", currentPath + ":" + dotnetToolsPath); + Console.WriteLine($"Updated PATH to include {dotnetToolsPath}"); + } else { + throw new PlatformNotSupportedException("Unsupported OS platform"); + } +} + +string cmdCommand = "cd " + worldServerDir + " && dotnet ef database update"; + +Console.WriteLine("Migrating game database..."); + +Process process; +if (isWindows) { + process = Process.Start("CMD.exe", "/C " + cmdCommand); +} else if (isLinux || isMac) { + process = Process.Start("bash", "-c \"" + cmdCommand + "\""); +} else { + throw new PlatformNotSupportedException("Unsupported OS platform"); +} + +process.WaitForExit(); + +if (process.ExitCode != 0) { + throw new Exception("Failed to migrate game database."); +} + +Console.WriteLine("Game Migration complete!"); + +using var xmlReader = new M2dReader(xmlPath); +using var exportedReader = new M2dReader(exportedPath); +using var serverReader = new M2dReader(serverPath); + +string dataDbConnection = $"Server={server};Port={port};Database={database};User={user};Password={password};oldguids=true"; + +DbContextOptions options = new DbContextOptionsBuilder() + .UseMySql(dataDbConnection, ServerVersion.AutoDetect(dataDbConnection)).Options; + +Console.WriteLine("Connecting to metadata database..."); +using var metadataContext = new MetadataContext(options); + +bool schemaChanged = SchemaVersionManager.ShouldRecreateDatabase(metadataContext); + +if (dropData || schemaChanged) { + Console.WriteLine("Dropping metadata database..."); + metadataContext.Database.EnsureDeleted(); + metadataContext.ChangeTracker.Clear(); +} +Console.WriteLine("Ensuring metadata database is created..."); +metadataContext.Database.EnsureCreated(); +metadataContext.Database.ExecuteSqlRaw(@"SET GLOBAL max_allowed_packet=268435456"); // 256MB + +// Store schema version after creation +SchemaVersionManager.StoreSchemaVersion(metadataContext); + +Console.WriteLine("Starting data ingestion..."); + +// Filter Xml results based on feature settings. +Filter.Load(xmlReader, locale, env); + +// new TriggerGenerator(xmlReader).Generate(); + +var modelReaders = new List { + new PrefixedM2dReader("/library/", Path.Combine(ms2Root, "Resource/Library.m2d")), + new PrefixedM2dReader("/model/map/", Path.Combine(ms2Root, "Resource/Model/Map.m2d")), + new PrefixedM2dReader("/model/effect/", Path.Combine(ms2Root, "Resource/Model/Effect.m2d")), + new PrefixedM2dReader("/model/camera/", Path.Combine(ms2Root, "Resource/Model/Camera.m2d")), + new PrefixedM2dReader("/model/tool/", Path.Combine(ms2Root, "Resource/Model/Tool.m2d")), + new PrefixedM2dReader("/model/item/", Path.Combine(ms2Root, "Resource/Model/Item.m2d")), + new PrefixedM2dReader("/model/npc/", Path.Combine(ms2Root, "Resource/Model/Npc.m2d")), + new PrefixedM2dReader("/model/path/", Path.Combine(ms2Root, "Resource/Model/Path.m2d")), + new PrefixedM2dReader("/model/character/", Path.Combine(ms2Root, "Resource/Model/Character.m2d")), + new PrefixedM2dReader("/model/textures/", Path.Combine(ms2Root, "Resource/Model/Textures.m2d")), +}; + +UpdateDatabase(metadataContext, new TriggerMapper(xmlReader)); + +UpdateDatabase(metadataContext, new ItemMapper(xmlReader, language, false)); +UpdateDatabase(metadataContext, new NpcMapper(xmlReader, language)); + +UpdateDatabase(metadataContext, new ServerTableMapper(serverReader)); +UpdateDatabase(metadataContext, new AiMapper(serverReader)); + +UpdateDatabase(metadataContext, new AdditionalEffectMapper(xmlReader)); +UpdateDatabase(metadataContext, new AnimationMapper(xmlReader)); +UpdateDatabase(metadataContext, new PetMapper(xmlReader)); +UpdateDatabase(metadataContext, new MapMapper(xmlReader, language)); +UpdateDatabase(metadataContext, new UgcMapMapper(xmlReader)); +UpdateDatabase(metadataContext, new ExportedUgcMapMapper(xmlReader)); +UpdateDatabase(metadataContext, new QuestMapper(xmlReader, language)); +UpdateDatabase(metadataContext, new RideMapper(xmlReader)); +UpdateDatabase(metadataContext, new ScriptMapper(xmlReader, language)); +UpdateDatabase(metadataContext, new SkillMapper(xmlReader, language)); +UpdateDatabase(metadataContext, new TableMapper(xmlReader, language)); +UpdateDatabase(metadataContext, new AchievementMapper(xmlReader)); +UpdateDatabase(metadataContext, new FunctionCubeMapper(xmlReader)); + +NifParserHelper.ParseNif(modelReaders); + +UpdateDatabase(metadataContext, new NifMapper()); +UpdateDatabase(metadataContext, new NxsMeshMapper()); + +var index = new FlatTypeIndex(exportedReader); + +XBlockParser parser = new XBlockParser(exportedReader, index); + +UpdateDatabase(metadataContext, new MapEntityMapper(metadataContext, parser)); + +MapDataMapper mapDataMapper = new MapDataMapper(metadataContext, parser); + +UpdateDatabase(metadataContext, mapDataMapper); + +mapDataMapper.ReportStats(); + +if (runNavmesh) { + _ = new NavMeshMapper(metadataContext, exportedReader); +} + +Console.WriteLine("Done!".ColorGreen()); + +void UpdateDatabase(DbContext context, TypeMapper mapper) where T : class { + string? tableName = context.GetTableName(); + Debug.Assert(!string.IsNullOrEmpty(tableName), $"Invalid table name: {tableName}"); + + Console.Write($"Processing {tableName}... "); + uint crc32C = mapper.Process(); + Console.Write($"Finished in {mapper.ElapsedMilliseconds}ms"); + Console.WriteLine(); + + var checksum = context.Find(tableName); + if (checksum != null) { + if (checksum.Crc32C == crc32C) { + Console.WriteLine($"Table {tableName} is up-to-date".ColorGreen()); + return; + } + + checksum.Crc32C = crc32C; + Console.WriteLine($"Table {tableName} outdated".ColorRed()); + int result = context.Database.ExecuteSqlRaw(@$"DELETE FROM `{tableName}`"); + Console.WriteLine($"Removed table {tableName} rows: {result}"); + } + + Stopwatch stopwatch = Stopwatch.StartNew(); + // Write entries to table + foreach (T result in mapper.Results) { + context.Add(result); + } + + // Write checksum to table + if (checksum == null) { + context.Add(new TableChecksum { + TableName = tableName, + Crc32C = crc32C, + }); + } else { + context.Update(checksum); + } + + context.SaveChanges(); + + stopwatch.Stop(); + Console.WriteLine($"Wrote {mapper.Results.Count} entries to {tableName} in {stopwatch.ElapsedMilliseconds}ms"); +} diff --git a/Maple2.File.Ingest/SchemaVersionManager.cs b/Maple2.File.Ingest/SchemaVersionManager.cs index 3c8eb70ff..e33f195f6 100644 --- a/Maple2.File.Ingest/SchemaVersionManager.cs +++ b/Maple2.File.Ingest/SchemaVersionManager.cs @@ -1,73 +1,73 @@ -using System.Text; -using Force.Crc32; -using Maple2.Database.Model.Metadata; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; - -namespace Maple2.File.Ingest; - -public static class SchemaVersionManager { - public static bool ShouldRecreateDatabase(DbContext context) { - string currentSchemaHash = GenerateSchemaHash(context); - - try { - SchemaVersion? schemaVersion = context.Set().FirstOrDefault(); - - if (schemaVersion == null) { - Console.WriteLine("No schema version found."); - return true; - } - - if (schemaVersion.SchemaHash != currentSchemaHash) { - Console.WriteLine("Schema has changed, database will be recreated."); - return true; - } - - return false; - } catch { - // Table doesn't exist or there was an error - return true; - } - } - - public static void StoreSchemaVersion(DbContext context) { - string hash = GenerateSchemaHash(context); - - // Remove any existing schema versions - foreach (SchemaVersion existing in context.Set().ToList()) { - context.Remove(existing); - } - - // Add new schema version - context.Add(new SchemaVersion { - SchemaHash = hash, - UpdatedAt = DateTime.UtcNow, - }); - - context.SaveChanges(); - } - - private static string GenerateSchemaHash(DbContext context) { - IModel model = context.Model; - IEnumerable entityTypes = model.GetEntityTypes(); - - var schemaDefinition = new StringBuilder(); - - foreach (IEntityType entityType in entityTypes.OrderBy(e => e.Name)) { - schemaDefinition.AppendLine(entityType.Name); - - // Add properties - foreach (IProperty property in entityType.GetProperties().OrderBy(p => p.Name)) { - schemaDefinition.AppendLine($" {property.Name}:{property.ClrType.Name}:{property.GetColumnName()}"); - } - - // Add relationships - foreach (INavigation navigation in entityType.GetNavigations().OrderBy(n => n.Name)) { - schemaDefinition.AppendLine($" Nav:{navigation.Name}:{navigation.TargetEntityType.Name}"); - } - } - - // Compute hash using the same CRC32C algorithm used elsewhere - return Crc32CAlgorithm.Compute(Encoding.UTF8.GetBytes(schemaDefinition.ToString())).ToString(); - } -} +using System.Text; +using Force.Crc32; +using Maple2.Database.Model.Metadata; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; + +namespace Maple2.File.Ingest; + +public static class SchemaVersionManager { + public static bool ShouldRecreateDatabase(DbContext context) { + string currentSchemaHash = GenerateSchemaHash(context); + + try { + SchemaVersion? schemaVersion = context.Set().FirstOrDefault(); + + if (schemaVersion == null) { + Console.WriteLine("No schema version found."); + return true; + } + + if (schemaVersion.SchemaHash != currentSchemaHash) { + Console.WriteLine("Schema has changed, database will be recreated."); + return true; + } + + return false; + } catch { + // Table doesn't exist or there was an error + return true; + } + } + + public static void StoreSchemaVersion(DbContext context) { + string hash = GenerateSchemaHash(context); + + // Remove any existing schema versions + foreach (SchemaVersion existing in context.Set().ToList()) { + context.Remove(existing); + } + + // Add new schema version + context.Add(new SchemaVersion { + SchemaHash = hash, + UpdatedAt = DateTime.UtcNow, + }); + + context.SaveChanges(); + } + + private static string GenerateSchemaHash(DbContext context) { + IModel model = context.Model; + IEnumerable entityTypes = model.GetEntityTypes(); + + var schemaDefinition = new StringBuilder(); + + foreach (IEntityType entityType in entityTypes.OrderBy(e => e.Name)) { + schemaDefinition.AppendLine(entityType.Name); + + // Add properties + foreach (IProperty property in entityType.GetProperties().OrderBy(p => p.Name)) { + schemaDefinition.AppendLine($" {property.Name}:{property.ClrType.Name}:{property.GetColumnName()}"); + } + + // Add relationships + foreach (INavigation navigation in entityType.GetNavigations().OrderBy(n => n.Name)) { + schemaDefinition.AppendLine($" Nav:{navigation.Name}:{navigation.TargetEntityType.Name}"); + } + } + + // Compute hash using the same CRC32C algorithm used elsewhere + return Crc32CAlgorithm.Compute(Encoding.UTF8.GetBytes(schemaDefinition.ToString())).ToString(); + } +} diff --git a/Maple2.File.Ingest/Utils/AiTranslate.cs b/Maple2.File.Ingest/Utils/AiTranslate.cs index b80ea9351..bd340d76f 100644 --- a/Maple2.File.Ingest/Utils/AiTranslate.cs +++ b/Maple2.File.Ingest/Utils/AiTranslate.cs @@ -1,37 +1,37 @@ -using System.Globalization; -using CsvHelper; - -namespace Maple2.File.Ingest.Utils; - -public static class AiTranslate { - private static readonly List<(string Kr, string En)> Lookup = new(); - - static AiTranslate() { - using var reader = new StreamReader("Utils/ai_translate.csv"); - using var csv = new CsvReader(reader, CultureInfo.InvariantCulture); - csv.Read(); - csv.ReadHeader(); - - while (csv.Read()) { - string kr = csv.GetField("kr"); - string en = csv.GetField("en"); - Lookup.Add((kr, en)); - } - } - - public static string Translate(string input) { - // Only translate if there is at least one non-ascii character. - if (!input.Any(c => c > 0x255)) { - return input; - } - - foreach ((string kr, string en) in Lookup) { - if (input.Contains(kr)) { - return input.Replace(kr, en); - } - } - - Console.WriteLine($"No translation for: {input.Trim()}"); - return input; - } -} +using System.Globalization; +using CsvHelper; + +namespace Maple2.File.Ingest.Utils; + +public static class AiTranslate { + private static readonly List<(string Kr, string En)> Lookup = new(); + + static AiTranslate() { + using var reader = new StreamReader("Utils/ai_translate.csv"); + using var csv = new CsvReader(reader, CultureInfo.InvariantCulture); + csv.Read(); + csv.ReadHeader(); + + while (csv.Read()) { + string kr = csv.GetField("kr"); + string en = csv.GetField("en"); + Lookup.Add((kr, en)); + } + } + + public static string Translate(string input) { + // Only translate if there is at least one non-ascii character. + if (!input.Any(c => c > 0x255)) { + return input; + } + + foreach ((string kr, string en) in Lookup) { + if (input.Contains(kr)) { + return input.Replace(kr, en); + } + } + + Console.WriteLine($"No translation for: {input.Trim()}"); + return input; + } +} diff --git a/Maple2.File.Ingest/Utils/AttributeExtensions.cs b/Maple2.File.Ingest/Utils/AttributeExtensions.cs index eac2f6832..fd969857f 100644 --- a/Maple2.File.Ingest/Utils/AttributeExtensions.cs +++ b/Maple2.File.Ingest/Utils/AttributeExtensions.cs @@ -1,240 +1,240 @@ -using Maple2.Model.Enum; - -namespace Maple2.File.Ingest.Utils; - -internal static class AttributeExtensions { - public static BasicAttribute ToBasicAttribute(this string value) { - return value.ToLower() switch { - "str" => BasicAttribute.Strength, - "dex" => BasicAttribute.Dexterity, - "int" => BasicAttribute.Intelligence, - "luk" => BasicAttribute.Luck, - "hp" => BasicAttribute.Health, - "hp_rgp" => BasicAttribute.HpRegen, - "hp_inv" => BasicAttribute.HpRegenInterval, - "sp" => BasicAttribute.Spirit, - "sp_rgp" => BasicAttribute.SpRegen, - "sp_inv" => BasicAttribute.SpRegenInterval, - "ep" => BasicAttribute.Stamina, - "ep_rgp" => BasicAttribute.StaminaRegen, - "ep_inv" => BasicAttribute.StaminaRegenInterval, - "asp" => BasicAttribute.AttackSpeed, - "msp" => BasicAttribute.MovementSpeed, - "atp" => BasicAttribute.Accuracy, - "evp" => BasicAttribute.Evasion, - "cap" => BasicAttribute.CriticalRate, - "cad" => BasicAttribute.CriticalDamage, - "car" => BasicAttribute.CriticalEvasion, - "ndd" => BasicAttribute.Defense, - "abp" => BasicAttribute.PerfectGuard, - "jmp" => BasicAttribute.JumpHeight, - "pap" => BasicAttribute.PhysicalAtk, - "map" => BasicAttribute.MagicalAtk, - "par" => BasicAttribute.PhysicalRes, - "mar" => BasicAttribute.MagicalRes, - "wapmin" => BasicAttribute.MinWeaponAtk, - "wapmax" => BasicAttribute.MaxWeaponAtk, - "dmg" => BasicAttribute.Damage, - "pen" => BasicAttribute.Piercing, - "rmsp" => BasicAttribute.MountSpeed, - "bap" => BasicAttribute.BonusAtk, - "bap_pet" => BasicAttribute.PetBonusAtk, - _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Invalid BasicAttribute."), - }; - } - - public static SpecialAttribute ToSpecialAttribute(this string value) { - return value.ToLower() switch { - "seg" => SpecialAttribute.Experience, - "smd" => SpecialAttribute.Meso, - "sss" => SpecialAttribute.SwimSpeed, - "dashdistance" => SpecialAttribute.DashDistance, - // "spd" => 4, - // "sid" => 5, - "finaladditionaldamage" => SpecialAttribute.TotalDamage, - "cri" => SpecialAttribute.CriticalDamage, - "sgi" => SpecialAttribute.NormalNpcDamage, - "sgi_leader" => SpecialAttribute.LeaderNpcDamage, - "sgi_elite" => SpecialAttribute.EliteNpcDamage, - "sgi_boss" => SpecialAttribute.BossNpcDamage, - "killhprestore" => SpecialAttribute.HpOnKill, - "killsprestore" => SpecialAttribute.SpiritOnKill, - "killeprestore" => SpecialAttribute.StaminaOnKill, - "heal" => SpecialAttribute.RecoveryBonus, - "receivedhealincrease" => SpecialAttribute.BonusRecoveryFromAlly, - "icedamage" => SpecialAttribute.IceDamage, - "firedamage" => SpecialAttribute.FireDamage, - "darkdamage" => SpecialAttribute.DarkDamage, - "lightdamage" => SpecialAttribute.HolyDamage, - "poisondamage" => SpecialAttribute.PoisonDamage, - "thunderdamage" => SpecialAttribute.ElectricDamage, - "nddincrease" => SpecialAttribute.MeleeDamage, - "lddincrease" => SpecialAttribute.RangedDamage, - "parpen" => SpecialAttribute.PhysicalPiercing, - "marpen" => SpecialAttribute.MagicalPiercing, - "icedamagereduce" => SpecialAttribute.ReduceIceDamage, - "firedamagereduce" => SpecialAttribute.ReduceFireDamage, - "darkdamagereduce" => SpecialAttribute.ReduceDarkDamage, - "lightdamagereduce" => SpecialAttribute.ReduceHolyDamage, - "poisondamagereduce" => SpecialAttribute.ReducePoisonDamage, - "thunderdamagereduce" => SpecialAttribute.ReduceElectricDamage, - "stunreduce" => SpecialAttribute.ReduceStun, - "conditionreduce" => SpecialAttribute.ReduceDebuff, - "skillcooldown" => SpecialAttribute.ReduceCooldown, - "neardistancedamagereduce" => SpecialAttribute.ReduceMeleeDamage, - "longdistancedamagereduce" => SpecialAttribute.ReduceRangedDamage, - "knockbackreduce" => SpecialAttribute.ReduceKnockBack, - "stunprocndd" => SpecialAttribute.MeleeStun, - "stunprocldd" => SpecialAttribute.RangedStun, - "knockbackprocndd" => SpecialAttribute.MeleeKnockBack, - "knockbackprocldd" => SpecialAttribute.RangedKnockBack, - "snareprocndd" => SpecialAttribute.MeleeImmobilize, - "snareprocldd" => SpecialAttribute.RangedImmobilize, - "aoeprocndd" => SpecialAttribute.MeleeSplashDamage, - "aoeprocldd" => SpecialAttribute.RangedSplashDamage, - "npckilldropitemincrate" => SpecialAttribute.DropRate, - "seg_questreward" => SpecialAttribute.QuestExp, - "smd_questreward" => SpecialAttribute.QuestMeso, - "seg_fishingreward" => SpecialAttribute.FishingExp, - "seg_arcadereward" => SpecialAttribute.ArcadeExp, - "seg_playinstrumentreward" => SpecialAttribute.PlayInstrumentExp, - "invoke_effect1" => SpecialAttribute.InvokeEffect1, - "invoke_effect2" => SpecialAttribute.InvokeEffect2, - "invoke_effect3" => SpecialAttribute.InvokeEffect3, - "pvpdamageincrease" => SpecialAttribute.PvpDamage, - "pvpdamagereduce" => SpecialAttribute.ReducePvpDamage, - "improveguildexp" => SpecialAttribute.GuildExp, - "improveguildcoin" => SpecialAttribute.GuildCoin, - "improvemassiveeventbexpball" => SpecialAttribute.MassiveEventExpBall, - "reduce_meso_trade_fee" => SpecialAttribute.ReduceMesoTradeFee, - "reduce_enchant_matrial_fee" => SpecialAttribute.ReduceEnchantMaterialFee, - "reduce_merat_revival_fee" => SpecialAttribute.ReduceMeretRevivalFee, - "improve_mining_reward_item" => SpecialAttribute.MiningRewardItem, - "improve_breeding_reward_item" => SpecialAttribute.BreedingRewardItem, - "improve_blacksmithing_reward_mastery" => SpecialAttribute.SmithingRewardMastery, - "improve_engraving_reward_mastery" => SpecialAttribute.EngravingRewardMastery, - "improve_gathering_reward_item" => SpecialAttribute.GatheringRewardItem, - "improve_farming_reward_item" => SpecialAttribute.FarmingRewardItem, - "improve_alchemist_reward_mastery" => SpecialAttribute.AlchemistRewardMastery, - "improve_cooking_reward_mastery" => SpecialAttribute.CookingRewardMastery, - "improve_acquire_gathering_exp" => SpecialAttribute.AcquireGatheringExp, - "skill_levelup_tier_1" => SpecialAttribute.SkillLevelUpTier1, - "skill_levelup_tier_2" => SpecialAttribute.SkillLevelUpTier2, - "skill_levelup_tier_3" => SpecialAttribute.SkillLevelUpTier3, - "skill_levelup_tier_4" => SpecialAttribute.SkillLevelUpTier4, - "skill_levelup_tier_5" => SpecialAttribute.SkillLevelUpTier5, - "skill_levelup_tier_6" => SpecialAttribute.SkillLevelUpTier6, - "skill_levelup_tier_7" => SpecialAttribute.SkillLevelUpTier7, - "skill_levelup_tier_8" => SpecialAttribute.SkillLevelUpTier8, - "skill_levelup_tier_9" => SpecialAttribute.SkillLevelUpTier9, - "skill_levelup_tier_10" => SpecialAttribute.SkillLevelUpTier10, - "skill_levelup_tier_11" => SpecialAttribute.SkillLevelUpTier11, - "skill_levelup_tier_12" => SpecialAttribute.SkillLevelUpTier12, - "skill_levelup_tier_13" => SpecialAttribute.SkillLevelUpTier13, - "skill_levelup_tier_14" => SpecialAttribute.SkillLevelUpTier14, - "improve_massive_ox_exp" => SpecialAttribute.MassiveOxExp, - "improve_massive_trapmaster_exp" => SpecialAttribute.MassiveTrapMasterExp, - "improve_massive_finalsurvival_exp" => SpecialAttribute.MassiveFinalSurvivalExp, - "improve_massive_crazyrunner_exp" => SpecialAttribute.MassiveCrazyRunnerExp, - "improve_massive_sh_crazyrunner_exp" => SpecialAttribute.MassiveShCrazyRunnerExp, - "improve_massive_escape_exp" => SpecialAttribute.MassiveEscapeExp, - "improve_massive_springbeach_exp" => SpecialAttribute.MassiveSpringBeachExp, - "improve_massive_dancedance_exp" => SpecialAttribute.MassiveDanceDanceExp, - "improve_massive_ox_msp" => SpecialAttribute.MassiveOxSpeed, - "improve_massive_trapmaster_msp" => SpecialAttribute.MassiveTrapMasterSpeed, - "improve_massive_finalsurvival_msp" => SpecialAttribute.MassiveFinalSurvivalSpeed, - "improve_massive_crazyrunner_msp" => SpecialAttribute.MassiveCrazyRunnerSpeed, - "improve_massive_sh_crazyrunner_msp" => SpecialAttribute.MassiveShCrazyRunnerSpeed, - "improve_massive_escape_msp" => SpecialAttribute.MassiveEscapeSpeed, - "improve_massive_springbeach_msp" => SpecialAttribute.MassiveSpringBeachSpeed, - "improve_massive_dancedance_msp" => SpecialAttribute.MassiveDanceDanceSpeed, - "npc_hit_reward_sp_ball" => SpecialAttribute.NpcHitRewardSpBall, - "npc_hit_reward_ep_ball" => SpecialAttribute.NpcHitRewardEpBall, - "improve_honor_token" => SpecialAttribute.HonorToken, - "improve_pvp_exp" => SpecialAttribute.PvpExp, - "improve_darkstream_damage" => SpecialAttribute.DarkStreamDamage, - "reduce_darkstream_recive_damage" => SpecialAttribute.ReduceDarkStreamReceiveDamage, - "improve_darkstream_evp" => SpecialAttribute.DarkStreamEvp, - "fishing_double_mastery" => SpecialAttribute.FishingDoubleMastery, - "playinstrument_double_mastery" => SpecialAttribute.PlayInstrumentDoubleMastery, - "complete_fieldmission_msp" => SpecialAttribute.CompleteFieldMissionSpeed, - "improve_glide_vertical_velocity" => SpecialAttribute.GlideVerticalVelocity, - "additionaleffect_95000018" => SpecialAttribute.AdditionalEffect95000018, - "additionaleffect_95000012" => SpecialAttribute.AdditionalEffect95000012, - "additionaleffect_95000014" => SpecialAttribute.AdditionalEffect95000014, - "additionaleffect_95000020" => SpecialAttribute.AdditionalEffect95000020, - "additionaleffect_95000021" => SpecialAttribute.AdditionalEffect95000021, - "additionaleffect_95000022" => SpecialAttribute.AdditionalEffect95000022, - "additionaleffect_95000023" => SpecialAttribute.AdditionalEffect95000023, - "additionaleffect_95000024" => SpecialAttribute.AdditionalEffect95000024, - "additionaleffect_95000025" => SpecialAttribute.AdditionalEffect95000025, - "additionaleffect_95000026" => SpecialAttribute.AdditionalEffect95000026, - "additionaleffect_95000027" => SpecialAttribute.AdditionalEffect95000027, - "additionaleffect_95000028" => SpecialAttribute.AdditionalEffect95000028, - "additionaleffect_95000029" => SpecialAttribute.AdditionalEffect95000029, - "reduce_recovery_ep_inv" => SpecialAttribute.ReduceRecoveryEpInv, - "improve_stat_wap_u" => SpecialAttribute.MaxWeaponAttack, - "mining_double_reward" => SpecialAttribute.MiningDoubleReward, - "breeding_double_reward" => SpecialAttribute.BreedingDoubleReward, - "gathering_double_reward" => SpecialAttribute.GatheringDoubleReward, - "farming_double_reward" => SpecialAttribute.FarmingDoubleReward, - "blacksmithing_double_reward" => SpecialAttribute.SmithingDoubleReward, - "engraving_double_reward" => SpecialAttribute.EngravingDoubleReward, - "alchemist_double_reward" => SpecialAttribute.AlchemistDoubleReward, - "cooking_double_reward" => SpecialAttribute.CookingDoubleReward, - "mining_double_mastery" => SpecialAttribute.MiningDoubleMastery, - "breeding_double_mastery" => SpecialAttribute.BreedingDoubleMastery, - "gathering_double_mastery" => SpecialAttribute.GatheringDoubleMastery, - "farming_double_mastery" => SpecialAttribute.FarmingDoubleMastery, - "blacksmithing_double_mastery" => SpecialAttribute.SmithingDoubleMastery, - "engraving_double_mastery" => SpecialAttribute.EngravingDoubleMastery, - "alchemist_double_mastery" => SpecialAttribute.AlchemistDoubleMastery, - "cooking_double_mastery" => SpecialAttribute.CookingDoubleMastery, - "improve_chaosraid_wap" => SpecialAttribute.ChaosRaidAttack, - "improve_chaosraid_asp" => SpecialAttribute.ChaosRaidAttackSpeed, - "improve_chaosraid_atp" => SpecialAttribute.ChaosRaidAccuracy, - "improve_chaosraid_hp" => SpecialAttribute.ChaosRaidHp, - "improve_recovery_ball" => SpecialAttribute.RecoveryBall, - "improve_fieldboss_kill_exp" => SpecialAttribute.FieldBossExp, - "improve_fieldboss_kill_drop" => SpecialAttribute.FieldBossDropRate, - "reduce_fieldboss_recive_damage" => SpecialAttribute.ReduceFieldBossReceiveDamage, - "additionaleffect_95000016" => SpecialAttribute.AdditionalEffect95000016, - "improve_pettrap_reward" => SpecialAttribute.PetTrapReward, - "mining_multiaction" => SpecialAttribute.MiningEfficiency, - "breeding_multiaction" => SpecialAttribute.BreedingEfficiency, - "gathering_multiaction" => SpecialAttribute.GatheringEfficiency, - "farming_multiaction" => SpecialAttribute.FarmingEfficiency, - "reduce_damage_by_targetmaxhp" => SpecialAttribute.ReduceDamageByTargetMaxHp, - "reduce_meso_revival_fee" => SpecialAttribute.ReduceMesoRevivalFee, - "improve_riding_run_speed" => SpecialAttribute.RidingRunSpeed, - "improve_dungeon_reward_meso" => SpecialAttribute.DungeonRewardMeso, - "improve_shop_buying_meso" => SpecialAttribute.ShopBuyingMeso, - "improve_itembox_reward_meso" => SpecialAttribute.ItemBoxRewardMeso, - "reduce_remakeoption_fee" => SpecialAttribute.ReduceRemakeOptionFee, - "reduce_airtaxi_fee" => SpecialAttribute.ReduceAirTaxiFee, - "improve_socket_unlock_probability" => SpecialAttribute.SocketUnlockProbability, - "reduce_gemstone_upgrade_fee" => SpecialAttribute.ReduceGemstoneUpgradeFee, - "reduce_pet_remakeoption_fee" => SpecialAttribute.ReducePetRemakeOptionFee, - "improve_riding_speed" => SpecialAttribute.RidingSpeed, - "improve_survival_kill_exp" => SpecialAttribute.ImproveSurvivalKillExp, - "improve_survival_time_exp" => SpecialAttribute.ImproveSurvivalTimeExp, - "offensive_physicaldamage" => SpecialAttribute.OffensivePhysicalDamage, - "offensive_magicaldamage" => SpecialAttribute.OffensiveMagicalDamage, - "reduce_gameitem_socket_unlock_fee" => SpecialAttribute.ReduceGameItemSocketUnlockFee, - _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Invalid SpecialAttribute."), - }; - } - - public static SpecialAttribute SgiTarget(this SpecialAttribute attribute, int sgiTarget) { - if (attribute == SpecialAttribute.NormalNpcDamage) { - return sgiTarget switch { - 2 => SpecialAttribute.LeaderNpcDamage, - 3 => SpecialAttribute.EliteNpcDamage, - 4 => SpecialAttribute.BossNpcDamage, - _ => SpecialAttribute.NormalNpcDamage, - }; - } - - return attribute; - } -} +using Maple2.Model.Enum; + +namespace Maple2.File.Ingest.Utils; + +internal static class AttributeExtensions { + public static BasicAttribute ToBasicAttribute(this string value) { + return value.ToLower() switch { + "str" => BasicAttribute.Strength, + "dex" => BasicAttribute.Dexterity, + "int" => BasicAttribute.Intelligence, + "luk" => BasicAttribute.Luck, + "hp" => BasicAttribute.Health, + "hp_rgp" => BasicAttribute.HpRegen, + "hp_inv" => BasicAttribute.HpRegenInterval, + "sp" => BasicAttribute.Spirit, + "sp_rgp" => BasicAttribute.SpRegen, + "sp_inv" => BasicAttribute.SpRegenInterval, + "ep" => BasicAttribute.Stamina, + "ep_rgp" => BasicAttribute.StaminaRegen, + "ep_inv" => BasicAttribute.StaminaRegenInterval, + "asp" => BasicAttribute.AttackSpeed, + "msp" => BasicAttribute.MovementSpeed, + "atp" => BasicAttribute.Accuracy, + "evp" => BasicAttribute.Evasion, + "cap" => BasicAttribute.CriticalRate, + "cad" => BasicAttribute.CriticalDamage, + "car" => BasicAttribute.CriticalEvasion, + "ndd" => BasicAttribute.Defense, + "abp" => BasicAttribute.PerfectGuard, + "jmp" => BasicAttribute.JumpHeight, + "pap" => BasicAttribute.PhysicalAtk, + "map" => BasicAttribute.MagicalAtk, + "par" => BasicAttribute.PhysicalRes, + "mar" => BasicAttribute.MagicalRes, + "wapmin" => BasicAttribute.MinWeaponAtk, + "wapmax" => BasicAttribute.MaxWeaponAtk, + "dmg" => BasicAttribute.Damage, + "pen" => BasicAttribute.Piercing, + "rmsp" => BasicAttribute.MountSpeed, + "bap" => BasicAttribute.BonusAtk, + "bap_pet" => BasicAttribute.PetBonusAtk, + _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Invalid BasicAttribute."), + }; + } + + public static SpecialAttribute ToSpecialAttribute(this string value) { + return value.ToLower() switch { + "seg" => SpecialAttribute.Experience, + "smd" => SpecialAttribute.Meso, + "sss" => SpecialAttribute.SwimSpeed, + "dashdistance" => SpecialAttribute.DashDistance, + // "spd" => 4, + // "sid" => 5, + "finaladditionaldamage" => SpecialAttribute.TotalDamage, + "cri" => SpecialAttribute.CriticalDamage, + "sgi" => SpecialAttribute.NormalNpcDamage, + "sgi_leader" => SpecialAttribute.LeaderNpcDamage, + "sgi_elite" => SpecialAttribute.EliteNpcDamage, + "sgi_boss" => SpecialAttribute.BossNpcDamage, + "killhprestore" => SpecialAttribute.HpOnKill, + "killsprestore" => SpecialAttribute.SpiritOnKill, + "killeprestore" => SpecialAttribute.StaminaOnKill, + "heal" => SpecialAttribute.RecoveryBonus, + "receivedhealincrease" => SpecialAttribute.BonusRecoveryFromAlly, + "icedamage" => SpecialAttribute.IceDamage, + "firedamage" => SpecialAttribute.FireDamage, + "darkdamage" => SpecialAttribute.DarkDamage, + "lightdamage" => SpecialAttribute.HolyDamage, + "poisondamage" => SpecialAttribute.PoisonDamage, + "thunderdamage" => SpecialAttribute.ElectricDamage, + "nddincrease" => SpecialAttribute.MeleeDamage, + "lddincrease" => SpecialAttribute.RangedDamage, + "parpen" => SpecialAttribute.PhysicalPiercing, + "marpen" => SpecialAttribute.MagicalPiercing, + "icedamagereduce" => SpecialAttribute.ReduceIceDamage, + "firedamagereduce" => SpecialAttribute.ReduceFireDamage, + "darkdamagereduce" => SpecialAttribute.ReduceDarkDamage, + "lightdamagereduce" => SpecialAttribute.ReduceHolyDamage, + "poisondamagereduce" => SpecialAttribute.ReducePoisonDamage, + "thunderdamagereduce" => SpecialAttribute.ReduceElectricDamage, + "stunreduce" => SpecialAttribute.ReduceStun, + "conditionreduce" => SpecialAttribute.ReduceDebuff, + "skillcooldown" => SpecialAttribute.ReduceCooldown, + "neardistancedamagereduce" => SpecialAttribute.ReduceMeleeDamage, + "longdistancedamagereduce" => SpecialAttribute.ReduceRangedDamage, + "knockbackreduce" => SpecialAttribute.ReduceKnockBack, + "stunprocndd" => SpecialAttribute.MeleeStun, + "stunprocldd" => SpecialAttribute.RangedStun, + "knockbackprocndd" => SpecialAttribute.MeleeKnockBack, + "knockbackprocldd" => SpecialAttribute.RangedKnockBack, + "snareprocndd" => SpecialAttribute.MeleeImmobilize, + "snareprocldd" => SpecialAttribute.RangedImmobilize, + "aoeprocndd" => SpecialAttribute.MeleeSplashDamage, + "aoeprocldd" => SpecialAttribute.RangedSplashDamage, + "npckilldropitemincrate" => SpecialAttribute.DropRate, + "seg_questreward" => SpecialAttribute.QuestExp, + "smd_questreward" => SpecialAttribute.QuestMeso, + "seg_fishingreward" => SpecialAttribute.FishingExp, + "seg_arcadereward" => SpecialAttribute.ArcadeExp, + "seg_playinstrumentreward" => SpecialAttribute.PlayInstrumentExp, + "invoke_effect1" => SpecialAttribute.InvokeEffect1, + "invoke_effect2" => SpecialAttribute.InvokeEffect2, + "invoke_effect3" => SpecialAttribute.InvokeEffect3, + "pvpdamageincrease" => SpecialAttribute.PvpDamage, + "pvpdamagereduce" => SpecialAttribute.ReducePvpDamage, + "improveguildexp" => SpecialAttribute.GuildExp, + "improveguildcoin" => SpecialAttribute.GuildCoin, + "improvemassiveeventbexpball" => SpecialAttribute.MassiveEventExpBall, + "reduce_meso_trade_fee" => SpecialAttribute.ReduceMesoTradeFee, + "reduce_enchant_matrial_fee" => SpecialAttribute.ReduceEnchantMaterialFee, + "reduce_merat_revival_fee" => SpecialAttribute.ReduceMeretRevivalFee, + "improve_mining_reward_item" => SpecialAttribute.MiningRewardItem, + "improve_breeding_reward_item" => SpecialAttribute.BreedingRewardItem, + "improve_blacksmithing_reward_mastery" => SpecialAttribute.SmithingRewardMastery, + "improve_engraving_reward_mastery" => SpecialAttribute.EngravingRewardMastery, + "improve_gathering_reward_item" => SpecialAttribute.GatheringRewardItem, + "improve_farming_reward_item" => SpecialAttribute.FarmingRewardItem, + "improve_alchemist_reward_mastery" => SpecialAttribute.AlchemistRewardMastery, + "improve_cooking_reward_mastery" => SpecialAttribute.CookingRewardMastery, + "improve_acquire_gathering_exp" => SpecialAttribute.AcquireGatheringExp, + "skill_levelup_tier_1" => SpecialAttribute.SkillLevelUpTier1, + "skill_levelup_tier_2" => SpecialAttribute.SkillLevelUpTier2, + "skill_levelup_tier_3" => SpecialAttribute.SkillLevelUpTier3, + "skill_levelup_tier_4" => SpecialAttribute.SkillLevelUpTier4, + "skill_levelup_tier_5" => SpecialAttribute.SkillLevelUpTier5, + "skill_levelup_tier_6" => SpecialAttribute.SkillLevelUpTier6, + "skill_levelup_tier_7" => SpecialAttribute.SkillLevelUpTier7, + "skill_levelup_tier_8" => SpecialAttribute.SkillLevelUpTier8, + "skill_levelup_tier_9" => SpecialAttribute.SkillLevelUpTier9, + "skill_levelup_tier_10" => SpecialAttribute.SkillLevelUpTier10, + "skill_levelup_tier_11" => SpecialAttribute.SkillLevelUpTier11, + "skill_levelup_tier_12" => SpecialAttribute.SkillLevelUpTier12, + "skill_levelup_tier_13" => SpecialAttribute.SkillLevelUpTier13, + "skill_levelup_tier_14" => SpecialAttribute.SkillLevelUpTier14, + "improve_massive_ox_exp" => SpecialAttribute.MassiveOxExp, + "improve_massive_trapmaster_exp" => SpecialAttribute.MassiveTrapMasterExp, + "improve_massive_finalsurvival_exp" => SpecialAttribute.MassiveFinalSurvivalExp, + "improve_massive_crazyrunner_exp" => SpecialAttribute.MassiveCrazyRunnerExp, + "improve_massive_sh_crazyrunner_exp" => SpecialAttribute.MassiveShCrazyRunnerExp, + "improve_massive_escape_exp" => SpecialAttribute.MassiveEscapeExp, + "improve_massive_springbeach_exp" => SpecialAttribute.MassiveSpringBeachExp, + "improve_massive_dancedance_exp" => SpecialAttribute.MassiveDanceDanceExp, + "improve_massive_ox_msp" => SpecialAttribute.MassiveOxSpeed, + "improve_massive_trapmaster_msp" => SpecialAttribute.MassiveTrapMasterSpeed, + "improve_massive_finalsurvival_msp" => SpecialAttribute.MassiveFinalSurvivalSpeed, + "improve_massive_crazyrunner_msp" => SpecialAttribute.MassiveCrazyRunnerSpeed, + "improve_massive_sh_crazyrunner_msp" => SpecialAttribute.MassiveShCrazyRunnerSpeed, + "improve_massive_escape_msp" => SpecialAttribute.MassiveEscapeSpeed, + "improve_massive_springbeach_msp" => SpecialAttribute.MassiveSpringBeachSpeed, + "improve_massive_dancedance_msp" => SpecialAttribute.MassiveDanceDanceSpeed, + "npc_hit_reward_sp_ball" => SpecialAttribute.NpcHitRewardSpBall, + "npc_hit_reward_ep_ball" => SpecialAttribute.NpcHitRewardEpBall, + "improve_honor_token" => SpecialAttribute.HonorToken, + "improve_pvp_exp" => SpecialAttribute.PvpExp, + "improve_darkstream_damage" => SpecialAttribute.DarkStreamDamage, + "reduce_darkstream_recive_damage" => SpecialAttribute.ReduceDarkStreamReceiveDamage, + "improve_darkstream_evp" => SpecialAttribute.DarkStreamEvp, + "fishing_double_mastery" => SpecialAttribute.FishingDoubleMastery, + "playinstrument_double_mastery" => SpecialAttribute.PlayInstrumentDoubleMastery, + "complete_fieldmission_msp" => SpecialAttribute.CompleteFieldMissionSpeed, + "improve_glide_vertical_velocity" => SpecialAttribute.GlideVerticalVelocity, + "additionaleffect_95000018" => SpecialAttribute.AdditionalEffect95000018, + "additionaleffect_95000012" => SpecialAttribute.AdditionalEffect95000012, + "additionaleffect_95000014" => SpecialAttribute.AdditionalEffect95000014, + "additionaleffect_95000020" => SpecialAttribute.AdditionalEffect95000020, + "additionaleffect_95000021" => SpecialAttribute.AdditionalEffect95000021, + "additionaleffect_95000022" => SpecialAttribute.AdditionalEffect95000022, + "additionaleffect_95000023" => SpecialAttribute.AdditionalEffect95000023, + "additionaleffect_95000024" => SpecialAttribute.AdditionalEffect95000024, + "additionaleffect_95000025" => SpecialAttribute.AdditionalEffect95000025, + "additionaleffect_95000026" => SpecialAttribute.AdditionalEffect95000026, + "additionaleffect_95000027" => SpecialAttribute.AdditionalEffect95000027, + "additionaleffect_95000028" => SpecialAttribute.AdditionalEffect95000028, + "additionaleffect_95000029" => SpecialAttribute.AdditionalEffect95000029, + "reduce_recovery_ep_inv" => SpecialAttribute.ReduceRecoveryEpInv, + "improve_stat_wap_u" => SpecialAttribute.MaxWeaponAttack, + "mining_double_reward" => SpecialAttribute.MiningDoubleReward, + "breeding_double_reward" => SpecialAttribute.BreedingDoubleReward, + "gathering_double_reward" => SpecialAttribute.GatheringDoubleReward, + "farming_double_reward" => SpecialAttribute.FarmingDoubleReward, + "blacksmithing_double_reward" => SpecialAttribute.SmithingDoubleReward, + "engraving_double_reward" => SpecialAttribute.EngravingDoubleReward, + "alchemist_double_reward" => SpecialAttribute.AlchemistDoubleReward, + "cooking_double_reward" => SpecialAttribute.CookingDoubleReward, + "mining_double_mastery" => SpecialAttribute.MiningDoubleMastery, + "breeding_double_mastery" => SpecialAttribute.BreedingDoubleMastery, + "gathering_double_mastery" => SpecialAttribute.GatheringDoubleMastery, + "farming_double_mastery" => SpecialAttribute.FarmingDoubleMastery, + "blacksmithing_double_mastery" => SpecialAttribute.SmithingDoubleMastery, + "engraving_double_mastery" => SpecialAttribute.EngravingDoubleMastery, + "alchemist_double_mastery" => SpecialAttribute.AlchemistDoubleMastery, + "cooking_double_mastery" => SpecialAttribute.CookingDoubleMastery, + "improve_chaosraid_wap" => SpecialAttribute.ChaosRaidAttack, + "improve_chaosraid_asp" => SpecialAttribute.ChaosRaidAttackSpeed, + "improve_chaosraid_atp" => SpecialAttribute.ChaosRaidAccuracy, + "improve_chaosraid_hp" => SpecialAttribute.ChaosRaidHp, + "improve_recovery_ball" => SpecialAttribute.RecoveryBall, + "improve_fieldboss_kill_exp" => SpecialAttribute.FieldBossExp, + "improve_fieldboss_kill_drop" => SpecialAttribute.FieldBossDropRate, + "reduce_fieldboss_recive_damage" => SpecialAttribute.ReduceFieldBossReceiveDamage, + "additionaleffect_95000016" => SpecialAttribute.AdditionalEffect95000016, + "improve_pettrap_reward" => SpecialAttribute.PetTrapReward, + "mining_multiaction" => SpecialAttribute.MiningEfficiency, + "breeding_multiaction" => SpecialAttribute.BreedingEfficiency, + "gathering_multiaction" => SpecialAttribute.GatheringEfficiency, + "farming_multiaction" => SpecialAttribute.FarmingEfficiency, + "reduce_damage_by_targetmaxhp" => SpecialAttribute.ReduceDamageByTargetMaxHp, + "reduce_meso_revival_fee" => SpecialAttribute.ReduceMesoRevivalFee, + "improve_riding_run_speed" => SpecialAttribute.RidingRunSpeed, + "improve_dungeon_reward_meso" => SpecialAttribute.DungeonRewardMeso, + "improve_shop_buying_meso" => SpecialAttribute.ShopBuyingMeso, + "improve_itembox_reward_meso" => SpecialAttribute.ItemBoxRewardMeso, + "reduce_remakeoption_fee" => SpecialAttribute.ReduceRemakeOptionFee, + "reduce_airtaxi_fee" => SpecialAttribute.ReduceAirTaxiFee, + "improve_socket_unlock_probability" => SpecialAttribute.SocketUnlockProbability, + "reduce_gemstone_upgrade_fee" => SpecialAttribute.ReduceGemstoneUpgradeFee, + "reduce_pet_remakeoption_fee" => SpecialAttribute.ReducePetRemakeOptionFee, + "improve_riding_speed" => SpecialAttribute.RidingSpeed, + "improve_survival_kill_exp" => SpecialAttribute.ImproveSurvivalKillExp, + "improve_survival_time_exp" => SpecialAttribute.ImproveSurvivalTimeExp, + "offensive_physicaldamage" => SpecialAttribute.OffensivePhysicalDamage, + "offensive_magicaldamage" => SpecialAttribute.OffensiveMagicalDamage, + "reduce_gameitem_socket_unlock_fee" => SpecialAttribute.ReduceGameItemSocketUnlockFee, + _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Invalid SpecialAttribute."), + }; + } + + public static SpecialAttribute SgiTarget(this SpecialAttribute attribute, int sgiTarget) { + if (attribute == SpecialAttribute.NormalNpcDamage) { + return sgiTarget switch { + 2 => SpecialAttribute.LeaderNpcDamage, + 3 => SpecialAttribute.EliteNpcDamage, + 4 => SpecialAttribute.BossNpcDamage, + _ => SpecialAttribute.NormalNpcDamage, + }; + } + + return attribute; + } +} diff --git a/Maple2.File.Ingest/Utils/DotRecast.cs b/Maple2.File.Ingest/Utils/DotRecast.cs index ab9fc8cd8..6a833062e 100644 --- a/Maple2.File.Ingest/Utils/DotRecast.cs +++ b/Maple2.File.Ingest/Utils/DotRecast.cs @@ -1,129 +1,129 @@ -using DotRecast.Core.Collections; -using DotRecast.Core.Numerics; -using DotRecast.Recast; -using DotRecast.Recast.Geom; - -namespace Maple2.File.Ingest.Utils; -internal class InputGeomProvider : IInputGeomProvider { - public readonly float[] vertices; - - public readonly int[] faces; - - public readonly float[] normals; - - private RcVec3f bmin; - - private RcVec3f bmax; - - private readonly RcTriMesh mesh; - private readonly List offMeshConnections; - private readonly List convexVolumes; - - - public InputGeomProvider(List verts, List tris) { - vertices = MapVertices(verts); - faces = MapFaces(tris); - - normals = new float[faces.Length]; - CalculateNormals(); - bmin = RcVecUtils.Create(vertices); - bmax = RcVecUtils.Create(vertices); - for (int i = 1; i < vertices.Length / 3; i++) { - bmin = RcVecUtils.Min(bmin, vertices, i * 3); - bmax = RcVecUtils.Max(bmax, vertices, i * 3); - } - - mesh = new RcTriMesh(vertices, faces); - offMeshConnections = []; - convexVolumes = []; - } - - private void CalculateNormals() { - for (int i = 0; i < faces.Length; i += 3) { - int num = faces[i] * 3; - int num2 = faces[i + 1] * 3; - int num3 = faces[i + 2] * 3; - RcVec3f rcVec3f = default; - RcVec3f rcVec3f2 = default; - rcVec3f.X = vertices[num2] - vertices[num]; - rcVec3f.Y = vertices[num2 + 1] - vertices[num + 1]; - rcVec3f.Z = vertices[num2 + 2] - vertices[num + 2]; - rcVec3f2.X = vertices[num3] - vertices[num]; - rcVec3f2.Y = vertices[num3 + 1] - vertices[num + 1]; - rcVec3f2.Z = vertices[num3 + 2] - vertices[num + 2]; - normals[i] = rcVec3f.Y * rcVec3f2.Z - rcVec3f.Z * rcVec3f2.Y; - normals[i + 1] = rcVec3f.Z * rcVec3f2.X - rcVec3f.X * rcVec3f2.Z; - normals[i + 2] = rcVec3f.X * rcVec3f2.Y - rcVec3f.Y * rcVec3f2.X; - float num4 = MathF.Sqrt(normals[i] * normals[i] + normals[i + 1] * normals[i + 1] + normals[i + 2] * normals[i + 2]); - if (num4 > 0f) { - num4 = 1f / num4; - normals[i] *= num4; - normals[i + 1] *= num4; - normals[i + 2] *= num4; - } - } - } - - private static int[] MapFaces(List meshFaces) { - int[] array = new int[meshFaces.Count]; - for (int i = 0; i < array.Length; i++) { - array[i] = meshFaces[i]; - } - - return array; - } - - private static float[] MapVertices(List vertexPositions) { - float[] array = new float[vertexPositions.Count]; - for (int i = 0; i < array.Length; i++) { - array[i] = vertexPositions[i]; - } - - return array; - } - - public List GetOffMeshConnections() { - return offMeshConnections; - } - - public void AddOffMeshConnection(RcVec3f start, RcVec3f end, float radius, bool bidir, int area, int flags) { - offMeshConnections.Add(new RcOffMeshConnection(start, end, radius, true, area, flags)); - } - - public void RemoveOffMeshConnections(Predicate filter) { - offMeshConnections.RemoveAll(filter); - } - - public void AddConvexVolume(float[] verts, float minh, float maxh, RcAreaModification areaMod) { - AddConvexVolume(new RcConvexVolume { - verts = verts, - hmin = minh, - hmax = maxh, - areaMod = areaMod, - }); - } - - public void AddConvexVolume(RcConvexVolume volume) { - convexVolumes.Add(volume); - } - - public IList ConvexVolumes() { - return convexVolumes; - } - - public RcTriMesh GetMesh() { - return mesh; - } - - public RcVec3f GetMeshBoundsMax() { - return bmax; - } - - public RcVec3f GetMeshBoundsMin() { - return bmin; - } - - public IEnumerable Meshes() { - return RcImmutableArray.Create(mesh); - } -} +using DotRecast.Core.Collections; +using DotRecast.Core.Numerics; +using DotRecast.Recast; +using DotRecast.Recast.Geom; + +namespace Maple2.File.Ingest.Utils; +internal class InputGeomProvider : IInputGeomProvider { + public readonly float[] vertices; + + public readonly int[] faces; + + public readonly float[] normals; + + private RcVec3f bmin; + + private RcVec3f bmax; + + private readonly RcTriMesh mesh; + private readonly List offMeshConnections; + private readonly List convexVolumes; + + + public InputGeomProvider(List verts, List tris) { + vertices = MapVertices(verts); + faces = MapFaces(tris); + + normals = new float[faces.Length]; + CalculateNormals(); + bmin = RcVecUtils.Create(vertices); + bmax = RcVecUtils.Create(vertices); + for (int i = 1; i < vertices.Length / 3; i++) { + bmin = RcVecUtils.Min(bmin, vertices, i * 3); + bmax = RcVecUtils.Max(bmax, vertices, i * 3); + } + + mesh = new RcTriMesh(vertices, faces); + offMeshConnections = []; + convexVolumes = []; + } + + private void CalculateNormals() { + for (int i = 0; i < faces.Length; i += 3) { + int num = faces[i] * 3; + int num2 = faces[i + 1] * 3; + int num3 = faces[i + 2] * 3; + RcVec3f rcVec3f = default; + RcVec3f rcVec3f2 = default; + rcVec3f.X = vertices[num2] - vertices[num]; + rcVec3f.Y = vertices[num2 + 1] - vertices[num + 1]; + rcVec3f.Z = vertices[num2 + 2] - vertices[num + 2]; + rcVec3f2.X = vertices[num3] - vertices[num]; + rcVec3f2.Y = vertices[num3 + 1] - vertices[num + 1]; + rcVec3f2.Z = vertices[num3 + 2] - vertices[num + 2]; + normals[i] = rcVec3f.Y * rcVec3f2.Z - rcVec3f.Z * rcVec3f2.Y; + normals[i + 1] = rcVec3f.Z * rcVec3f2.X - rcVec3f.X * rcVec3f2.Z; + normals[i + 2] = rcVec3f.X * rcVec3f2.Y - rcVec3f.Y * rcVec3f2.X; + float num4 = MathF.Sqrt(normals[i] * normals[i] + normals[i + 1] * normals[i + 1] + normals[i + 2] * normals[i + 2]); + if (num4 > 0f) { + num4 = 1f / num4; + normals[i] *= num4; + normals[i + 1] *= num4; + normals[i + 2] *= num4; + } + } + } + + private static int[] MapFaces(List meshFaces) { + int[] array = new int[meshFaces.Count]; + for (int i = 0; i < array.Length; i++) { + array[i] = meshFaces[i]; + } + + return array; + } + + private static float[] MapVertices(List vertexPositions) { + float[] array = new float[vertexPositions.Count]; + for (int i = 0; i < array.Length; i++) { + array[i] = vertexPositions[i]; + } + + return array; + } + + public List GetOffMeshConnections() { + return offMeshConnections; + } + + public void AddOffMeshConnection(RcVec3f start, RcVec3f end, float radius, bool bidir, int area, int flags) { + offMeshConnections.Add(new RcOffMeshConnection(start, end, radius, true, area, flags)); + } + + public void RemoveOffMeshConnections(Predicate filter) { + offMeshConnections.RemoveAll(filter); + } + + public void AddConvexVolume(float[] verts, float minh, float maxh, RcAreaModification areaMod) { + AddConvexVolume(new RcConvexVolume { + verts = verts, + hmin = minh, + hmax = maxh, + areaMod = areaMod, + }); + } + + public void AddConvexVolume(RcConvexVolume volume) { + convexVolumes.Add(volume); + } + + public IList ConvexVolumes() { + return convexVolumes; + } + + public RcTriMesh GetMesh() { + return mesh; + } + + public RcVec3f GetMeshBoundsMax() { + return bmax; + } + + public RcVec3f GetMeshBoundsMin() { + return bmin; + } + + public IEnumerable Meshes() { + return RcImmutableArray.Create(mesh); + } +} diff --git a/Maple2.File.Ingest/Utils/NavmeshHash.cs b/Maple2.File.Ingest/Utils/NavmeshHash.cs index a25e1447b..9e477428e 100644 --- a/Maple2.File.Ingest/Utils/NavmeshHash.cs +++ b/Maple2.File.Ingest/Utils/NavmeshHash.cs @@ -1,41 +1,41 @@ -using System.Security.Cryptography; -using Maple2.Tools; - -namespace Maple2.File.Ingest.Utils; - -public static class NavmeshHash { - public static bool HasValidHash(string filename) { - string hashPath = Path.Combine(Paths.NAVMESH_HASH_DIR, $"{filename}-hash"); - - if (!System.IO.File.Exists(hashPath)) { - return false; - } - - string currentHash = System.IO.File.ReadAllText(hashPath); - string newHash = GetHash(filename); - - return currentHash.Equals(newHash); - } - - public static void WriteHash(string filename) { - string hashPath = Path.Combine(Paths.NAVMESH_HASH_DIR, $"{filename}-hash"); - - string newHash = GetHash(filename); - - System.IO.File.WriteAllText(hashPath, newHash); - } - - private static string GetHash(string filename) { - string filepath = Path.Combine(Paths.NAVMESH_DIR, $"{filename}.navmesh"); - - if (!System.IO.File.Exists(filepath)) { - return ""; - } - - using MD5 md5 = MD5.Create(); - using FileStream stream = System.IO.File.OpenRead(filepath); - - byte[] hash = md5.ComputeHash(stream); - return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); - } -} +using System.Security.Cryptography; +using Maple2.Tools; + +namespace Maple2.File.Ingest.Utils; + +public static class NavmeshHash { + public static bool HasValidHash(string filename) { + string hashPath = Path.Combine(Paths.NAVMESH_HASH_DIR, $"{filename}-hash"); + + if (!System.IO.File.Exists(hashPath)) { + return false; + } + + string currentHash = System.IO.File.ReadAllText(hashPath); + string newHash = GetHash(filename); + + return currentHash.Equals(newHash); + } + + public static void WriteHash(string filename) { + string hashPath = Path.Combine(Paths.NAVMESH_HASH_DIR, $"{filename}-hash"); + + string newHash = GetHash(filename); + + System.IO.File.WriteAllText(hashPath, newHash); + } + + private static string GetHash(string filename) { + string filepath = Path.Combine(Paths.NAVMESH_DIR, $"{filename}.navmesh"); + + if (!System.IO.File.Exists(filepath)) { + return ""; + } + + using MD5 md5 = MD5.Create(); + using FileStream stream = System.IO.File.OpenRead(filepath); + + byte[] hash = md5.ComputeHash(stream); + return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); + } +} diff --git a/Maple2.File.Ingest/Utils/StringTable.cs b/Maple2.File.Ingest/Utils/StringTable.cs index bdfed022d..2d399f6de 100644 --- a/Maple2.File.Ingest/Utils/StringTable.cs +++ b/Maple2.File.Ingest/Utils/StringTable.cs @@ -1,27 +1,27 @@ -using System.Xml; -using Maple2.File.IO; - -namespace Maple2.File.Ingest.Utils; - -public class StringTable { - public readonly Dictionary Table; - - public StringTable(M2dReader xmlReader) { - Table = new Dictionary(); - - XmlDocument doc = xmlReader.GetXmlDocument(xmlReader.GetEntry("en/stringcommon.xml")); - XmlNodeList? nodes = doc.SelectNodes("ms2/key"); - if (nodes == null) { - throw new InvalidOperationException("No nodes found in stringcommon.xml"); - } - foreach (XmlNode node in nodes) { - string? id = node.Attributes?["id"]?.Value; - string value = node.Attributes?["value"]?.Value ?? ""; - if (id == null) { - continue; - } - - Table[id] = value; - } - } -} +using System.Xml; +using Maple2.File.IO; + +namespace Maple2.File.Ingest.Utils; + +public class StringTable { + public readonly Dictionary Table; + + public StringTable(M2dReader xmlReader) { + Table = new Dictionary(); + + XmlDocument doc = xmlReader.GetXmlDocument(xmlReader.GetEntry("en/stringcommon.xml")); + XmlNodeList? nodes = doc.SelectNodes("ms2/key"); + if (nodes == null) { + throw new InvalidOperationException("No nodes found in stringcommon.xml"); + } + foreach (XmlNode node in nodes) { + string? id = node.Attributes?["id"]?.Value; + string value = node.Attributes?["value"]?.Value ?? ""; + if (id == null) { + continue; + } + + Table[id] = value; + } + } +} diff --git a/Maple2.File.Ingest/Utils/TriggerDefinitionOverride.cs b/Maple2.File.Ingest/Utils/TriggerDefinitionOverride.cs index dde5581ea..79b4d9bc0 100644 --- a/Maple2.File.Ingest/Utils/TriggerDefinitionOverride.cs +++ b/Maple2.File.Ingest/Utils/TriggerDefinitionOverride.cs @@ -1,1173 +1,1173 @@ -using System.Diagnostics; - -namespace Maple2.File.Ingest.Utils; - -internal class TriggerDefinitionOverride { - private const string Required = ""; - - // Function Name - public readonly string Name; - // Docstring description - public readonly string Description = string.Empty; - - // Parameter Names - public Dictionary Names { get; init; } = null!; - - // Parameter Types - public Dictionary Types { get; init; } = null!; - - // Comparison Operation (Only for Conditions) - public (string Field, string Op, string Default) Compare { get; init; } - - public string? FunctionSplitter { get; init; } - public Dictionary FunctionLookup { get; init; } = null!; - - private TriggerDefinitionOverride(string name, string? splitter = null) { - Name = name; - FunctionSplitter = splitter; - } - - public static readonly Dictionary ActionOverride = new Dictionary(); - public static readonly Dictionary ConditionOverride = new Dictionary(); - - static TriggerDefinitionOverride() { - // Action Override - ActionOverride["add_balloon_talk"] = new TriggerDefinitionOverride("add_balloon_talk") { - Names = BuildNameOverride(("spawnPointID", "spawnId")), - Types = BuildTypeOverride(("spawnId", null), ("duration", null), ("delayTick", null), ("npcID", null)), - }; - ActionOverride["add_buff"] = new TriggerDefinitionOverride("add_buff") { - Names = BuildNameOverride(("arg1", "boxIds"), ("arg2", "skillId"), ("arg3", "level"), ("arg4", "ignorePlayer"), ("arg5", "isSkillSet")), - Types = BuildTypeOverride(("boxIds", Required), ("skillId", Required), ("level", Required), ("ignorePlayer", "True"), ("isSkillSet", "True")), - }; - ActionOverride["add_cinematic_talk"] = new TriggerDefinitionOverride("add_cinematic_talk") { - Names = BuildNameOverride(("npcID", "npcId"), ("illustID", "illustId"), ("illust", "illustId"), ("delay", "delayTick")), - Types = BuildTypeOverride(("npcId", Required), ("duration", null), ("align", null), ("delayTick", null)), - }; - ActionOverride["add_effect_nif"] = new TriggerDefinitionOverride("add_effect_nif") { - Names = BuildNameOverride(("spawnPointID", "spawnId")), - Types = BuildTypeOverride(("spawnId", Required), ("isOutline", null), ("scale", null), ("rotateZ", null)), - }; - ActionOverride["add_user_value"] = new TriggerDefinitionOverride("add_user_value") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("value", Required)), - }; - ActionOverride["allocate_battlefield_points"] = new TriggerDefinitionOverride("allocate_battlefield_points") { - Names = BuildNameOverride(("arg1", "boxId"), ("arg2", "points")), - Types = BuildTypeOverride(("boxId", Required), ("points", Required)), - }; - ActionOverride["announce"] = new TriggerDefinitionOverride("announce") { - Names = BuildNameOverride(("arg1", "type"), ("arg2", "content")), - Types = BuildTypeOverride(("type", null), ("content", Required), ("arg3", null)), - }; - ActionOverride["arcade_boom_boom_ocean"] = new TriggerDefinitionOverride(string.Empty) { - FunctionSplitter = "type", - FunctionLookup = new Dictionary { - ["StartGame"] = new TriggerDefinitionOverride("arcade_boom_boom_ocean_start_game", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("lifeCount", Required)), - }, - ["EndGame"] = new TriggerDefinitionOverride("arcade_boom_boom_ocean_end_game", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }, - ["SetSkillScore"] = new TriggerDefinitionOverride("arcade_boom_boom_ocean_set_skill_score", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("type", Required), ("id", Required), ("score", Required)), - }, - ["StartRound"] = new TriggerDefinitionOverride("arcade_boom_boom_ocean_start_round", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("type", Required), ("round", Required), ("roundDuration", Required), ("timeScoreRate", Required)), - }, - ["ClearRound"] = new TriggerDefinitionOverride("arcade_boom_boom_ocean_clear_round", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("type", Required), ("round", Required)), - }, - }, - }; - ActionOverride["arcade_spring_farm"] = new TriggerDefinitionOverride("") { - FunctionSplitter = "type", - FunctionLookup = new Dictionary { - ["StartGame"] = new TriggerDefinitionOverride("arcade_spring_farm_start_game", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("lifeCount", Required)), - }, - ["EndGame"] = new TriggerDefinitionOverride("arcade_spring_farm_end_game", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }, - ["SetInteractScore"] = new TriggerDefinitionOverride("arcade_spring_farm_set_interact_score", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("id", Required), ("score", Required)), - }, - ["SpawnMonster"] = new TriggerDefinitionOverride("arcade_spring_farm_spawn_monster", splitter: "type") { - Names = BuildNameOverride(("spawnID", "spawnIds")), - Types = BuildTypeOverride(("spawnIds", Required), ("score", Required)), - }, - ["StartRound"] = new TriggerDefinitionOverride("arcade_spring_farm_start_round", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("uiDuration", Required), ("round", Required), ("roundDuration", Required), ("timeScoreType", Required), ("timeScoreRate", Required)), - }, - ["ClearRound"] = new TriggerDefinitionOverride("arcade_spring_farm_clear_round", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("round", Required)), - }, - }, - }; - ActionOverride["arcade_three_two_one"] = new TriggerDefinitionOverride("") { - FunctionSplitter = "type", - FunctionLookup = new Dictionary { - ["StartGame"] = new TriggerDefinitionOverride("arcade_three_two_one_start_game", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("lifeCount", Required), ("initScore", Required)), - }, - ["EndGame"] = new TriggerDefinitionOverride("arcade_three_two_one_end_game", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }, - ["StartRound"] = new TriggerDefinitionOverride("arcade_three_two_one_start_round", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("uiDuration", Required), ("round", Required)), - }, - ["ResultRound"] = new TriggerDefinitionOverride("arcade_three_two_one_result_round", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("resultDirection", Required)), - }, - ["ResultRound2"] = new TriggerDefinitionOverride("arcade_three_two_one_result_round2", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("round", Required)), - }, - ["ClearRound"] = new TriggerDefinitionOverride("arcade_three_two_one_clear_round", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("round", Required)), - }, - }, - }; - ActionOverride["arcade_three_two_one2"] = new TriggerDefinitionOverride("") { - FunctionSplitter = "type", - FunctionLookup = new Dictionary { - ["StartGame"] = new TriggerDefinitionOverride("arcade_three_two_one2_start_game", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("lifeCount", Required), ("initScore", Required)), - }, - ["EndGame"] = new TriggerDefinitionOverride("arcade_three_two_one2_end_game", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }, - ["StartRound"] = new TriggerDefinitionOverride("arcade_three_two_one2_start_round", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("uiDuration", Required), ("round", Required)), - }, - ["ResultRound"] = new TriggerDefinitionOverride("arcade_three_two_one2_result_round", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("resultDirection", Required)), - }, - ["ResultRound2"] = new TriggerDefinitionOverride("arcade_three_two_one2_result_round2", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("round", Required)), - }, - ["ClearRound"] = new TriggerDefinitionOverride("arcade_three_two_one2_clear_round", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("round", Required)), - }, - }, - }; - ActionOverride["arcade_three_two_one3"] = new TriggerDefinitionOverride("") { - FunctionSplitter = "type", - FunctionLookup = new Dictionary { - ["StartGame"] = new TriggerDefinitionOverride("arcade_three_two_one3_start_game", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("lifeCount", Required), ("initScore", Required)), - }, - ["EndGame"] = new TriggerDefinitionOverride("arcade_three_two_one3_end_game", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }, - ["StartRound"] = new TriggerDefinitionOverride("arcade_three_two_one3_start_round", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("uiDuration", Required), ("round", Required)), - }, - ["ResultRound"] = new TriggerDefinitionOverride("arcade_three_two_one3_result_round", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("resultDirection", Required)), - }, - ["ResultRound2"] = new TriggerDefinitionOverride("arcade_three_two_one3_result_round2", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("round", Required)), - }, - ["ClearRound"] = new TriggerDefinitionOverride("arcade_three_two_one3_clear_round", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("round", Required)), - }, - }, - }; - ActionOverride["change_background"] = new TriggerDefinitionOverride("change_background") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("dds", Required)), - }; - ActionOverride["change_monster"] = new TriggerDefinitionOverride("change_monster") { - Names = BuildNameOverride(("arg1", "fromSpawnId"), ("arg2", "toSpawnId")), - Types = BuildTypeOverride(("fromSpawnId", Required), ("toSpawnId", Required)), - }; - ActionOverride["close_cinematic"] = new TriggerDefinitionOverride("close_cinematic") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ActionOverride["create_field_game"] = new TriggerDefinitionOverride("create_field_game") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("type", Required), ("reset", null)), - }; - ActionOverride["create_item"] = new TriggerDefinitionOverride("create_item") { - Names = BuildNameOverride(("arg1", "spawnIds"), ("arg2", "triggerId"), ("arg3", "itemId")), - Types = BuildTypeOverride(("spawnIds", Required), ("triggerId", null), ("itemId", null), ("arg5", null)), - }; - ActionOverride["spawn_monster"] = new TriggerDefinitionOverride("spawn_monster") { - Names = BuildNameOverride(("arg1", "spawnIds"), ("arg2", "autoTarget"), ("agr2", "autoTarget"), ("arg", "autoTarget"), ("arg3", "delay")), - Types = BuildTypeOverride(("spawnIds", Required), ("autoTarget", "True"), ("delay", null)), - }; - ActionOverride["create_widget"] = new TriggerDefinitionOverride("create_widget") { - Names = BuildNameOverride(("arg1", "type")), - Types = BuildTypeOverride(("type", Required)), - }; - ActionOverride["dark_stream"] = new TriggerDefinitionOverride("dark_stream") { - FunctionSplitter = "type", - FunctionLookup = new Dictionary { - ["StartGame"] = new TriggerDefinitionOverride("dark_stream_start_game", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("round", Required)), - }, - ["SpawnMonster"] = new TriggerDefinitionOverride("dark_stream_spawn_monster", splitter: "type") { - Names = BuildNameOverride(("spawnID", "spawnIds")), - Types = BuildTypeOverride(("spawnIds", Required), ("score", Required)), - }, - ["StartRound"] = new TriggerDefinitionOverride("dark_stream_start_round", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("uiDuration", Required), ("round", Required), ("damagePenalty", Required)), - }, - ["ClearRound"] = new TriggerDefinitionOverride("dark_stream_clear_round", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("round", Required)), - }, - }, - }; - ActionOverride["debug_string"] = new TriggerDefinitionOverride("debug_string") { - Names = BuildNameOverride(("arg1", "value"), ("string", "value")), - Types = BuildTypeOverride(("value", Required)), - }; - ActionOverride["destroy_monster"] = new TriggerDefinitionOverride("destroy_monster") { - Names = BuildNameOverride(("arg1", "spawnIds"), ("agr2", "arg2")), - Types = BuildTypeOverride(("spawnIds", Required), ("arg2", "True")), - }; - ActionOverride["dungeon_clear"] = new TriggerDefinitionOverride("dungeon_clear") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("uiType", null)), - }; - ActionOverride["dungeon_clear_round"] = new TriggerDefinitionOverride("dungeon_clear_round") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("round", Required)), - }; - - ActionOverride["dungeon_close_timer"] = new TriggerDefinitionOverride("dungeon_close_timer") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - - ActionOverride["dungeon_disable_ranking"] = new TriggerDefinitionOverride("dungeon_disable_ranking") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - - ActionOverride["dungeon_enable_give_up"] = new TriggerDefinitionOverride("dungeon_enable_give_up") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("isEnable", null)), - }; - - ActionOverride["dungeon_fail"] = new TriggerDefinitionOverride("dungeon_fail") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - - ActionOverride["dungeon_mission_complete"] = new TriggerDefinitionOverride("dungeon_mission_complete") { - Names = BuildNameOverride(("missionID", "missionId")), - Types = BuildTypeOverride(("missionId", Required)), - }; - - ActionOverride["dungeon_move_lap_time_to_now"] = new TriggerDefinitionOverride("dungeon_move_lap_time_to_now") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("id", Required)), - }; - - ActionOverride["dungeon_reset_time"] = new TriggerDefinitionOverride("dungeon_reset_time") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("seconds", Required)), - }; - - ActionOverride["dungeon_set_end_time"] = new TriggerDefinitionOverride("dungeon_set_end_time") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - - ActionOverride["dungeon_set_lap_time"] = new TriggerDefinitionOverride("dungeon_set_lap_time") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("id", Required), ("lapTime", null)), - }; - - ActionOverride["dungeon_stop_timer"] = new TriggerDefinitionOverride("dungeon_stop_timer") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ActionOverride["dungeon_variable"] = new TriggerDefinitionOverride("set_dungeon_variable") { - Names = BuildNameOverride(("varID", "varId")), - Types = BuildTypeOverride(("varId", Required), ("value", Required)), - }; - ActionOverride["enable_local_camera"] = new TriggerDefinitionOverride("enable_local_camera") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("isEnable", null)), - }; - ActionOverride["enable_spawn_point_pc"] = new TriggerDefinitionOverride("enable_spawn_point_pc") { - Names = BuildNameOverride(("spawnPointID", "spawnId")), - Types = BuildTypeOverride(("spawnId", Required), ("isEnable", null)), - }; - ActionOverride["end_mini_game"] = new TriggerDefinitionOverride("end_mini_game") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("winnerBoxId", null), ("isEnable", null), ("isOnlyWinner", null)), - }; - ActionOverride["end_mini_game_round"] = new TriggerDefinitionOverride("end_mini_game_round") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("winnerBoxId", Required), ("expRate", null), ("meso", null), ("isOnlyWinner", null), ("isGainLoserBonus", null)), - }; - ActionOverride["face_emotion"] = new TriggerDefinitionOverride("face_emotion") { - Names = BuildNameOverride(("spawnPointID", "spawnId"), ("spwnPointID", "spawnId")), - Types = BuildTypeOverride(("spawnId", null)), - }; - ActionOverride["field_game_constant"] = new TriggerDefinitionOverride("field_game_constant") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("key", Required), ("value", Required), ("locale", null)), - }; - ActionOverride["field_game_message"] = new TriggerDefinitionOverride("field_game_message") { - Names = BuildNameOverride(("arg2", "script"), ("arg3", "duration")), - Types = BuildTypeOverride(("custom", null), ("type", Required), ("duration", null), ("arg1", null), ("script", Required)), - }; - ActionOverride["field_war_end"] = new TriggerDefinitionOverride("field_war_end") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("isClear", null)), - }; - ActionOverride["give_exp"] = new TriggerDefinitionOverride("give_exp") { - Names = BuildNameOverride(("arg1", "boxId"), ("arg2", "rate")), - Types = BuildTypeOverride(("boxId", Required), ("rate", "1.0"), ("arg3", null)), - }; - ActionOverride["give_guild_exp"] = new TriggerDefinitionOverride("give_guild_exp") { - Names = BuildNameOverride(("boxID", "boxId")), - Types = BuildTypeOverride(("boxId", null), ("type", Required)), - }; - ActionOverride["give_reward_content"] = new TriggerDefinitionOverride("give_reward_content") { - Names = BuildNameOverride(("rewardID", "rewardId")), - Types = BuildTypeOverride(("rewardId", Required)), - }; - ActionOverride["guide_event"] = new TriggerDefinitionOverride("guide_event") { - Names = BuildNameOverride(("eventID", "eventId")), - Types = BuildTypeOverride(("eventId", Required)), - }; - ActionOverride["guild_vs_game_end_game"] = new TriggerDefinitionOverride("guild_vs_game_end_game") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ActionOverride["guild_vs_game_give_contribution"] = new TriggerDefinitionOverride("guild_vs_game_give_contribution") { - Names = BuildNameOverride(("teamID", "teamId")), - Types = BuildTypeOverride(("teamId", Required), ("isWin", null)), - }; - ActionOverride["guild_vs_game_give_reward"] = new TriggerDefinitionOverride("guild_vs_game_give_reward") { - Names = BuildNameOverride(("teamID", "teamId")), - Types = BuildTypeOverride(("teamId", Required), ("isWin", null)), - }; - ActionOverride["guild_vs_game_log_result"] = new TriggerDefinitionOverride("guild_vs_game_log_result") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ActionOverride["guild_vs_game_log_won_by_default"] = new TriggerDefinitionOverride("guild_vs_game_log_won_by_default") { - Names = BuildNameOverride(("teamID", "teamId")), - Types = BuildTypeOverride(("teamId", Required)), - }; - ActionOverride["guild_vs_game_result"] = new TriggerDefinitionOverride("guild_vs_game_result") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ActionOverride["guild_vs_game_score_by_user"] = new TriggerDefinitionOverride("guild_vs_game_score_by_user") { - Names = BuildNameOverride(("triggerBoxID", "boxId")), - Types = BuildTypeOverride(("boxId", Required), ("score", Required)), - }; - ActionOverride["hide_guide_summary"] = new TriggerDefinitionOverride("hide_guide_summary") { - Names = BuildNameOverride(("entityID", "entityId"), ("textID", "textId")), - Types = BuildTypeOverride(("entityId", Required), ("textId", null)), - }; - ActionOverride["init_npc_rotation"] = new TriggerDefinitionOverride("init_npc_rotation") { - Names = BuildNameOverride(("arg1", "spawnIds")), - Types = BuildTypeOverride(("spawnIds", Required)), - }; - ActionOverride["kick_music_audience"] = new TriggerDefinitionOverride("kick_music_audience") { - Names = BuildNameOverride(("targetBoxID", "boxId"), ("targetPortalID", "portalId")), - Types = BuildTypeOverride(("boxId", Required), ("portalId", Required)), - }; - ActionOverride["limit_spawn_npc_count"] = new TriggerDefinitionOverride("limit_spawn_npc_count") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("limitCount", Required)), - }; - ActionOverride["lock_my_pc"] = new TriggerDefinitionOverride("lock_my_pc") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("isLock", null)), - }; - ActionOverride["mini_game_camera_direction"] = new TriggerDefinitionOverride("mini_game_camera_direction") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("boxId", Required), ("cameraId", Required)), - }; - ActionOverride["mini_game_give_exp"] = new TriggerDefinitionOverride("mini_game_give_exp") { - Names = BuildNameOverride(("isOutSide", "isOutside")), - Types = BuildTypeOverride(("boxId", Required), ("expRate", "1.0"), ("isOutside", null)), - }; - ActionOverride["mini_game_give_reward"] = new TriggerDefinitionOverride("mini_game_give_reward") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("winnerBoxId", Required), ("contentType", Required)), - }; - ActionOverride["move_npc"] = new TriggerDefinitionOverride("move_npc") { - Names = BuildNameOverride(("arg1", "spawnId"), ("arg2", "patrolName")), - Types = BuildTypeOverride(("spawnId", Required), ("patrolName", Required)), - }; - ActionOverride["move_npc_to_pos"] = new TriggerDefinitionOverride("move_npc_to_pos") { - Names = BuildNameOverride(("spawnPointID", "spawnId")), - Types = BuildTypeOverride(("spawnId", Required), ("pos", Required), ("rot", Required)), - }; - ActionOverride["move_random_user"] = new TriggerDefinitionOverride("move_random_user") { - Names = BuildNameOverride(("arg1", "mapId"), ("arg2", "portalId"), ("arg3", "boxId"), ("arg4", "count")), - Types = BuildTypeOverride(("mapId", Required), ("portalId", Required), ("boxId", Required), ("count", Required)), - }; - ActionOverride["move_to_portal"] = new TriggerDefinitionOverride("move_to_portal") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("userTagId", null), ("portalId", null), ("boxId", null)), - }; - ActionOverride["move_user"] = new TriggerDefinitionOverride("move_user") { - Names = BuildNameOverride(("arg1", "mapId"), ("arg2", "portalId"), ("arg3", "boxId")), - Types = BuildTypeOverride(("mapId", null), ("portalId", null), ("boxId", null)), - }; - ActionOverride["move_user_path"] = new TriggerDefinitionOverride("move_user_path") { - Names = BuildNameOverride(("arg1", "patrolName")), - Types = BuildTypeOverride(("patrolName", Required)), - }; - ActionOverride["move_user_to_box"] = new TriggerDefinitionOverride("move_user_to_box") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("boxId", Required), ("portalId", Required)), - }; - ActionOverride["move_user_to_pos"] = new TriggerDefinitionOverride("move_user_to_pos") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("pos", Required), ("rot", null)), - }; - ActionOverride["notice"] = new TriggerDefinitionOverride("notice") { - Names = BuildNameOverride(("arg1", "type"), ("arg2", "script")), - Types = BuildTypeOverride(("type", null), ("script", Required), ("arg3", null)), - }; - ActionOverride["npc_remove_additional_effect"] = new TriggerDefinitionOverride("npc_remove_additional_effect") { - Names = BuildNameOverride(("spawnPointID", "spawnId"), ("additionalEffectID", "additionalEffectId")), - Types = BuildTypeOverride(("spawnId", Required), ("additionalEffectId", Required)), - }; - ActionOverride["npc_to_patrol_in_box"] = new TriggerDefinitionOverride("npc_to_patrol_in_box") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("boxId", Required), ("npcId", Required)), - }; - ActionOverride["patrol_condition_user"] = new TriggerDefinitionOverride("patrol_condition_user") { - Names = BuildNameOverride(("additionalEffectID", "additionalEffectId")), - Types = BuildTypeOverride(("patrolIndex", Required), ("additionalEffectId", Required)), - }; - ActionOverride["play_scene_movie"] = new TriggerDefinitionOverride("play_scene_movie") { - Names = BuildNameOverride(("movieID", "movieId")), - Types = BuildTypeOverride(("movieId", null)), - }; - ActionOverride["play_system_sound_by_user_tag"] = new TriggerDefinitionOverride("play_system_sound_by_user_tag") { - Names = BuildNameOverride(("userTagID", "userTagId")), - Types = BuildTypeOverride(("userTagId", Required), ("soundKey", Required)), - }; - ActionOverride["play_system_sound_in_box"] = new TriggerDefinitionOverride("play_system_sound_in_box") { - Names = BuildNameOverride(("arg1", "boxIds"), ("arg2", "sound")), - Types = BuildTypeOverride(("boxIds", null), ("sound", Required)), - }; - ActionOverride["random_additional_effect"] = new TriggerDefinitionOverride("random_additional_effect") { - Names = BuildNameOverride(("Target", "target"), ("triggerBoxID", "boxId"), ("spawnPointID", "spawnId"), ("arg1", "boxIds"), ("additionalEffectID", "additionalEffectId")), - Types = BuildTypeOverride(("boxId", null), ("spawnId", null), ("targetCount", null), ("tick", null), ("waitTick", null), ("additionalEffectId", null)), - }; - ActionOverride["remove_balloon_talk"] = new TriggerDefinitionOverride("remove_balloon_talk") { - Names = BuildNameOverride(("spawnPointID", "spawnId")), - Types = BuildTypeOverride(("spawnId", null)), - }; - ActionOverride["remove_buff"] = new TriggerDefinitionOverride("remove_buff") { - Names = BuildNameOverride(("arg1", "boxId"), ("arg2", "skillId"), ("arg3", "isPlayer")), - Types = BuildTypeOverride(("boxId", Required), ("skillId", Required), ("isPlayer", null)), - }; - ActionOverride["remove_cinematic_talk"] = new TriggerDefinitionOverride("remove_cinematic_talk") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ActionOverride["remove_effect_nif"] = new TriggerDefinitionOverride("remove_effect_nif") { - Names = BuildNameOverride(("spawnPointID", "spawnId")), - Types = BuildTypeOverride(("spawnId", Required)), - }; - ActionOverride["reset_camera"] = new TriggerDefinitionOverride("reset_camera") { - Names = BuildNameOverride(("arg1", "interpolationTime"), ("arg2", "interpolationTime")), - Types = BuildTypeOverride(("interpolationTime", null)), - }; - ActionOverride["reset_timer"] = new TriggerDefinitionOverride("reset_timer") { - Names = BuildNameOverride(("arg1", "timerId")), - Types = BuildTypeOverride(), - }; - ActionOverride["room_expire"] = new TriggerDefinitionOverride("room_expire") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ActionOverride["score_board_create"] = new TriggerDefinitionOverride("score_board_create") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("type", null), ("title", null), ("maxScore", null)), - }; - ActionOverride["score_board_remove"] = new TriggerDefinitionOverride("score_board_remove") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ActionOverride["score_board_set_score"] = new TriggerDefinitionOverride("score_board_set_score") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("score", Required)), - }; - ActionOverride["select_camera"] = new TriggerDefinitionOverride("select_camera") { - Names = BuildNameOverride(("arg1", "triggerId"), ("arg2", "enable")), - Types = BuildTypeOverride(("triggerId", Required), ("enable", "True")), - }; - ActionOverride["select_camera_path"] = new TriggerDefinitionOverride("select_camera_path") { - Names = BuildNameOverride(("arg1", "pathIds"), ("arg2", "returnView")), - Types = BuildTypeOverride(("pathIds", Required), ("returnView", "True")), - }; - ActionOverride["set_achievement"] = new TriggerDefinitionOverride("set_achievement") { - Names = BuildNameOverride(("arg1", "triggerId"), ("arg2", "type"), ("arg3", "achieve")), - Types = BuildTypeOverride(("triggerId", null)), - }; - ActionOverride["set_actor"] = new TriggerDefinitionOverride("set_actor") { - Names = BuildNameOverride(("arg1", "triggerId"), ("arg2", "visible"), ("arg3", "initialSequence")), - Types = BuildTypeOverride(("triggerId", Required), ("visible", null), ("arg4", null), ("arg5", null)), - }; - ActionOverride["set_agent"] = new TriggerDefinitionOverride("set_agent") { - Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "visible")), - Types = BuildTypeOverride(("triggerIds", Required), ("visible", null)), - }; - ActionOverride["set_ai_extra_data"] = new TriggerDefinitionOverride("set_ai_extra_data") { - Names = BuildNameOverride(("boxID", "boxId")), - Types = BuildTypeOverride(("key", Required), ("value", Required), ("isModify", null), ("boxId", null)), - }; - ActionOverride["set_ambient_light"] = new TriggerDefinitionOverride("set_ambient_light") { - Names = BuildNameOverride(("arg1", "primary"), ("arg2", "secondary"), ("arg3", "tertiary")), - Types = BuildTypeOverride(("primary", Required), ("secondary", null), ("tertiary", null)), - }; - ActionOverride["set_breakable"] = new TriggerDefinitionOverride("set_breakable") { - Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "enable")), - Types = BuildTypeOverride(("triggerIds", Required), ("enable", null)), - }; - ActionOverride["set_cinematic_intro"] = new TriggerDefinitionOverride("set_cinematic_intro") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ActionOverride["set_cinematic_ui"] = new TriggerDefinitionOverride("set_cinematic_ui") { - Names = BuildNameOverride(("arg1", "type"), ("arg2", "script")), - Types = BuildTypeOverride(("type", Required), ("script", null), ("arg3", null)), - }; - ActionOverride["set_dialogue"] = new TriggerDefinitionOverride("set_dialogue") { - Names = BuildNameOverride(("arg1", "type"), ("arg2", "spawnId"), ("arg3", "script"), ("arg4", "time")), - Types = BuildTypeOverride(("type", Required), ("spawnId", null), ("script", Required), ("time", null), ("arg5", null), ("align", null)), - }; - ActionOverride["set_cube"] = new TriggerDefinitionOverride("set_cube") { - Names = BuildNameOverride(("IDs", "triggerIds"), ("arg1", "triggerIds"), ("arg2", "isVisible")), - Types = BuildTypeOverride(("triggerIds", Required), ("isVisible", null), ("randomCount", null)), - }; - ActionOverride["set_directional_light"] = new TriggerDefinitionOverride("set_directional_light") { - Names = BuildNameOverride(("arg1", "diffuseColor"), ("arg2", "specularColor")), - Types = BuildTypeOverride(("diffuseColor", Required), ("specularColor", null)), - }; - ActionOverride["set_effect"] = new TriggerDefinitionOverride("set_effect") { - Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "visible"), ("arg3", "startDelay"), ("arg4", "interval")), - Types = BuildTypeOverride(("triggerIds", null), ("visible", null), ("startDelay", null), ("interval", null)), - }; - ActionOverride["set_event_ui"] = new TriggerDefinitionOverride(string.Empty) { - FunctionSplitter = "arg1", - FunctionLookup = new Dictionary { - ["0"] = new TriggerDefinitionOverride("set_event_ui_round", splitter: "arg1") { - Names = BuildNameOverride(("arg2", "rounds"), ("arg4", "vOffset")), - Types = BuildTypeOverride(("rounds", Required), ("arg3", null), ("vOffset", null)), - }, - ["1"] = new TriggerDefinitionOverride("set_event_ui_script", splitter: "arg1") { - Names = BuildNameOverride(("arg1", "type"), ("arg2", "script"), ("arg3", "duration"), ("arg4", "boxIds")), - Types = BuildTypeOverride(("type", Required), ("script", null), ("duration", Required), ("boxIds", null)), - }, - ["2"] = new TriggerDefinitionOverride("set_event_ui_countdown", splitter: "arg1") { - Names = BuildNameOverride(("arg2", "script"), ("arg3", "roundCountdown"), ("arg4", "boxIds")), - Types = BuildTypeOverride(("script", null), ("roundCountdown", Required), ("boxIds", null)), - }, - ["3"] = new TriggerDefinitionOverride("set_event_ui_script", splitter: "arg1") { - Names = BuildNameOverride(("arg1", "type"), ("arg2", "script"), ("arg3", "duration"), ("arg4", "boxIds")), - Types = BuildTypeOverride(("type", Required), ("script", null), ("duration", Required), ("boxIds", null)), - }, - ["4"] = new TriggerDefinitionOverride("set_event_ui_script", splitter: "arg1") { - Names = BuildNameOverride(("arg1", "type"), ("arg2", "script"), ("arg3", "duration"), ("arg4", "boxIds")), - Types = BuildTypeOverride(("type", Required), ("script", null), ("duration", Required), ("boxIds", null)), - }, - ["5"] = new TriggerDefinitionOverride("set_event_ui_script", splitter: "arg1") { - Names = BuildNameOverride(("arg1", "type"), ("arg2", "script"), ("arg3", "duration"), ("arg4", "boxIds")), - Types = BuildTypeOverride(("type", Required), ("script", null), ("duration", Required), ("boxIds", null)), - }, - ["6"] = new TriggerDefinitionOverride("set_event_ui_script", splitter: "arg1") { - Names = BuildNameOverride(("arg1", "type"), ("arg2", "script"), ("arg3", "duration"), ("arg4", "boxIds")), - Types = BuildTypeOverride(("type", Required), ("script", null), ("duration", Required), ("boxIds", null)), - }, - ["7"] = new TriggerDefinitionOverride("set_event_ui_script", splitter: "arg1") { - Names = BuildNameOverride(("arg1", "type"), ("arg2", "script"), ("arg3", "duration"), ("arg4", "boxIds")), - Types = BuildTypeOverride(("type", Required), ("script", null), ("duration", Required), ("boxIds", null)), - }, - }, - }; - ActionOverride["set_gravity"] = new TriggerDefinitionOverride("set_gravity") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("gravity", Required)), - }; - ActionOverride["set_interact_object"] = new TriggerDefinitionOverride("set_interact_object") { - Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "state")), - Types = BuildTypeOverride(("triggerIds", Required), ("state", Required), ("arg4", null), ("arg3", null)), - }; - ActionOverride["set_ladder"] = new TriggerDefinitionOverride("set_ladder") { - Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "visible"), ("arg3", "enable"), ("arg4", "fade")), - Types = BuildTypeOverride(("triggerIds", Required), ("visible", null), ("enable", null), ("fade", null)), - }; - ActionOverride["set_local_camera"] = new TriggerDefinitionOverride("set_local_camera") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("cameraId", Required), ("enable", null)), - }; - ActionOverride["set_mesh"] = new TriggerDefinitionOverride("set_mesh") { - Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "visible"), ("arg3", "startDelay"), ("arg4", "interval"), ("arg5", "fade")), - Types = BuildTypeOverride(("triggerIds", Required), ("visible", null), ("startDelay", null), ("interval", null), ("fade", null)), - }; - ActionOverride["set_mesh_animation"] = new TriggerDefinitionOverride("set_mesh_animation") { - Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "visible"), ("arg3", "startDelay"), ("arg4", "interval")), - Types = BuildTypeOverride(("triggerIds", Required), ("visible", null), ("startDelay", null), ("interval", null)), - }; - ActionOverride["set_mini_game_area_for_hack"] = new TriggerDefinitionOverride("set_mini_game_area_for_hack") { - Names = BuildNameOverride(("boxID", "boxId")), - Types = BuildTypeOverride(("boxId", Required)), - }; - ActionOverride["set_npc_duel_hp_bar"] = new TriggerDefinitionOverride("set_npc_duel_hp_bar") { - Names = BuildNameOverride(("spawnPointID", "spawnId")), - Types = BuildTypeOverride(("isOpen", null), ("spawnId", Required), ("durationTick", null), ("npcHpStep", null)), - }; - ActionOverride["set_npc_emotion_loop"] = new TriggerDefinitionOverride("set_npc_emotion_loop") { - Names = BuildNameOverride(("arg1", "spawnId"), ("arg2", "sequenceName"), ("arg3", "duration"), ("arg", "duration")), - Types = BuildTypeOverride(("spawnId", Required), ("duration", null)), - }; - ActionOverride["set_npc_emotion_sequence"] = new TriggerDefinitionOverride("set_npc_emotion_sequence") { - Names = BuildNameOverride(("arg1", "spawnId"), ("arg2", "sequenceName"), ("arg3", "durationTick")), - Types = BuildTypeOverride(("spawnId", Required), ("sequenceName", Required), ("durationTick", null)), - }; - ActionOverride["set_npc_rotation"] = new TriggerDefinitionOverride("set_npc_rotation") { - Names = BuildNameOverride(("arg1", "spawnId"), ("arg2", "rotation")), - Types = BuildTypeOverride(("spawnId", Required), ("rotation", Required)), - }; - ActionOverride["set_onetime_effect"] = new TriggerDefinitionOverride("set_onetime_effect") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("id", null), ("enable", null)), - }; - ActionOverride["set_pc_emotion_loop"] = new TriggerDefinitionOverride("set_pc_emotion_loop") { - Names = BuildNameOverride(("arg1", "sequenceName"), ("arg2", "duration"), ("arg3", "loop")), - Types = BuildTypeOverride(("sequenceName", Required), ("duration", null), ("loop", null)), - }; - ActionOverride["set_pc_emotion_sequence"] = new TriggerDefinitionOverride("set_pc_emotion_sequence") { - Names = BuildNameOverride(("arg1", "sequenceNames")), - Types = BuildTypeOverride(("sequenceNames", Required)), - }; - ActionOverride["set_pc_rotation"] = new TriggerDefinitionOverride("set_pc_rotation") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("rotation", Required)), - }; - ActionOverride["set_photo_studio"] = new TriggerDefinitionOverride("set_photo_studio") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("isEnable", null)), - }; - ActionOverride["set_portal"] = new TriggerDefinitionOverride("set_portal") { - Names = BuildNameOverride(("arg1", "portalId"), ("arg2", "visible"), ("arg3", "enable"), ("arg4", "minimapVisible"), ("arg", "minimapVisible")), - Types = BuildTypeOverride(("portalId", Required), ("visible", null), ("enable", null), ("minimapVisible", null), ("arg5", null)), - }; - ActionOverride["set_pvp_zone"] = new TriggerDefinitionOverride("set_pvp_zone") { - Names = BuildNameOverride(("arg1", "boxId"), ("arg2", "prepareTime"), ("arg3", "matchTime"), ("arg4", "additionalEffectId"), ("arg5", "type"), ("arg6", "boxIds")), - Types = BuildTypeOverride(("boxId", Required), ("prepareTime", Required), ("matchTime", Required), ("additionalEffectId", null), ("type", null), ("boxIds", null)), - }; - ActionOverride["set_quest_accept"] = new TriggerDefinitionOverride("set_quest_accept") { - Names = BuildNameOverride(("questID", "questId"), ("arg1", "questId")), - Types = BuildTypeOverride(("questId", Required)), - }; - ActionOverride["set_quest_complete"] = new TriggerDefinitionOverride("set_quest_complete") { - Names = BuildNameOverride(("questID", "questId")), - Types = BuildTypeOverride(("questId", Required)), - }; - ActionOverride["set_random_mesh"] = new TriggerDefinitionOverride("set_random_mesh") { - Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "visible"), ("arg3", "startDelay"), ("arg4", "interval"), ("arg5", "fade")), - Types = BuildTypeOverride(("triggerIds", Required), ("visible", null), ("startDelay", null), ("interval", null), ("fade", null)), - }; - ActionOverride["set_rope"] = new TriggerDefinitionOverride("set_rope") { - Names = BuildNameOverride(("arg1", "triggerId"), ("arg2", "visible"), ("arg3", "enable"), ("arg4", "fade")), - Types = BuildTypeOverride(("triggerId", Required), ("visible", null), ("enable", null), ("fade", null)), - }; - ActionOverride["set_scene_skip"] = new TriggerDefinitionOverride("set_scene_skip") { - Names = BuildNameOverride(("arg1", "state"), ("arg2", "action")), - Types = BuildTypeOverride(("state", null)), - }; - ActionOverride["set_skill"] = new TriggerDefinitionOverride("set_skill") { - Names = BuildNameOverride(("objectIDs", "triggerIds"), ("arg1", "triggerIds"), ("arg2", "enable"), ("isEnable", "enable")), - Types = BuildTypeOverride(("triggerIds", Required), ("enable", null)), - }; - ActionOverride["set_skip"] = new TriggerDefinitionOverride("set_skip") { - Names = BuildNameOverride(("arg1", "state")), - Types = BuildTypeOverride(("state", null)), - }; - ActionOverride["set_sound"] = new TriggerDefinitionOverride("set_sound") { - Names = BuildNameOverride(("arg1", "triggerId"), ("arg2", "enable")), - Types = BuildTypeOverride(("triggerId", Required), ("enable", null)), - }; - ActionOverride["set_state"] = new TriggerDefinitionOverride("set_state") { - Names = BuildNameOverride(("arg1", "id"), ("arg2", "states"), ("arg3", "randomize")), - Types = BuildTypeOverride(("id", Required), ("states", Required), ("randomize", null)), - }; - ActionOverride["set_time_scale"] = new TriggerDefinitionOverride("set_time_scale") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("enable", null), ("startScale", null), ("endScale", null), ("duration", null), ("interpolator", null)), - }; - ActionOverride["set_timer"] = new TriggerDefinitionOverride("set_timer") { - Names = BuildNameOverride(("arg1", "timerId"), ("arg2", "seconds"), ("arg3", "autoRemove"), ("ara3", "autoRemove"), ("arg4", "display"), ("arg5", "vOffset"), ("arg6", "type")), - Types = BuildTypeOverride(("seconds", null), ("autoRemove", null), ("display", null), ("vOffset", null)), - }; - ActionOverride["set_user_value"] = new TriggerDefinitionOverride("set_user_value") { - Names = BuildNameOverride(("triggerID", "triggerId")), - Types = BuildTypeOverride(("triggerId", null), ("key", Required), ("value", Required)), - }; - ActionOverride["set_user_value_from_dungeon_reward_count"] = new TriggerDefinitionOverride("set_user_value_from_dungeon_reward_count") { - Names = BuildNameOverride(("dungeonRewardID", "dungeonRewardId")), - Types = BuildTypeOverride(("dungeonRewardId", Required)), - }; - ActionOverride["set_user_value_from_guild_vs_game_score"] = new TriggerDefinitionOverride("set_user_value_from_guild_vs_game_score") { - Names = BuildNameOverride(("teamID", "teamId")), - Types = BuildTypeOverride(("teamId", Required)), - }; - ActionOverride["set_user_value_from_user_count"] = new TriggerDefinitionOverride("set_user_value_from_user_count") { - Names = BuildNameOverride(("triggerBoxID", "triggerBoxId"), ("userTagID", "userTagId")), - Types = BuildTypeOverride(("triggerBoxId", Required), ("key", Required), ("userTagId", Required)), - }; - ActionOverride["set_visible_breakable_object"] = new TriggerDefinitionOverride("set_visible_breakable_object") { - Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "visible")), - Types = BuildTypeOverride(("triggerIds", Required), ("visible", null)), - }; - ActionOverride["set_visible_ui"] = new TriggerDefinitionOverride("set_visible_ui") { - Names = BuildNameOverride(("uiName", "uiNames")), - Types = BuildTypeOverride(("uiNames", Required), ("visible", null)), - }; - ActionOverride["shadow_expedition"] = new TriggerDefinitionOverride("") { - FunctionSplitter = "type", - FunctionLookup = new Dictionary { - ["OpenBossGauge"] = new TriggerDefinitionOverride("shadow_expedition_open_boss_gauge", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("maxGaugePoint", Required)), - }, - ["CloseBossGauge"] = new TriggerDefinitionOverride("shadow_expedition_close_boss_gauge", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }, - }, - }; - ActionOverride["show_caption"] = new TriggerDefinitionOverride("show_caption") { - Names = BuildNameOverride(("offestRateX", "offsetRateX")), - Types = BuildTypeOverride(("type", Required), ("title", Required), ("align", null), ("offsetRateX", null), ("offsetRateY", null), ("duration", null), ("scale", null)), - }; - ActionOverride["show_count_ui"] = new TriggerDefinitionOverride("show_count_ui") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("text", Required), ("stage", null), ("count", Required), ("soundType", "1")), - }; - ActionOverride["show_event_result"] = new TriggerDefinitionOverride("show_event_result") { - Names = BuildNameOverride(("userTagID", "userTagId"), ("triggerBoxID", "triggerBoxId"), ("isOutSide", "isOutside")), - Types = BuildTypeOverride(("type", Required), ("text", Required), ("duration", null), ("userTagId", null), ("triggerBoxId", null), ("isOutside", null)), - }; - ActionOverride["show_guide_summary"] = new TriggerDefinitionOverride("show_guide_summary") { - Names = BuildNameOverride(("entityID", "entityId"), ("textID", "textId"), ("durationTime", "duration")), - Types = BuildTypeOverride(("entityId", Required), ("textId", null), ("duration", null)), - }; - ActionOverride["show_round_ui"] = new TriggerDefinitionOverride("show_round_ui") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("round", Required), ("duration", null), ("isFinalRound", null)), - }; - ActionOverride["side_npc_talk"] = new TriggerDefinitionOverride("") { - Types = BuildTypeOverride(("type", "talk")), - FunctionSplitter = "type", - FunctionLookup = new Dictionary { - ["talk"] = new TriggerDefinitionOverride("side_npc_talk", splitter: "type") { - Names = BuildNameOverride(("npcID", "npcId")), - Types = BuildTypeOverride(("npcId", Required), ("illust", Required), ("duration", Required), ("script", Required)), - }, - ["talkbottom"] = new TriggerDefinitionOverride("side_npc_talk_bottom", splitter: "type") { - Names = BuildNameOverride(("npcID", "npcId")), - Types = BuildTypeOverride(("npcId", Required), ("illust", Required), ("duration", Required), ("script", Required)), - }, - ["movie"] = new TriggerDefinitionOverride("side_npc_movie", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("usm", Required), ("duration", Required)), - }, - ["cutin"] = new TriggerDefinitionOverride("side_npc_cutin", splitter: "type") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("illust", Required), ("duration", Required)), - }, - }, - }; - ActionOverride["sight_range"] = new TriggerDefinitionOverride("sight_range") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("enable", null), ("range", Required), ("rangeZ", null), ("border", null)), - }; - ActionOverride["spawn_item_range"] = new TriggerDefinitionOverride("spawn_item_range") { - Names = BuildNameOverride(("rangeID", "rangeIds")), - Types = BuildTypeOverride(("rangeIds", Required), ("randomPickCount", Required)), - }; - ActionOverride["spawn_npc_range"] = new TriggerDefinitionOverride("spawn_npc_range") { - Names = BuildNameOverride(("rangeID", "rangeIds")), - Types = BuildTypeOverride(("rangeIds", Required), ("isAutoTargeting", null), ("randomPickCount", null), ("score", null)), - }; - ActionOverride["start_combine_spawn"] = new TriggerDefinitionOverride("start_combine_spawn") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("groupId", Required), ("isStart", null)), - }; - ActionOverride["start_mini_game"] = new TriggerDefinitionOverride("start_mini_game") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("boxId", Required), ("round", Required), ("gameName", Required), ("isShowResultUI", "True")), - }; - ActionOverride["start_mini_game_round"] = new TriggerDefinitionOverride("start_mini_game_round") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("boxId", Required), ("round", Required)), - }; - ActionOverride["start_tutorial"] = new TriggerDefinitionOverride("start_tutorial") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ActionOverride["talk_npc"] = new TriggerDefinitionOverride("talk_npc") { - Names = BuildNameOverride(("spawnPointID", "spawnId")), - Types = BuildTypeOverride(("spawnId", Required)), - }; - ActionOverride["unset_mini_game_area_for_hack"] = new TriggerDefinitionOverride("unset_mini_game_area_for_hack") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ActionOverride["use_state"] = new TriggerDefinitionOverride("use_state") { - Names = BuildNameOverride(("arg1", "id"), ("arg2", "randomize")), - Types = BuildTypeOverride(("id", null), ("randomize", null)), - }; - ActionOverride["user_tag_symbol"] = new TriggerDefinitionOverride("user_tag_symbol") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("symbol1", Required), ("symbol2", Required)), - }; - ActionOverride["user_value_to_number_mesh"] = new TriggerDefinitionOverride("user_value_to_number_mesh") { - Names = BuildNameOverride(("startMeshID", "startMeshId")), - Types = BuildTypeOverride(("key", Required), ("startMeshId", Required), ("digitCount", Required)), - }; - ActionOverride["visible_my_pc"] = new TriggerDefinitionOverride("visible_my_pc") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("isVisible", Required)), - }; - ActionOverride["weather"] = new TriggerDefinitionOverride("weather") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("weatherType", Required)), - }; - ActionOverride["wedding_broken"] = new TriggerDefinitionOverride("wedding_broken") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ActionOverride["wedding_move_user"] = new TriggerDefinitionOverride("wedding_move_user") { - Names = BuildNameOverride(("arg1", "mapId"), ("arg2", "portalIds"), ("arg3", "boxId")), - Types = BuildTypeOverride(("entryType", Required), ("mapId", Required), ("portalIds", Required), ("boxId", Required)), - }; - ActionOverride["wedding_mutual_agree"] = new TriggerDefinitionOverride("wedding_mutual_agree") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("agreeType", Required)), - }; - ActionOverride["wedding_mutual_cancel"] = new TriggerDefinitionOverride("wedding_mutual_cancel") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("agreeType", Required)), - }; - ActionOverride["wedding_set_user_emotion"] = new TriggerDefinitionOverride("wedding_set_user_emotion") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("entryType", Required), ("id", Required)), - }; - ActionOverride["wedding_set_user_look_at"] = new TriggerDefinitionOverride("wedding_set_user_look_at") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("entryType", Required), ("lookAtEntryType", Required), ("immediate", null)), - }; - ActionOverride["wedding_set_user_rotation"] = new TriggerDefinitionOverride("wedding_set_user_rotation") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("entryType", Required), ("rotation", Required), ("immediate", null)), - }; - ActionOverride["wedding_user_to_patrol"] = new TriggerDefinitionOverride("wedding_user_to_patrol") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("entryType", Required), ("patrolIndex", null)), - }; - ActionOverride["wedding_vow_complete"] = new TriggerDefinitionOverride("wedding_vow_complete") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ActionOverride["widget_action"] = new TriggerDefinitionOverride("widget_action") { - Names = BuildNameOverride(("arg1", "type"), ("arg2", "func"), ("arg3", "widgetArg")), - Types = BuildTypeOverride(("type", Required), ("func", Required), ("widgetArgNum", null)), - }; - ActionOverride["write_log"] = new TriggerDefinitionOverride("write_log") { - Names = BuildNameOverride(("arg1", "logName"), ("arg2", "triggerId"), ("arg3", "event"), ("arg4", "level"), ("arg5", "subEvent")), - Types = BuildTypeOverride(("logName", Required), ("triggerId", null), ("level", null)), - }; - - // Condition Override - ConditionOverride["all_of"] = new TriggerDefinitionOverride("all_of") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ConditionOverride["true"] = new TriggerDefinitionOverride("true") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ConditionOverride["any_one"] = new TriggerDefinitionOverride("any_one") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ConditionOverride["always"] = new TriggerDefinitionOverride("always") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("arg1", "True")), - }; - ConditionOverride["bonus_game_reward_detected"] = new TriggerDefinitionOverride("bonus_game_reward") { - Names = BuildNameOverride(("arg1", "boxId"), ("arg2", "type")), - Types = BuildTypeOverride(("boxId", Required), ("type", Required)), - Compare = BuildCompareOverride("type", ""), - }; - ConditionOverride["check_any_user_additional_effect"] = new TriggerDefinitionOverride("check_any_user_additional_effect") { - Names = BuildNameOverride(("triggerBoxID", "boxId"), ("additionalEffectID", "additionalEffectId")), - Types = BuildTypeOverride(("boxId", Required), ("additionalEffectId", Required), ("level", Required)), - }; - ConditionOverride["check_dungeon_lobby_user_count"] = new TriggerDefinitionOverride("check_dungeon_lobby_user_count") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ConditionOverride["check_npc_additional_effect"] = new TriggerDefinitionOverride("check_npc_additional_effect") { - Names = BuildNameOverride(("spawnPointID", "spawnId"), ("additionalEffectID", "additionalEffectId")), - Types = BuildTypeOverride(("spawnId", Required), ("additionalEffectId", Required), ("level", Required)), - }; - ConditionOverride["check_npc_damage"] = new TriggerDefinitionOverride("npc_damage") { - Names = BuildNameOverride(("spawnPointID", "spawnId")), - Types = BuildTypeOverride(("spawnId", Required), ("damageRate", Required), ("operator", "GreaterEqual")), - Compare = BuildCompareOverride("damageRate", "operator", "GreaterEqual"), - }; - ConditionOverride["check_npc_extra_data"] = new TriggerDefinitionOverride("npc_extra_data") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("spawnPointId", Required), ("extraDataKey", Required), ("extraDataValue", Required)), - Compare = BuildCompareOverride("extraDataValue", "operator", Required), - }; - ConditionOverride["check_npc_hp"] = new TriggerDefinitionOverride("npc_hp") { - Names = BuildNameOverride(("spawnPointId", "spawnId")), - Types = BuildTypeOverride(("value", Required), ("spawnId", Required), ("isRelative", Required)), - Compare = BuildCompareOverride("value", "compare", Required), - }; - ConditionOverride["npc_is_dead_by_string_id"] = new TriggerDefinitionOverride("npc_is_dead_by_string_id") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("stringId", Required)), - }; - ConditionOverride["check_same_user_tag"] = new TriggerDefinitionOverride("check_same_user_tag") { - Names = BuildNameOverride(("triggerBoxID", "boxId")), - Types = BuildTypeOverride(("boxId", Required)), - }; - ConditionOverride["check_user"] = new TriggerDefinitionOverride("check_user") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ConditionOverride["check_user_count"] = new TriggerDefinitionOverride("user_count") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("checkCount", null)), - Compare = BuildCompareOverride("checkCount", ""), - }; - ConditionOverride["count_users"] = new TriggerDefinitionOverride("count_users") { - Names = BuildNameOverride(("arg1", "boxId"), ("arg2", "minUsers"), ("arg3", "operator"), ("userTagID", "userTagId")), - Types = BuildTypeOverride(("boxId", Required), ("minUsers", Required), ("operator", "GreaterEqual"), ("userTagId", null)), - Compare = BuildCompareOverride("minUsers", "operator", "GreaterEqual"), - }; - ConditionOverride["day_of_week"] = new TriggerDefinitionOverride("day_of_week") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("dayOfWeeks", Required)), - Compare = BuildCompareOverride("dayOfWeeks", "", "in"), - }; - ConditionOverride["detect_liftable_object"] = new TriggerDefinitionOverride("detect_liftable_object") { - Names = BuildNameOverride(("triggerBoxIDs", "boxIds"), ("itemID", "itemId")), - Types = BuildTypeOverride(("boxIds", Required), ("itemId", Required)), - }; - ConditionOverride["dungeon_check_play_time"] = new TriggerDefinitionOverride("dungeon_play_time") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("playSeconds", Required), ("operator", "GreaterEqual")), - Compare = BuildCompareOverride("playSeconds", "operator", "GreaterEqual"), - }; - ConditionOverride["dungeon_check_state"] = new TriggerDefinitionOverride("dungeon_state") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - Compare = BuildCompareOverride("checkState", ""), - }; - ConditionOverride["dungeon_first_user_mission_score"] = new TriggerDefinitionOverride("dungeon_first_user_mission_score") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("score", Required), ("operator", "GreaterEqual")), - Compare = BuildCompareOverride("score", "operator", "GreaterEqual"), - }; - ConditionOverride["dungeon_id"] = new TriggerDefinitionOverride("dungeon_id") { - Names = BuildNameOverride(("dungeonID", "dungeonId")), - Types = BuildTypeOverride(("dungeonId", Required)), - Compare = BuildCompareOverride("dungeonId", ""), - }; - ConditionOverride["dungeon_level"] = new TriggerDefinitionOverride("dungeon_level") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("level", Required)), - Compare = BuildCompareOverride("level", ""), - }; - ConditionOverride["dungeon_max_user_count"] = new TriggerDefinitionOverride("dungeon_max_user_count") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("value", Required)), - Compare = BuildCompareOverride("value", ""), - }; - ConditionOverride["dungeon_round_require"] = new TriggerDefinitionOverride("dungeon_round") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("round", Required)), - Compare = BuildCompareOverride("round", ""), - }; - ConditionOverride["dungeon_time_out"] = new TriggerDefinitionOverride("dungeon_timeout") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ConditionOverride["dungeon_variable"] = new TriggerDefinitionOverride("dungeon_variable") { - Names = BuildNameOverride(("varID", "varId")), - Types = BuildTypeOverride(("varId", Required), ("value", Required)), - Compare = BuildCompareOverride("value", ""), - }; - ConditionOverride["guild_vs_game_scored_team"] = new TriggerDefinitionOverride("guild_vs_game_scored_team") { - Names = BuildNameOverride(("teamID", "teamId")), - Types = BuildTypeOverride(("teamId", Required)), - }; - ConditionOverride["guild_vs_game_winner_team"] = new TriggerDefinitionOverride("guild_vs_game_winner_team") { - Names = BuildNameOverride(("teamID", "teamId")), - Types = BuildTypeOverride(("teamId", Required)), - }; - ConditionOverride["is_dungeon_room"] = new TriggerDefinitionOverride("is_dungeon_room") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ConditionOverride["is_playing_maple_survival"] = new TriggerDefinitionOverride("is_playing_maple_survival") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(), - }; - ConditionOverride["monster_dead"] = new TriggerDefinitionOverride("monster_dead") { - Names = BuildNameOverride(("arg1", "spawnIds"), ("arg2", "autoTarget")), - Types = BuildTypeOverride(("spawnIds", Required), ("autoTarget", "True")), - }; - ConditionOverride["monster_in_combat"] = new TriggerDefinitionOverride("monster_in_combat") { - Names = BuildNameOverride(("arg1", "spawnIds")), - Types = BuildTypeOverride(("spawnIds", Required)), - }; - ConditionOverride["npc_detected"] = new TriggerDefinitionOverride("npc_detected") { - Names = BuildNameOverride(("arg1", "boxId"), ("arg2", "spawnIds")), - Types = BuildTypeOverride(("boxId", Required), ("spawnIds", Required)), - }; - ConditionOverride["object_interacted"] = new TriggerDefinitionOverride("object_interacted") { - Names = BuildNameOverride(("arg1", "interactIds"), ("arg2", "state"), ("ar2", "state")), - Types = BuildTypeOverride(("interactIds", Required), ("state", "0")), - }; - ConditionOverride["pvp_zone_ended"] = new TriggerDefinitionOverride("pvp_zone_ended") { - Names = BuildNameOverride(("arg1", "boxId")), - Types = BuildTypeOverride(("boxId", Required)), - }; - ConditionOverride["quest_user_detected"] = new TriggerDefinitionOverride("quest_user_detected") { - Names = BuildNameOverride(("arg1", "boxIds"), ("arg2", "questIds"), ("arg3", "questStates"), ("arg4", "jobCode")), - Types = BuildTypeOverride(("boxIds", Required), ("questIds", Required), ("questStates", Required), ("jobCode", null)), - }; - ConditionOverride["random_condition"] = new TriggerDefinitionOverride("random_condition") { - Names = BuildNameOverride(("arg1", "weight")), - Types = BuildTypeOverride(("weight", Required)), - }; - ConditionOverride["score_board_compare"] = new TriggerDefinitionOverride("score_board_score") { - Names = BuildNameOverride(("compareOp", "operator")), - Types = BuildTypeOverride(("operator", "GreaterEqual"), ("score", Required)), - Compare = BuildCompareOverride("score", "operator", "GreaterEqual"), - }; - ConditionOverride["shadow_expedition_reach_point"] = new TriggerDefinitionOverride("shadow_expedition_points") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("point", Required)), - Compare = BuildCompareOverride("point", "", "GreaterEqual"), - }; - ConditionOverride["time_expired"] = new TriggerDefinitionOverride("time_expired") { - Names = BuildNameOverride(("arg1", "timerId")), - Types = BuildTypeOverride(("timerId", Required)), - }; - ConditionOverride["user_detected"] = new TriggerDefinitionOverride("user_detected") { - Names = BuildNameOverride(("arg1", "boxIds"), ("arg2", "jobCode")), - Types = BuildTypeOverride(("boxIds", Required), ("jobCode", null)), - }; - ConditionOverride["user_value"] = new TriggerDefinitionOverride("user_value") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("key", Required), ("value", Required), ("operator", "Equal")), - Compare = BuildCompareOverride("value", "operator"), - }; - ConditionOverride["wait_and_reset_tick"] = new TriggerDefinitionOverride("wait_and_reset_tick") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("waitTick", Required)), - }; - ConditionOverride["wait_seconds_user_value"] = new TriggerDefinitionOverride("wait_seconds_user_value") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("key", Required)), - }; - ConditionOverride["wait_tick"] = new TriggerDefinitionOverride("wait_tick") { - Names = BuildNameOverride(("arg1", "waitTick")), - Types = BuildTypeOverride(("waitTick", Required)), - }; - ConditionOverride["wedding_entry_in_field"] = new TriggerDefinitionOverride("wedding_entry_in_field") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("entryType", Required), ("isInField", Required)), - }; - ConditionOverride["wedding_hall_state"] = new TriggerDefinitionOverride("wedding_hall_state") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("success", null)), - Compare = BuildCompareOverride("hall_state", ""), - }; - ConditionOverride["wedding_mutual_agree_result"] = new TriggerDefinitionOverride("wedding_mutual_agree_result") { - Names = BuildNameOverride(), - Types = BuildTypeOverride(("agreeType", Required), ("success", "True")), - Compare = BuildCompareOverride("success", ""), - }; - ConditionOverride["widget_condition"] = new TriggerDefinitionOverride("widget_value") { - Names = BuildNameOverride(("arg1", "type"), ("arg2", "widgetName"), ("arg3", "condition")), - Types = BuildTypeOverride(("type", Required), ("widgetName", Required)), - Compare = BuildCompareOverride("condition", "condition", ""), - }; - } - - private static Dictionary BuildNameOverride(params (string, string)[] overrides) { - Dictionary mapping = []; - foreach ((string Old, string New) entry in overrides) { - string oldName = TriggerTranslate.ToSnakeCase(entry.Old); - string newName = TriggerTranslate.ToSnakeCase(entry.New); - Debug.Assert(!mapping.ContainsKey(oldName), $"Duplicate override key: {oldName}"); - mapping.Add(oldName, newName); - } - return mapping; - } - - private static Dictionary BuildTypeOverride(params (string, string?)[] overrides) { - Dictionary mapping = []; - foreach ((string name, string? defaultValue) in overrides) { - string argName = TriggerTranslate.ToSnakeCase(name); - Debug.Assert(!mapping.ContainsKey(argName), $"Duplicate override key: {argName}"); - mapping.Add(argName, defaultValue); - } - return mapping; - } - - // Passing an invalid string as @default - private static (string, string, string) BuildCompareOverride(string field, string op, string @default = "Equal") { - return (TriggerTranslate.ToSnakeCase(field), TriggerTranslate.ToSnakeCase(op), @default); - } -} +using System.Diagnostics; + +namespace Maple2.File.Ingest.Utils; + +internal class TriggerDefinitionOverride { + private const string Required = ""; + + // Function Name + public readonly string Name; + // Docstring description + public readonly string Description = string.Empty; + + // Parameter Names + public Dictionary Names { get; init; } = null!; + + // Parameter Types + public Dictionary Types { get; init; } = null!; + + // Comparison Operation (Only for Conditions) + public (string Field, string Op, string Default) Compare { get; init; } + + public string? FunctionSplitter { get; init; } + public Dictionary FunctionLookup { get; init; } = null!; + + private TriggerDefinitionOverride(string name, string? splitter = null) { + Name = name; + FunctionSplitter = splitter; + } + + public static readonly Dictionary ActionOverride = new Dictionary(); + public static readonly Dictionary ConditionOverride = new Dictionary(); + + static TriggerDefinitionOverride() { + // Action Override + ActionOverride["add_balloon_talk"] = new TriggerDefinitionOverride("add_balloon_talk") { + Names = BuildNameOverride(("spawnPointID", "spawnId")), + Types = BuildTypeOverride(("spawnId", null), ("duration", null), ("delayTick", null), ("npcID", null)), + }; + ActionOverride["add_buff"] = new TriggerDefinitionOverride("add_buff") { + Names = BuildNameOverride(("arg1", "boxIds"), ("arg2", "skillId"), ("arg3", "level"), ("arg4", "ignorePlayer"), ("arg5", "isSkillSet")), + Types = BuildTypeOverride(("boxIds", Required), ("skillId", Required), ("level", Required), ("ignorePlayer", "True"), ("isSkillSet", "True")), + }; + ActionOverride["add_cinematic_talk"] = new TriggerDefinitionOverride("add_cinematic_talk") { + Names = BuildNameOverride(("npcID", "npcId"), ("illustID", "illustId"), ("illust", "illustId"), ("delay", "delayTick")), + Types = BuildTypeOverride(("npcId", Required), ("duration", null), ("align", null), ("delayTick", null)), + }; + ActionOverride["add_effect_nif"] = new TriggerDefinitionOverride("add_effect_nif") { + Names = BuildNameOverride(("spawnPointID", "spawnId")), + Types = BuildTypeOverride(("spawnId", Required), ("isOutline", null), ("scale", null), ("rotateZ", null)), + }; + ActionOverride["add_user_value"] = new TriggerDefinitionOverride("add_user_value") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("value", Required)), + }; + ActionOverride["allocate_battlefield_points"] = new TriggerDefinitionOverride("allocate_battlefield_points") { + Names = BuildNameOverride(("arg1", "boxId"), ("arg2", "points")), + Types = BuildTypeOverride(("boxId", Required), ("points", Required)), + }; + ActionOverride["announce"] = new TriggerDefinitionOverride("announce") { + Names = BuildNameOverride(("arg1", "type"), ("arg2", "content")), + Types = BuildTypeOverride(("type", null), ("content", Required), ("arg3", null)), + }; + ActionOverride["arcade_boom_boom_ocean"] = new TriggerDefinitionOverride(string.Empty) { + FunctionSplitter = "type", + FunctionLookup = new Dictionary { + ["StartGame"] = new TriggerDefinitionOverride("arcade_boom_boom_ocean_start_game", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("lifeCount", Required)), + }, + ["EndGame"] = new TriggerDefinitionOverride("arcade_boom_boom_ocean_end_game", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }, + ["SetSkillScore"] = new TriggerDefinitionOverride("arcade_boom_boom_ocean_set_skill_score", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("type", Required), ("id", Required), ("score", Required)), + }, + ["StartRound"] = new TriggerDefinitionOverride("arcade_boom_boom_ocean_start_round", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("type", Required), ("round", Required), ("roundDuration", Required), ("timeScoreRate", Required)), + }, + ["ClearRound"] = new TriggerDefinitionOverride("arcade_boom_boom_ocean_clear_round", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("type", Required), ("round", Required)), + }, + }, + }; + ActionOverride["arcade_spring_farm"] = new TriggerDefinitionOverride("") { + FunctionSplitter = "type", + FunctionLookup = new Dictionary { + ["StartGame"] = new TriggerDefinitionOverride("arcade_spring_farm_start_game", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("lifeCount", Required)), + }, + ["EndGame"] = new TriggerDefinitionOverride("arcade_spring_farm_end_game", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }, + ["SetInteractScore"] = new TriggerDefinitionOverride("arcade_spring_farm_set_interact_score", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("id", Required), ("score", Required)), + }, + ["SpawnMonster"] = new TriggerDefinitionOverride("arcade_spring_farm_spawn_monster", splitter: "type") { + Names = BuildNameOverride(("spawnID", "spawnIds")), + Types = BuildTypeOverride(("spawnIds", Required), ("score", Required)), + }, + ["StartRound"] = new TriggerDefinitionOverride("arcade_spring_farm_start_round", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("uiDuration", Required), ("round", Required), ("roundDuration", Required), ("timeScoreType", Required), ("timeScoreRate", Required)), + }, + ["ClearRound"] = new TriggerDefinitionOverride("arcade_spring_farm_clear_round", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("round", Required)), + }, + }, + }; + ActionOverride["arcade_three_two_one"] = new TriggerDefinitionOverride("") { + FunctionSplitter = "type", + FunctionLookup = new Dictionary { + ["StartGame"] = new TriggerDefinitionOverride("arcade_three_two_one_start_game", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("lifeCount", Required), ("initScore", Required)), + }, + ["EndGame"] = new TriggerDefinitionOverride("arcade_three_two_one_end_game", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }, + ["StartRound"] = new TriggerDefinitionOverride("arcade_three_two_one_start_round", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("uiDuration", Required), ("round", Required)), + }, + ["ResultRound"] = new TriggerDefinitionOverride("arcade_three_two_one_result_round", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("resultDirection", Required)), + }, + ["ResultRound2"] = new TriggerDefinitionOverride("arcade_three_two_one_result_round2", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("round", Required)), + }, + ["ClearRound"] = new TriggerDefinitionOverride("arcade_three_two_one_clear_round", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("round", Required)), + }, + }, + }; + ActionOverride["arcade_three_two_one2"] = new TriggerDefinitionOverride("") { + FunctionSplitter = "type", + FunctionLookup = new Dictionary { + ["StartGame"] = new TriggerDefinitionOverride("arcade_three_two_one2_start_game", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("lifeCount", Required), ("initScore", Required)), + }, + ["EndGame"] = new TriggerDefinitionOverride("arcade_three_two_one2_end_game", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }, + ["StartRound"] = new TriggerDefinitionOverride("arcade_three_two_one2_start_round", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("uiDuration", Required), ("round", Required)), + }, + ["ResultRound"] = new TriggerDefinitionOverride("arcade_three_two_one2_result_round", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("resultDirection", Required)), + }, + ["ResultRound2"] = new TriggerDefinitionOverride("arcade_three_two_one2_result_round2", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("round", Required)), + }, + ["ClearRound"] = new TriggerDefinitionOverride("arcade_three_two_one2_clear_round", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("round", Required)), + }, + }, + }; + ActionOverride["arcade_three_two_one3"] = new TriggerDefinitionOverride("") { + FunctionSplitter = "type", + FunctionLookup = new Dictionary { + ["StartGame"] = new TriggerDefinitionOverride("arcade_three_two_one3_start_game", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("lifeCount", Required), ("initScore", Required)), + }, + ["EndGame"] = new TriggerDefinitionOverride("arcade_three_two_one3_end_game", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }, + ["StartRound"] = new TriggerDefinitionOverride("arcade_three_two_one3_start_round", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("uiDuration", Required), ("round", Required)), + }, + ["ResultRound"] = new TriggerDefinitionOverride("arcade_three_two_one3_result_round", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("resultDirection", Required)), + }, + ["ResultRound2"] = new TriggerDefinitionOverride("arcade_three_two_one3_result_round2", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("round", Required)), + }, + ["ClearRound"] = new TriggerDefinitionOverride("arcade_three_two_one3_clear_round", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("round", Required)), + }, + }, + }; + ActionOverride["change_background"] = new TriggerDefinitionOverride("change_background") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("dds", Required)), + }; + ActionOverride["change_monster"] = new TriggerDefinitionOverride("change_monster") { + Names = BuildNameOverride(("arg1", "fromSpawnId"), ("arg2", "toSpawnId")), + Types = BuildTypeOverride(("fromSpawnId", Required), ("toSpawnId", Required)), + }; + ActionOverride["close_cinematic"] = new TriggerDefinitionOverride("close_cinematic") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ActionOverride["create_field_game"] = new TriggerDefinitionOverride("create_field_game") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("type", Required), ("reset", null)), + }; + ActionOverride["create_item"] = new TriggerDefinitionOverride("create_item") { + Names = BuildNameOverride(("arg1", "spawnIds"), ("arg2", "triggerId"), ("arg3", "itemId")), + Types = BuildTypeOverride(("spawnIds", Required), ("triggerId", null), ("itemId", null), ("arg5", null)), + }; + ActionOverride["spawn_monster"] = new TriggerDefinitionOverride("spawn_monster") { + Names = BuildNameOverride(("arg1", "spawnIds"), ("arg2", "autoTarget"), ("agr2", "autoTarget"), ("arg", "autoTarget"), ("arg3", "delay")), + Types = BuildTypeOverride(("spawnIds", Required), ("autoTarget", "True"), ("delay", null)), + }; + ActionOverride["create_widget"] = new TriggerDefinitionOverride("create_widget") { + Names = BuildNameOverride(("arg1", "type")), + Types = BuildTypeOverride(("type", Required)), + }; + ActionOverride["dark_stream"] = new TriggerDefinitionOverride("dark_stream") { + FunctionSplitter = "type", + FunctionLookup = new Dictionary { + ["StartGame"] = new TriggerDefinitionOverride("dark_stream_start_game", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("round", Required)), + }, + ["SpawnMonster"] = new TriggerDefinitionOverride("dark_stream_spawn_monster", splitter: "type") { + Names = BuildNameOverride(("spawnID", "spawnIds")), + Types = BuildTypeOverride(("spawnIds", Required), ("score", Required)), + }, + ["StartRound"] = new TriggerDefinitionOverride("dark_stream_start_round", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("uiDuration", Required), ("round", Required), ("damagePenalty", Required)), + }, + ["ClearRound"] = new TriggerDefinitionOverride("dark_stream_clear_round", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("round", Required)), + }, + }, + }; + ActionOverride["debug_string"] = new TriggerDefinitionOverride("debug_string") { + Names = BuildNameOverride(("arg1", "value"), ("string", "value")), + Types = BuildTypeOverride(("value", Required)), + }; + ActionOverride["destroy_monster"] = new TriggerDefinitionOverride("destroy_monster") { + Names = BuildNameOverride(("arg1", "spawnIds"), ("agr2", "arg2")), + Types = BuildTypeOverride(("spawnIds", Required), ("arg2", "True")), + }; + ActionOverride["dungeon_clear"] = new TriggerDefinitionOverride("dungeon_clear") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("uiType", null)), + }; + ActionOverride["dungeon_clear_round"] = new TriggerDefinitionOverride("dungeon_clear_round") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("round", Required)), + }; + + ActionOverride["dungeon_close_timer"] = new TriggerDefinitionOverride("dungeon_close_timer") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + + ActionOverride["dungeon_disable_ranking"] = new TriggerDefinitionOverride("dungeon_disable_ranking") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + + ActionOverride["dungeon_enable_give_up"] = new TriggerDefinitionOverride("dungeon_enable_give_up") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("isEnable", null)), + }; + + ActionOverride["dungeon_fail"] = new TriggerDefinitionOverride("dungeon_fail") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + + ActionOverride["dungeon_mission_complete"] = new TriggerDefinitionOverride("dungeon_mission_complete") { + Names = BuildNameOverride(("missionID", "missionId")), + Types = BuildTypeOverride(("missionId", Required)), + }; + + ActionOverride["dungeon_move_lap_time_to_now"] = new TriggerDefinitionOverride("dungeon_move_lap_time_to_now") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("id", Required)), + }; + + ActionOverride["dungeon_reset_time"] = new TriggerDefinitionOverride("dungeon_reset_time") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("seconds", Required)), + }; + + ActionOverride["dungeon_set_end_time"] = new TriggerDefinitionOverride("dungeon_set_end_time") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + + ActionOverride["dungeon_set_lap_time"] = new TriggerDefinitionOverride("dungeon_set_lap_time") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("id", Required), ("lapTime", null)), + }; + + ActionOverride["dungeon_stop_timer"] = new TriggerDefinitionOverride("dungeon_stop_timer") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ActionOverride["dungeon_variable"] = new TriggerDefinitionOverride("set_dungeon_variable") { + Names = BuildNameOverride(("varID", "varId")), + Types = BuildTypeOverride(("varId", Required), ("value", Required)), + }; + ActionOverride["enable_local_camera"] = new TriggerDefinitionOverride("enable_local_camera") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("isEnable", null)), + }; + ActionOverride["enable_spawn_point_pc"] = new TriggerDefinitionOverride("enable_spawn_point_pc") { + Names = BuildNameOverride(("spawnPointID", "spawnId")), + Types = BuildTypeOverride(("spawnId", Required), ("isEnable", null)), + }; + ActionOverride["end_mini_game"] = new TriggerDefinitionOverride("end_mini_game") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("winnerBoxId", null), ("isEnable", null), ("isOnlyWinner", null)), + }; + ActionOverride["end_mini_game_round"] = new TriggerDefinitionOverride("end_mini_game_round") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("winnerBoxId", Required), ("expRate", null), ("meso", null), ("isOnlyWinner", null), ("isGainLoserBonus", null)), + }; + ActionOverride["face_emotion"] = new TriggerDefinitionOverride("face_emotion") { + Names = BuildNameOverride(("spawnPointID", "spawnId"), ("spwnPointID", "spawnId")), + Types = BuildTypeOverride(("spawnId", null)), + }; + ActionOverride["field_game_constant"] = new TriggerDefinitionOverride("field_game_constant") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("key", Required), ("value", Required), ("locale", null)), + }; + ActionOverride["field_game_message"] = new TriggerDefinitionOverride("field_game_message") { + Names = BuildNameOverride(("arg2", "script"), ("arg3", "duration")), + Types = BuildTypeOverride(("custom", null), ("type", Required), ("duration", null), ("arg1", null), ("script", Required)), + }; + ActionOverride["field_war_end"] = new TriggerDefinitionOverride("field_war_end") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("isClear", null)), + }; + ActionOverride["give_exp"] = new TriggerDefinitionOverride("give_exp") { + Names = BuildNameOverride(("arg1", "boxId"), ("arg2", "rate")), + Types = BuildTypeOverride(("boxId", Required), ("rate", "1.0"), ("arg3", null)), + }; + ActionOverride["give_guild_exp"] = new TriggerDefinitionOverride("give_guild_exp") { + Names = BuildNameOverride(("boxID", "boxId")), + Types = BuildTypeOverride(("boxId", null), ("type", Required)), + }; + ActionOverride["give_reward_content"] = new TriggerDefinitionOverride("give_reward_content") { + Names = BuildNameOverride(("rewardID", "rewardId")), + Types = BuildTypeOverride(("rewardId", Required)), + }; + ActionOverride["guide_event"] = new TriggerDefinitionOverride("guide_event") { + Names = BuildNameOverride(("eventID", "eventId")), + Types = BuildTypeOverride(("eventId", Required)), + }; + ActionOverride["guild_vs_game_end_game"] = new TriggerDefinitionOverride("guild_vs_game_end_game") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ActionOverride["guild_vs_game_give_contribution"] = new TriggerDefinitionOverride("guild_vs_game_give_contribution") { + Names = BuildNameOverride(("teamID", "teamId")), + Types = BuildTypeOverride(("teamId", Required), ("isWin", null)), + }; + ActionOverride["guild_vs_game_give_reward"] = new TriggerDefinitionOverride("guild_vs_game_give_reward") { + Names = BuildNameOverride(("teamID", "teamId")), + Types = BuildTypeOverride(("teamId", Required), ("isWin", null)), + }; + ActionOverride["guild_vs_game_log_result"] = new TriggerDefinitionOverride("guild_vs_game_log_result") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ActionOverride["guild_vs_game_log_won_by_default"] = new TriggerDefinitionOverride("guild_vs_game_log_won_by_default") { + Names = BuildNameOverride(("teamID", "teamId")), + Types = BuildTypeOverride(("teamId", Required)), + }; + ActionOverride["guild_vs_game_result"] = new TriggerDefinitionOverride("guild_vs_game_result") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ActionOverride["guild_vs_game_score_by_user"] = new TriggerDefinitionOverride("guild_vs_game_score_by_user") { + Names = BuildNameOverride(("triggerBoxID", "boxId")), + Types = BuildTypeOverride(("boxId", Required), ("score", Required)), + }; + ActionOverride["hide_guide_summary"] = new TriggerDefinitionOverride("hide_guide_summary") { + Names = BuildNameOverride(("entityID", "entityId"), ("textID", "textId")), + Types = BuildTypeOverride(("entityId", Required), ("textId", null)), + }; + ActionOverride["init_npc_rotation"] = new TriggerDefinitionOverride("init_npc_rotation") { + Names = BuildNameOverride(("arg1", "spawnIds")), + Types = BuildTypeOverride(("spawnIds", Required)), + }; + ActionOverride["kick_music_audience"] = new TriggerDefinitionOverride("kick_music_audience") { + Names = BuildNameOverride(("targetBoxID", "boxId"), ("targetPortalID", "portalId")), + Types = BuildTypeOverride(("boxId", Required), ("portalId", Required)), + }; + ActionOverride["limit_spawn_npc_count"] = new TriggerDefinitionOverride("limit_spawn_npc_count") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("limitCount", Required)), + }; + ActionOverride["lock_my_pc"] = new TriggerDefinitionOverride("lock_my_pc") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("isLock", null)), + }; + ActionOverride["mini_game_camera_direction"] = new TriggerDefinitionOverride("mini_game_camera_direction") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("boxId", Required), ("cameraId", Required)), + }; + ActionOverride["mini_game_give_exp"] = new TriggerDefinitionOverride("mini_game_give_exp") { + Names = BuildNameOverride(("isOutSide", "isOutside")), + Types = BuildTypeOverride(("boxId", Required), ("expRate", "1.0"), ("isOutside", null)), + }; + ActionOverride["mini_game_give_reward"] = new TriggerDefinitionOverride("mini_game_give_reward") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("winnerBoxId", Required), ("contentType", Required)), + }; + ActionOverride["move_npc"] = new TriggerDefinitionOverride("move_npc") { + Names = BuildNameOverride(("arg1", "spawnId"), ("arg2", "patrolName")), + Types = BuildTypeOverride(("spawnId", Required), ("patrolName", Required)), + }; + ActionOverride["move_npc_to_pos"] = new TriggerDefinitionOverride("move_npc_to_pos") { + Names = BuildNameOverride(("spawnPointID", "spawnId")), + Types = BuildTypeOverride(("spawnId", Required), ("pos", Required), ("rot", Required)), + }; + ActionOverride["move_random_user"] = new TriggerDefinitionOverride("move_random_user") { + Names = BuildNameOverride(("arg1", "mapId"), ("arg2", "portalId"), ("arg3", "boxId"), ("arg4", "count")), + Types = BuildTypeOverride(("mapId", Required), ("portalId", Required), ("boxId", Required), ("count", Required)), + }; + ActionOverride["move_to_portal"] = new TriggerDefinitionOverride("move_to_portal") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("userTagId", null), ("portalId", null), ("boxId", null)), + }; + ActionOverride["move_user"] = new TriggerDefinitionOverride("move_user") { + Names = BuildNameOverride(("arg1", "mapId"), ("arg2", "portalId"), ("arg3", "boxId")), + Types = BuildTypeOverride(("mapId", null), ("portalId", null), ("boxId", null)), + }; + ActionOverride["move_user_path"] = new TriggerDefinitionOverride("move_user_path") { + Names = BuildNameOverride(("arg1", "patrolName")), + Types = BuildTypeOverride(("patrolName", Required)), + }; + ActionOverride["move_user_to_box"] = new TriggerDefinitionOverride("move_user_to_box") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("boxId", Required), ("portalId", Required)), + }; + ActionOverride["move_user_to_pos"] = new TriggerDefinitionOverride("move_user_to_pos") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("pos", Required), ("rot", null)), + }; + ActionOverride["notice"] = new TriggerDefinitionOverride("notice") { + Names = BuildNameOverride(("arg1", "type"), ("arg2", "script")), + Types = BuildTypeOverride(("type", null), ("script", Required), ("arg3", null)), + }; + ActionOverride["npc_remove_additional_effect"] = new TriggerDefinitionOverride("npc_remove_additional_effect") { + Names = BuildNameOverride(("spawnPointID", "spawnId"), ("additionalEffectID", "additionalEffectId")), + Types = BuildTypeOverride(("spawnId", Required), ("additionalEffectId", Required)), + }; + ActionOverride["npc_to_patrol_in_box"] = new TriggerDefinitionOverride("npc_to_patrol_in_box") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("boxId", Required), ("npcId", Required)), + }; + ActionOverride["patrol_condition_user"] = new TriggerDefinitionOverride("patrol_condition_user") { + Names = BuildNameOverride(("additionalEffectID", "additionalEffectId")), + Types = BuildTypeOverride(("patrolIndex", Required), ("additionalEffectId", Required)), + }; + ActionOverride["play_scene_movie"] = new TriggerDefinitionOverride("play_scene_movie") { + Names = BuildNameOverride(("movieID", "movieId")), + Types = BuildTypeOverride(("movieId", null)), + }; + ActionOverride["play_system_sound_by_user_tag"] = new TriggerDefinitionOverride("play_system_sound_by_user_tag") { + Names = BuildNameOverride(("userTagID", "userTagId")), + Types = BuildTypeOverride(("userTagId", Required), ("soundKey", Required)), + }; + ActionOverride["play_system_sound_in_box"] = new TriggerDefinitionOverride("play_system_sound_in_box") { + Names = BuildNameOverride(("arg1", "boxIds"), ("arg2", "sound")), + Types = BuildTypeOverride(("boxIds", null), ("sound", Required)), + }; + ActionOverride["random_additional_effect"] = new TriggerDefinitionOverride("random_additional_effect") { + Names = BuildNameOverride(("Target", "target"), ("triggerBoxID", "boxId"), ("spawnPointID", "spawnId"), ("arg1", "boxIds"), ("additionalEffectID", "additionalEffectId")), + Types = BuildTypeOverride(("boxId", null), ("spawnId", null), ("targetCount", null), ("tick", null), ("waitTick", null), ("additionalEffectId", null)), + }; + ActionOverride["remove_balloon_talk"] = new TriggerDefinitionOverride("remove_balloon_talk") { + Names = BuildNameOverride(("spawnPointID", "spawnId")), + Types = BuildTypeOverride(("spawnId", null)), + }; + ActionOverride["remove_buff"] = new TriggerDefinitionOverride("remove_buff") { + Names = BuildNameOverride(("arg1", "boxId"), ("arg2", "skillId"), ("arg3", "isPlayer")), + Types = BuildTypeOverride(("boxId", Required), ("skillId", Required), ("isPlayer", null)), + }; + ActionOverride["remove_cinematic_talk"] = new TriggerDefinitionOverride("remove_cinematic_talk") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ActionOverride["remove_effect_nif"] = new TriggerDefinitionOverride("remove_effect_nif") { + Names = BuildNameOverride(("spawnPointID", "spawnId")), + Types = BuildTypeOverride(("spawnId", Required)), + }; + ActionOverride["reset_camera"] = new TriggerDefinitionOverride("reset_camera") { + Names = BuildNameOverride(("arg1", "interpolationTime"), ("arg2", "interpolationTime")), + Types = BuildTypeOverride(("interpolationTime", null)), + }; + ActionOverride["reset_timer"] = new TriggerDefinitionOverride("reset_timer") { + Names = BuildNameOverride(("arg1", "timerId")), + Types = BuildTypeOverride(), + }; + ActionOverride["room_expire"] = new TriggerDefinitionOverride("room_expire") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ActionOverride["score_board_create"] = new TriggerDefinitionOverride("score_board_create") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("type", null), ("title", null), ("maxScore", null)), + }; + ActionOverride["score_board_remove"] = new TriggerDefinitionOverride("score_board_remove") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ActionOverride["score_board_set_score"] = new TriggerDefinitionOverride("score_board_set_score") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("score", Required)), + }; + ActionOverride["select_camera"] = new TriggerDefinitionOverride("select_camera") { + Names = BuildNameOverride(("arg1", "triggerId"), ("arg2", "enable")), + Types = BuildTypeOverride(("triggerId", Required), ("enable", "True")), + }; + ActionOverride["select_camera_path"] = new TriggerDefinitionOverride("select_camera_path") { + Names = BuildNameOverride(("arg1", "pathIds"), ("arg2", "returnView")), + Types = BuildTypeOverride(("pathIds", Required), ("returnView", "True")), + }; + ActionOverride["set_achievement"] = new TriggerDefinitionOverride("set_achievement") { + Names = BuildNameOverride(("arg1", "triggerId"), ("arg2", "type"), ("arg3", "achieve")), + Types = BuildTypeOverride(("triggerId", null)), + }; + ActionOverride["set_actor"] = new TriggerDefinitionOverride("set_actor") { + Names = BuildNameOverride(("arg1", "triggerId"), ("arg2", "visible"), ("arg3", "initialSequence")), + Types = BuildTypeOverride(("triggerId", Required), ("visible", null), ("arg4", null), ("arg5", null)), + }; + ActionOverride["set_agent"] = new TriggerDefinitionOverride("set_agent") { + Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "visible")), + Types = BuildTypeOverride(("triggerIds", Required), ("visible", null)), + }; + ActionOverride["set_ai_extra_data"] = new TriggerDefinitionOverride("set_ai_extra_data") { + Names = BuildNameOverride(("boxID", "boxId")), + Types = BuildTypeOverride(("key", Required), ("value", Required), ("isModify", null), ("boxId", null)), + }; + ActionOverride["set_ambient_light"] = new TriggerDefinitionOverride("set_ambient_light") { + Names = BuildNameOverride(("arg1", "primary"), ("arg2", "secondary"), ("arg3", "tertiary")), + Types = BuildTypeOverride(("primary", Required), ("secondary", null), ("tertiary", null)), + }; + ActionOverride["set_breakable"] = new TriggerDefinitionOverride("set_breakable") { + Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "enable")), + Types = BuildTypeOverride(("triggerIds", Required), ("enable", null)), + }; + ActionOverride["set_cinematic_intro"] = new TriggerDefinitionOverride("set_cinematic_intro") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ActionOverride["set_cinematic_ui"] = new TriggerDefinitionOverride("set_cinematic_ui") { + Names = BuildNameOverride(("arg1", "type"), ("arg2", "script")), + Types = BuildTypeOverride(("type", Required), ("script", null), ("arg3", null)), + }; + ActionOverride["set_dialogue"] = new TriggerDefinitionOverride("set_dialogue") { + Names = BuildNameOverride(("arg1", "type"), ("arg2", "spawnId"), ("arg3", "script"), ("arg4", "time")), + Types = BuildTypeOverride(("type", Required), ("spawnId", null), ("script", Required), ("time", null), ("arg5", null), ("align", null)), + }; + ActionOverride["set_cube"] = new TriggerDefinitionOverride("set_cube") { + Names = BuildNameOverride(("IDs", "triggerIds"), ("arg1", "triggerIds"), ("arg2", "isVisible")), + Types = BuildTypeOverride(("triggerIds", Required), ("isVisible", null), ("randomCount", null)), + }; + ActionOverride["set_directional_light"] = new TriggerDefinitionOverride("set_directional_light") { + Names = BuildNameOverride(("arg1", "diffuseColor"), ("arg2", "specularColor")), + Types = BuildTypeOverride(("diffuseColor", Required), ("specularColor", null)), + }; + ActionOverride["set_effect"] = new TriggerDefinitionOverride("set_effect") { + Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "visible"), ("arg3", "startDelay"), ("arg4", "interval")), + Types = BuildTypeOverride(("triggerIds", null), ("visible", null), ("startDelay", null), ("interval", null)), + }; + ActionOverride["set_event_ui"] = new TriggerDefinitionOverride(string.Empty) { + FunctionSplitter = "arg1", + FunctionLookup = new Dictionary { + ["0"] = new TriggerDefinitionOverride("set_event_ui_round", splitter: "arg1") { + Names = BuildNameOverride(("arg2", "rounds"), ("arg4", "vOffset")), + Types = BuildTypeOverride(("rounds", Required), ("arg3", null), ("vOffset", null)), + }, + ["1"] = new TriggerDefinitionOverride("set_event_ui_script", splitter: "arg1") { + Names = BuildNameOverride(("arg1", "type"), ("arg2", "script"), ("arg3", "duration"), ("arg4", "boxIds")), + Types = BuildTypeOverride(("type", Required), ("script", null), ("duration", Required), ("boxIds", null)), + }, + ["2"] = new TriggerDefinitionOverride("set_event_ui_countdown", splitter: "arg1") { + Names = BuildNameOverride(("arg2", "script"), ("arg3", "roundCountdown"), ("arg4", "boxIds")), + Types = BuildTypeOverride(("script", null), ("roundCountdown", Required), ("boxIds", null)), + }, + ["3"] = new TriggerDefinitionOverride("set_event_ui_script", splitter: "arg1") { + Names = BuildNameOverride(("arg1", "type"), ("arg2", "script"), ("arg3", "duration"), ("arg4", "boxIds")), + Types = BuildTypeOverride(("type", Required), ("script", null), ("duration", Required), ("boxIds", null)), + }, + ["4"] = new TriggerDefinitionOverride("set_event_ui_script", splitter: "arg1") { + Names = BuildNameOverride(("arg1", "type"), ("arg2", "script"), ("arg3", "duration"), ("arg4", "boxIds")), + Types = BuildTypeOverride(("type", Required), ("script", null), ("duration", Required), ("boxIds", null)), + }, + ["5"] = new TriggerDefinitionOverride("set_event_ui_script", splitter: "arg1") { + Names = BuildNameOverride(("arg1", "type"), ("arg2", "script"), ("arg3", "duration"), ("arg4", "boxIds")), + Types = BuildTypeOverride(("type", Required), ("script", null), ("duration", Required), ("boxIds", null)), + }, + ["6"] = new TriggerDefinitionOverride("set_event_ui_script", splitter: "arg1") { + Names = BuildNameOverride(("arg1", "type"), ("arg2", "script"), ("arg3", "duration"), ("arg4", "boxIds")), + Types = BuildTypeOverride(("type", Required), ("script", null), ("duration", Required), ("boxIds", null)), + }, + ["7"] = new TriggerDefinitionOverride("set_event_ui_script", splitter: "arg1") { + Names = BuildNameOverride(("arg1", "type"), ("arg2", "script"), ("arg3", "duration"), ("arg4", "boxIds")), + Types = BuildTypeOverride(("type", Required), ("script", null), ("duration", Required), ("boxIds", null)), + }, + }, + }; + ActionOverride["set_gravity"] = new TriggerDefinitionOverride("set_gravity") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("gravity", Required)), + }; + ActionOverride["set_interact_object"] = new TriggerDefinitionOverride("set_interact_object") { + Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "state")), + Types = BuildTypeOverride(("triggerIds", Required), ("state", Required), ("arg4", null), ("arg3", null)), + }; + ActionOverride["set_ladder"] = new TriggerDefinitionOverride("set_ladder") { + Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "visible"), ("arg3", "enable"), ("arg4", "fade")), + Types = BuildTypeOverride(("triggerIds", Required), ("visible", null), ("enable", null), ("fade", null)), + }; + ActionOverride["set_local_camera"] = new TriggerDefinitionOverride("set_local_camera") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("cameraId", Required), ("enable", null)), + }; + ActionOverride["set_mesh"] = new TriggerDefinitionOverride("set_mesh") { + Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "visible"), ("arg3", "startDelay"), ("arg4", "interval"), ("arg5", "fade")), + Types = BuildTypeOverride(("triggerIds", Required), ("visible", null), ("startDelay", null), ("interval", null), ("fade", null)), + }; + ActionOverride["set_mesh_animation"] = new TriggerDefinitionOverride("set_mesh_animation") { + Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "visible"), ("arg3", "startDelay"), ("arg4", "interval")), + Types = BuildTypeOverride(("triggerIds", Required), ("visible", null), ("startDelay", null), ("interval", null)), + }; + ActionOverride["set_mini_game_area_for_hack"] = new TriggerDefinitionOverride("set_mini_game_area_for_hack") { + Names = BuildNameOverride(("boxID", "boxId")), + Types = BuildTypeOverride(("boxId", Required)), + }; + ActionOverride["set_npc_duel_hp_bar"] = new TriggerDefinitionOverride("set_npc_duel_hp_bar") { + Names = BuildNameOverride(("spawnPointID", "spawnId")), + Types = BuildTypeOverride(("isOpen", null), ("spawnId", Required), ("durationTick", null), ("npcHpStep", null)), + }; + ActionOverride["set_npc_emotion_loop"] = new TriggerDefinitionOverride("set_npc_emotion_loop") { + Names = BuildNameOverride(("arg1", "spawnId"), ("arg2", "sequenceName"), ("arg3", "duration"), ("arg", "duration")), + Types = BuildTypeOverride(("spawnId", Required), ("duration", null)), + }; + ActionOverride["set_npc_emotion_sequence"] = new TriggerDefinitionOverride("set_npc_emotion_sequence") { + Names = BuildNameOverride(("arg1", "spawnId"), ("arg2", "sequenceName"), ("arg3", "durationTick")), + Types = BuildTypeOverride(("spawnId", Required), ("sequenceName", Required), ("durationTick", null)), + }; + ActionOverride["set_npc_rotation"] = new TriggerDefinitionOverride("set_npc_rotation") { + Names = BuildNameOverride(("arg1", "spawnId"), ("arg2", "rotation")), + Types = BuildTypeOverride(("spawnId", Required), ("rotation", Required)), + }; + ActionOverride["set_onetime_effect"] = new TriggerDefinitionOverride("set_onetime_effect") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("id", null), ("enable", null)), + }; + ActionOverride["set_pc_emotion_loop"] = new TriggerDefinitionOverride("set_pc_emotion_loop") { + Names = BuildNameOverride(("arg1", "sequenceName"), ("arg2", "duration"), ("arg3", "loop")), + Types = BuildTypeOverride(("sequenceName", Required), ("duration", null), ("loop", null)), + }; + ActionOverride["set_pc_emotion_sequence"] = new TriggerDefinitionOverride("set_pc_emotion_sequence") { + Names = BuildNameOverride(("arg1", "sequenceNames")), + Types = BuildTypeOverride(("sequenceNames", Required)), + }; + ActionOverride["set_pc_rotation"] = new TriggerDefinitionOverride("set_pc_rotation") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("rotation", Required)), + }; + ActionOverride["set_photo_studio"] = new TriggerDefinitionOverride("set_photo_studio") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("isEnable", null)), + }; + ActionOverride["set_portal"] = new TriggerDefinitionOverride("set_portal") { + Names = BuildNameOverride(("arg1", "portalId"), ("arg2", "visible"), ("arg3", "enable"), ("arg4", "minimapVisible"), ("arg", "minimapVisible")), + Types = BuildTypeOverride(("portalId", Required), ("visible", null), ("enable", null), ("minimapVisible", null), ("arg5", null)), + }; + ActionOverride["set_pvp_zone"] = new TriggerDefinitionOverride("set_pvp_zone") { + Names = BuildNameOverride(("arg1", "boxId"), ("arg2", "prepareTime"), ("arg3", "matchTime"), ("arg4", "additionalEffectId"), ("arg5", "type"), ("arg6", "boxIds")), + Types = BuildTypeOverride(("boxId", Required), ("prepareTime", Required), ("matchTime", Required), ("additionalEffectId", null), ("type", null), ("boxIds", null)), + }; + ActionOverride["set_quest_accept"] = new TriggerDefinitionOverride("set_quest_accept") { + Names = BuildNameOverride(("questID", "questId"), ("arg1", "questId")), + Types = BuildTypeOverride(("questId", Required)), + }; + ActionOverride["set_quest_complete"] = new TriggerDefinitionOverride("set_quest_complete") { + Names = BuildNameOverride(("questID", "questId")), + Types = BuildTypeOverride(("questId", Required)), + }; + ActionOverride["set_random_mesh"] = new TriggerDefinitionOverride("set_random_mesh") { + Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "visible"), ("arg3", "startDelay"), ("arg4", "interval"), ("arg5", "fade")), + Types = BuildTypeOverride(("triggerIds", Required), ("visible", null), ("startDelay", null), ("interval", null), ("fade", null)), + }; + ActionOverride["set_rope"] = new TriggerDefinitionOverride("set_rope") { + Names = BuildNameOverride(("arg1", "triggerId"), ("arg2", "visible"), ("arg3", "enable"), ("arg4", "fade")), + Types = BuildTypeOverride(("triggerId", Required), ("visible", null), ("enable", null), ("fade", null)), + }; + ActionOverride["set_scene_skip"] = new TriggerDefinitionOverride("set_scene_skip") { + Names = BuildNameOverride(("arg1", "state"), ("arg2", "action")), + Types = BuildTypeOverride(("state", null)), + }; + ActionOverride["set_skill"] = new TriggerDefinitionOverride("set_skill") { + Names = BuildNameOverride(("objectIDs", "triggerIds"), ("arg1", "triggerIds"), ("arg2", "enable"), ("isEnable", "enable")), + Types = BuildTypeOverride(("triggerIds", Required), ("enable", null)), + }; + ActionOverride["set_skip"] = new TriggerDefinitionOverride("set_skip") { + Names = BuildNameOverride(("arg1", "state")), + Types = BuildTypeOverride(("state", null)), + }; + ActionOverride["set_sound"] = new TriggerDefinitionOverride("set_sound") { + Names = BuildNameOverride(("arg1", "triggerId"), ("arg2", "enable")), + Types = BuildTypeOverride(("triggerId", Required), ("enable", null)), + }; + ActionOverride["set_state"] = new TriggerDefinitionOverride("set_state") { + Names = BuildNameOverride(("arg1", "id"), ("arg2", "states"), ("arg3", "randomize")), + Types = BuildTypeOverride(("id", Required), ("states", Required), ("randomize", null)), + }; + ActionOverride["set_time_scale"] = new TriggerDefinitionOverride("set_time_scale") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("enable", null), ("startScale", null), ("endScale", null), ("duration", null), ("interpolator", null)), + }; + ActionOverride["set_timer"] = new TriggerDefinitionOverride("set_timer") { + Names = BuildNameOverride(("arg1", "timerId"), ("arg2", "seconds"), ("arg3", "autoRemove"), ("ara3", "autoRemove"), ("arg4", "display"), ("arg5", "vOffset"), ("arg6", "type")), + Types = BuildTypeOverride(("seconds", null), ("autoRemove", null), ("display", null), ("vOffset", null)), + }; + ActionOverride["set_user_value"] = new TriggerDefinitionOverride("set_user_value") { + Names = BuildNameOverride(("triggerID", "triggerId")), + Types = BuildTypeOverride(("triggerId", null), ("key", Required), ("value", Required)), + }; + ActionOverride["set_user_value_from_dungeon_reward_count"] = new TriggerDefinitionOverride("set_user_value_from_dungeon_reward_count") { + Names = BuildNameOverride(("dungeonRewardID", "dungeonRewardId")), + Types = BuildTypeOverride(("dungeonRewardId", Required)), + }; + ActionOverride["set_user_value_from_guild_vs_game_score"] = new TriggerDefinitionOverride("set_user_value_from_guild_vs_game_score") { + Names = BuildNameOverride(("teamID", "teamId")), + Types = BuildTypeOverride(("teamId", Required)), + }; + ActionOverride["set_user_value_from_user_count"] = new TriggerDefinitionOverride("set_user_value_from_user_count") { + Names = BuildNameOverride(("triggerBoxID", "triggerBoxId"), ("userTagID", "userTagId")), + Types = BuildTypeOverride(("triggerBoxId", Required), ("key", Required), ("userTagId", Required)), + }; + ActionOverride["set_visible_breakable_object"] = new TriggerDefinitionOverride("set_visible_breakable_object") { + Names = BuildNameOverride(("arg1", "triggerIds"), ("arg2", "visible")), + Types = BuildTypeOverride(("triggerIds", Required), ("visible", null)), + }; + ActionOverride["set_visible_ui"] = new TriggerDefinitionOverride("set_visible_ui") { + Names = BuildNameOverride(("uiName", "uiNames")), + Types = BuildTypeOverride(("uiNames", Required), ("visible", null)), + }; + ActionOverride["shadow_expedition"] = new TriggerDefinitionOverride("") { + FunctionSplitter = "type", + FunctionLookup = new Dictionary { + ["OpenBossGauge"] = new TriggerDefinitionOverride("shadow_expedition_open_boss_gauge", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("maxGaugePoint", Required)), + }, + ["CloseBossGauge"] = new TriggerDefinitionOverride("shadow_expedition_close_boss_gauge", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }, + }, + }; + ActionOverride["show_caption"] = new TriggerDefinitionOverride("show_caption") { + Names = BuildNameOverride(("offestRateX", "offsetRateX")), + Types = BuildTypeOverride(("type", Required), ("title", Required), ("align", null), ("offsetRateX", null), ("offsetRateY", null), ("duration", null), ("scale", null)), + }; + ActionOverride["show_count_ui"] = new TriggerDefinitionOverride("show_count_ui") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("text", Required), ("stage", null), ("count", Required), ("soundType", "1")), + }; + ActionOverride["show_event_result"] = new TriggerDefinitionOverride("show_event_result") { + Names = BuildNameOverride(("userTagID", "userTagId"), ("triggerBoxID", "triggerBoxId"), ("isOutSide", "isOutside")), + Types = BuildTypeOverride(("type", Required), ("text", Required), ("duration", null), ("userTagId", null), ("triggerBoxId", null), ("isOutside", null)), + }; + ActionOverride["show_guide_summary"] = new TriggerDefinitionOverride("show_guide_summary") { + Names = BuildNameOverride(("entityID", "entityId"), ("textID", "textId"), ("durationTime", "duration")), + Types = BuildTypeOverride(("entityId", Required), ("textId", null), ("duration", null)), + }; + ActionOverride["show_round_ui"] = new TriggerDefinitionOverride("show_round_ui") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("round", Required), ("duration", null), ("isFinalRound", null)), + }; + ActionOverride["side_npc_talk"] = new TriggerDefinitionOverride("") { + Types = BuildTypeOverride(("type", "talk")), + FunctionSplitter = "type", + FunctionLookup = new Dictionary { + ["talk"] = new TriggerDefinitionOverride("side_npc_talk", splitter: "type") { + Names = BuildNameOverride(("npcID", "npcId")), + Types = BuildTypeOverride(("npcId", Required), ("illust", Required), ("duration", Required), ("script", Required)), + }, + ["talkbottom"] = new TriggerDefinitionOverride("side_npc_talk_bottom", splitter: "type") { + Names = BuildNameOverride(("npcID", "npcId")), + Types = BuildTypeOverride(("npcId", Required), ("illust", Required), ("duration", Required), ("script", Required)), + }, + ["movie"] = new TriggerDefinitionOverride("side_npc_movie", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("usm", Required), ("duration", Required)), + }, + ["cutin"] = new TriggerDefinitionOverride("side_npc_cutin", splitter: "type") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("illust", Required), ("duration", Required)), + }, + }, + }; + ActionOverride["sight_range"] = new TriggerDefinitionOverride("sight_range") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("enable", null), ("range", Required), ("rangeZ", null), ("border", null)), + }; + ActionOverride["spawn_item_range"] = new TriggerDefinitionOverride("spawn_item_range") { + Names = BuildNameOverride(("rangeID", "rangeIds")), + Types = BuildTypeOverride(("rangeIds", Required), ("randomPickCount", Required)), + }; + ActionOverride["spawn_npc_range"] = new TriggerDefinitionOverride("spawn_npc_range") { + Names = BuildNameOverride(("rangeID", "rangeIds")), + Types = BuildTypeOverride(("rangeIds", Required), ("isAutoTargeting", null), ("randomPickCount", null), ("score", null)), + }; + ActionOverride["start_combine_spawn"] = new TriggerDefinitionOverride("start_combine_spawn") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("groupId", Required), ("isStart", null)), + }; + ActionOverride["start_mini_game"] = new TriggerDefinitionOverride("start_mini_game") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("boxId", Required), ("round", Required), ("gameName", Required), ("isShowResultUI", "True")), + }; + ActionOverride["start_mini_game_round"] = new TriggerDefinitionOverride("start_mini_game_round") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("boxId", Required), ("round", Required)), + }; + ActionOverride["start_tutorial"] = new TriggerDefinitionOverride("start_tutorial") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ActionOverride["talk_npc"] = new TriggerDefinitionOverride("talk_npc") { + Names = BuildNameOverride(("spawnPointID", "spawnId")), + Types = BuildTypeOverride(("spawnId", Required)), + }; + ActionOverride["unset_mini_game_area_for_hack"] = new TriggerDefinitionOverride("unset_mini_game_area_for_hack") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ActionOverride["use_state"] = new TriggerDefinitionOverride("use_state") { + Names = BuildNameOverride(("arg1", "id"), ("arg2", "randomize")), + Types = BuildTypeOverride(("id", null), ("randomize", null)), + }; + ActionOverride["user_tag_symbol"] = new TriggerDefinitionOverride("user_tag_symbol") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("symbol1", Required), ("symbol2", Required)), + }; + ActionOverride["user_value_to_number_mesh"] = new TriggerDefinitionOverride("user_value_to_number_mesh") { + Names = BuildNameOverride(("startMeshID", "startMeshId")), + Types = BuildTypeOverride(("key", Required), ("startMeshId", Required), ("digitCount", Required)), + }; + ActionOverride["visible_my_pc"] = new TriggerDefinitionOverride("visible_my_pc") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("isVisible", Required)), + }; + ActionOverride["weather"] = new TriggerDefinitionOverride("weather") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("weatherType", Required)), + }; + ActionOverride["wedding_broken"] = new TriggerDefinitionOverride("wedding_broken") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ActionOverride["wedding_move_user"] = new TriggerDefinitionOverride("wedding_move_user") { + Names = BuildNameOverride(("arg1", "mapId"), ("arg2", "portalIds"), ("arg3", "boxId")), + Types = BuildTypeOverride(("entryType", Required), ("mapId", Required), ("portalIds", Required), ("boxId", Required)), + }; + ActionOverride["wedding_mutual_agree"] = new TriggerDefinitionOverride("wedding_mutual_agree") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("agreeType", Required)), + }; + ActionOverride["wedding_mutual_cancel"] = new TriggerDefinitionOverride("wedding_mutual_cancel") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("agreeType", Required)), + }; + ActionOverride["wedding_set_user_emotion"] = new TriggerDefinitionOverride("wedding_set_user_emotion") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("entryType", Required), ("id", Required)), + }; + ActionOverride["wedding_set_user_look_at"] = new TriggerDefinitionOverride("wedding_set_user_look_at") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("entryType", Required), ("lookAtEntryType", Required), ("immediate", null)), + }; + ActionOverride["wedding_set_user_rotation"] = new TriggerDefinitionOverride("wedding_set_user_rotation") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("entryType", Required), ("rotation", Required), ("immediate", null)), + }; + ActionOverride["wedding_user_to_patrol"] = new TriggerDefinitionOverride("wedding_user_to_patrol") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("entryType", Required), ("patrolIndex", null)), + }; + ActionOverride["wedding_vow_complete"] = new TriggerDefinitionOverride("wedding_vow_complete") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ActionOverride["widget_action"] = new TriggerDefinitionOverride("widget_action") { + Names = BuildNameOverride(("arg1", "type"), ("arg2", "func"), ("arg3", "widgetArg")), + Types = BuildTypeOverride(("type", Required), ("func", Required), ("widgetArgNum", null)), + }; + ActionOverride["write_log"] = new TriggerDefinitionOverride("write_log") { + Names = BuildNameOverride(("arg1", "logName"), ("arg2", "triggerId"), ("arg3", "event"), ("arg4", "level"), ("arg5", "subEvent")), + Types = BuildTypeOverride(("logName", Required), ("triggerId", null), ("level", null)), + }; + + // Condition Override + ConditionOverride["all_of"] = new TriggerDefinitionOverride("all_of") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ConditionOverride["true"] = new TriggerDefinitionOverride("true") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ConditionOverride["any_one"] = new TriggerDefinitionOverride("any_one") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ConditionOverride["always"] = new TriggerDefinitionOverride("always") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("arg1", "True")), + }; + ConditionOverride["bonus_game_reward_detected"] = new TriggerDefinitionOverride("bonus_game_reward") { + Names = BuildNameOverride(("arg1", "boxId"), ("arg2", "type")), + Types = BuildTypeOverride(("boxId", Required), ("type", Required)), + Compare = BuildCompareOverride("type", ""), + }; + ConditionOverride["check_any_user_additional_effect"] = new TriggerDefinitionOverride("check_any_user_additional_effect") { + Names = BuildNameOverride(("triggerBoxID", "boxId"), ("additionalEffectID", "additionalEffectId")), + Types = BuildTypeOverride(("boxId", Required), ("additionalEffectId", Required), ("level", Required)), + }; + ConditionOverride["check_dungeon_lobby_user_count"] = new TriggerDefinitionOverride("check_dungeon_lobby_user_count") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ConditionOverride["check_npc_additional_effect"] = new TriggerDefinitionOverride("check_npc_additional_effect") { + Names = BuildNameOverride(("spawnPointID", "spawnId"), ("additionalEffectID", "additionalEffectId")), + Types = BuildTypeOverride(("spawnId", Required), ("additionalEffectId", Required), ("level", Required)), + }; + ConditionOverride["check_npc_damage"] = new TriggerDefinitionOverride("npc_damage") { + Names = BuildNameOverride(("spawnPointID", "spawnId")), + Types = BuildTypeOverride(("spawnId", Required), ("damageRate", Required), ("operator", "GreaterEqual")), + Compare = BuildCompareOverride("damageRate", "operator", "GreaterEqual"), + }; + ConditionOverride["check_npc_extra_data"] = new TriggerDefinitionOverride("npc_extra_data") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("spawnPointId", Required), ("extraDataKey", Required), ("extraDataValue", Required)), + Compare = BuildCompareOverride("extraDataValue", "operator", Required), + }; + ConditionOverride["check_npc_hp"] = new TriggerDefinitionOverride("npc_hp") { + Names = BuildNameOverride(("spawnPointId", "spawnId")), + Types = BuildTypeOverride(("value", Required), ("spawnId", Required), ("isRelative", Required)), + Compare = BuildCompareOverride("value", "compare", Required), + }; + ConditionOverride["npc_is_dead_by_string_id"] = new TriggerDefinitionOverride("npc_is_dead_by_string_id") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("stringId", Required)), + }; + ConditionOverride["check_same_user_tag"] = new TriggerDefinitionOverride("check_same_user_tag") { + Names = BuildNameOverride(("triggerBoxID", "boxId")), + Types = BuildTypeOverride(("boxId", Required)), + }; + ConditionOverride["check_user"] = new TriggerDefinitionOverride("check_user") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ConditionOverride["check_user_count"] = new TriggerDefinitionOverride("user_count") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("checkCount", null)), + Compare = BuildCompareOverride("checkCount", ""), + }; + ConditionOverride["count_users"] = new TriggerDefinitionOverride("count_users") { + Names = BuildNameOverride(("arg1", "boxId"), ("arg2", "minUsers"), ("arg3", "operator"), ("userTagID", "userTagId")), + Types = BuildTypeOverride(("boxId", Required), ("minUsers", Required), ("operator", "GreaterEqual"), ("userTagId", null)), + Compare = BuildCompareOverride("minUsers", "operator", "GreaterEqual"), + }; + ConditionOverride["day_of_week"] = new TriggerDefinitionOverride("day_of_week") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("dayOfWeeks", Required)), + Compare = BuildCompareOverride("dayOfWeeks", "", "in"), + }; + ConditionOverride["detect_liftable_object"] = new TriggerDefinitionOverride("detect_liftable_object") { + Names = BuildNameOverride(("triggerBoxIDs", "boxIds"), ("itemID", "itemId")), + Types = BuildTypeOverride(("boxIds", Required), ("itemId", Required)), + }; + ConditionOverride["dungeon_check_play_time"] = new TriggerDefinitionOverride("dungeon_play_time") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("playSeconds", Required), ("operator", "GreaterEqual")), + Compare = BuildCompareOverride("playSeconds", "operator", "GreaterEqual"), + }; + ConditionOverride["dungeon_check_state"] = new TriggerDefinitionOverride("dungeon_state") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + Compare = BuildCompareOverride("checkState", ""), + }; + ConditionOverride["dungeon_first_user_mission_score"] = new TriggerDefinitionOverride("dungeon_first_user_mission_score") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("score", Required), ("operator", "GreaterEqual")), + Compare = BuildCompareOverride("score", "operator", "GreaterEqual"), + }; + ConditionOverride["dungeon_id"] = new TriggerDefinitionOverride("dungeon_id") { + Names = BuildNameOverride(("dungeonID", "dungeonId")), + Types = BuildTypeOverride(("dungeonId", Required)), + Compare = BuildCompareOverride("dungeonId", ""), + }; + ConditionOverride["dungeon_level"] = new TriggerDefinitionOverride("dungeon_level") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("level", Required)), + Compare = BuildCompareOverride("level", ""), + }; + ConditionOverride["dungeon_max_user_count"] = new TriggerDefinitionOverride("dungeon_max_user_count") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("value", Required)), + Compare = BuildCompareOverride("value", ""), + }; + ConditionOverride["dungeon_round_require"] = new TriggerDefinitionOverride("dungeon_round") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("round", Required)), + Compare = BuildCompareOverride("round", ""), + }; + ConditionOverride["dungeon_time_out"] = new TriggerDefinitionOverride("dungeon_timeout") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ConditionOverride["dungeon_variable"] = new TriggerDefinitionOverride("dungeon_variable") { + Names = BuildNameOverride(("varID", "varId")), + Types = BuildTypeOverride(("varId", Required), ("value", Required)), + Compare = BuildCompareOverride("value", ""), + }; + ConditionOverride["guild_vs_game_scored_team"] = new TriggerDefinitionOverride("guild_vs_game_scored_team") { + Names = BuildNameOverride(("teamID", "teamId")), + Types = BuildTypeOverride(("teamId", Required)), + }; + ConditionOverride["guild_vs_game_winner_team"] = new TriggerDefinitionOverride("guild_vs_game_winner_team") { + Names = BuildNameOverride(("teamID", "teamId")), + Types = BuildTypeOverride(("teamId", Required)), + }; + ConditionOverride["is_dungeon_room"] = new TriggerDefinitionOverride("is_dungeon_room") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ConditionOverride["is_playing_maple_survival"] = new TriggerDefinitionOverride("is_playing_maple_survival") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(), + }; + ConditionOverride["monster_dead"] = new TriggerDefinitionOverride("monster_dead") { + Names = BuildNameOverride(("arg1", "spawnIds"), ("arg2", "autoTarget")), + Types = BuildTypeOverride(("spawnIds", Required), ("autoTarget", "True")), + }; + ConditionOverride["monster_in_combat"] = new TriggerDefinitionOverride("monster_in_combat") { + Names = BuildNameOverride(("arg1", "spawnIds")), + Types = BuildTypeOverride(("spawnIds", Required)), + }; + ConditionOverride["npc_detected"] = new TriggerDefinitionOverride("npc_detected") { + Names = BuildNameOverride(("arg1", "boxId"), ("arg2", "spawnIds")), + Types = BuildTypeOverride(("boxId", Required), ("spawnIds", Required)), + }; + ConditionOverride["object_interacted"] = new TriggerDefinitionOverride("object_interacted") { + Names = BuildNameOverride(("arg1", "interactIds"), ("arg2", "state"), ("ar2", "state")), + Types = BuildTypeOverride(("interactIds", Required), ("state", "0")), + }; + ConditionOverride["pvp_zone_ended"] = new TriggerDefinitionOverride("pvp_zone_ended") { + Names = BuildNameOverride(("arg1", "boxId")), + Types = BuildTypeOverride(("boxId", Required)), + }; + ConditionOverride["quest_user_detected"] = new TriggerDefinitionOverride("quest_user_detected") { + Names = BuildNameOverride(("arg1", "boxIds"), ("arg2", "questIds"), ("arg3", "questStates"), ("arg4", "jobCode")), + Types = BuildTypeOverride(("boxIds", Required), ("questIds", Required), ("questStates", Required), ("jobCode", null)), + }; + ConditionOverride["random_condition"] = new TriggerDefinitionOverride("random_condition") { + Names = BuildNameOverride(("arg1", "weight")), + Types = BuildTypeOverride(("weight", Required)), + }; + ConditionOverride["score_board_compare"] = new TriggerDefinitionOverride("score_board_score") { + Names = BuildNameOverride(("compareOp", "operator")), + Types = BuildTypeOverride(("operator", "GreaterEqual"), ("score", Required)), + Compare = BuildCompareOverride("score", "operator", "GreaterEqual"), + }; + ConditionOverride["shadow_expedition_reach_point"] = new TriggerDefinitionOverride("shadow_expedition_points") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("point", Required)), + Compare = BuildCompareOverride("point", "", "GreaterEqual"), + }; + ConditionOverride["time_expired"] = new TriggerDefinitionOverride("time_expired") { + Names = BuildNameOverride(("arg1", "timerId")), + Types = BuildTypeOverride(("timerId", Required)), + }; + ConditionOverride["user_detected"] = new TriggerDefinitionOverride("user_detected") { + Names = BuildNameOverride(("arg1", "boxIds"), ("arg2", "jobCode")), + Types = BuildTypeOverride(("boxIds", Required), ("jobCode", null)), + }; + ConditionOverride["user_value"] = new TriggerDefinitionOverride("user_value") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("key", Required), ("value", Required), ("operator", "Equal")), + Compare = BuildCompareOverride("value", "operator"), + }; + ConditionOverride["wait_and_reset_tick"] = new TriggerDefinitionOverride("wait_and_reset_tick") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("waitTick", Required)), + }; + ConditionOverride["wait_seconds_user_value"] = new TriggerDefinitionOverride("wait_seconds_user_value") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("key", Required)), + }; + ConditionOverride["wait_tick"] = new TriggerDefinitionOverride("wait_tick") { + Names = BuildNameOverride(("arg1", "waitTick")), + Types = BuildTypeOverride(("waitTick", Required)), + }; + ConditionOverride["wedding_entry_in_field"] = new TriggerDefinitionOverride("wedding_entry_in_field") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("entryType", Required), ("isInField", Required)), + }; + ConditionOverride["wedding_hall_state"] = new TriggerDefinitionOverride("wedding_hall_state") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("success", null)), + Compare = BuildCompareOverride("hall_state", ""), + }; + ConditionOverride["wedding_mutual_agree_result"] = new TriggerDefinitionOverride("wedding_mutual_agree_result") { + Names = BuildNameOverride(), + Types = BuildTypeOverride(("agreeType", Required), ("success", "True")), + Compare = BuildCompareOverride("success", ""), + }; + ConditionOverride["widget_condition"] = new TriggerDefinitionOverride("widget_value") { + Names = BuildNameOverride(("arg1", "type"), ("arg2", "widgetName"), ("arg3", "condition")), + Types = BuildTypeOverride(("type", Required), ("widgetName", Required)), + Compare = BuildCompareOverride("condition", "condition", ""), + }; + } + + private static Dictionary BuildNameOverride(params (string, string)[] overrides) { + Dictionary mapping = []; + foreach ((string Old, string New) entry in overrides) { + string oldName = TriggerTranslate.ToSnakeCase(entry.Old); + string newName = TriggerTranslate.ToSnakeCase(entry.New); + Debug.Assert(!mapping.ContainsKey(oldName), $"Duplicate override key: {oldName}"); + mapping.Add(oldName, newName); + } + return mapping; + } + + private static Dictionary BuildTypeOverride(params (string, string?)[] overrides) { + Dictionary mapping = []; + foreach ((string name, string? defaultValue) in overrides) { + string argName = TriggerTranslate.ToSnakeCase(name); + Debug.Assert(!mapping.ContainsKey(argName), $"Duplicate override key: {argName}"); + mapping.Add(argName, defaultValue); + } + return mapping; + } + + // Passing an invalid string as @default + private static (string, string, string) BuildCompareOverride(string field, string op, string @default = "Equal") { + return (TriggerTranslate.ToSnakeCase(field), TriggerTranslate.ToSnakeCase(op), @default); + } +} diff --git a/Maple2.File.Ingest/Utils/TriggerTranslate.cs b/Maple2.File.Ingest/Utils/TriggerTranslate.cs index fdd97637d..f58a7bf3f 100644 --- a/Maple2.File.Ingest/Utils/TriggerTranslate.cs +++ b/Maple2.File.Ingest/Utils/TriggerTranslate.cs @@ -1,128 +1,128 @@ -using System.Globalization; -using System.Text; - -namespace Maple2.File.Ingest.Utils; - -public static class TriggerTranslate { - public static readonly Dictionary ActionLookup = new() { - {"대화를설정한다", "Set Dialogue"}, - {"랜덤메쉬를설정한다", "Set Random Mesh"}, - {"로그를남긴다", "Write Log"}, - {"로프를설정한다", "Set Rope"}, - {"AGENT를설정한다", "Set Agent"}, - {"NPC를이동시킨다", "Move NPC"}, - {"메쉬를설정한다", "Set Mesh"}, - {"메쉬애니를설정한다", "Set Mesh Animation"}, - {"몬스터를변경한다", "Change Monster"}, - {"몬스터를생성한다", "Spawn Monster"}, - {"몬스터소멸시킨다", "Destroy Monster"}, - {"무작위유저를이동시킨다", "Move Random User"}, - {"버프를걸어준다", "Add Buff"}, - {"버프를삭제한다", "Remove Buff"}, - {"사다리를설정한다", "Set Ladder"}, - {"사운드를설정한다", "Set Sound"}, - {"상태를사용한다", "Use State"}, - {"상태를설정한다", "Set State"}, - {"스킬을설정한다", "Set Skill"}, - {"스킵을설정한다", "Set Skip"}, - {"아이템을생성한다", "Create Item"}, - {"액터를설정한다", "Set Actor"}, - {"업적이벤트를발생시킨다", "Set Achievement"}, - {"연출를설정한다", "Set Direction"}, - {"오브젝트반응설정한다", "Set Interact Object"}, - {"움직이는발판을설정한다", "Set Breakable"}, - {"유저를경로이동시킨다", "Move User Path"}, - {"유저를이동시킨다", "Move User"}, - {"이벤트를설정한다", "Set Event"}, - {"이펙트를설정한다", "Set Effect"}, - {"PVP존을설정한다", "Set Pvp Zone"}, - {"카메라경로를선택한다", "Select Camera Path"}, - {"카메라를선택한다", "Select Camera"}, - {"카메라리셋", "Reset Camera"}, - {"타이머를설정한다", "Set Timer"}, - {"타이머를초기화한다", "Reset Timer"}, - {"포탈을설정한다", "Set Portal"}, - {"연출UI를설정한다", "Set Cinematic UI"}, - {"이벤트UI를설정한다", "Set Event UI"}, - {"공지를한다", "Announce"}, - {"전장점수를준다", "Allocate Battlefield Points"}, - }; - - public static readonly Dictionary ConditionLookup = new() { - {"랜덤조건", "Random Condition"}, - {"NPC를감지했으면", "NPC Detected"}, - {"몬스터가전투상태면", "Monster In Combat"}, - {"몬스터가죽어있으면", "Monster Dead"}, - {"무조건", "Always"}, - {"보너스게임보상받은유저를감지했으면", "Bonus Game Reward Detected"}, - {"시간이경과했으면", "Time Expired"}, - {"여러명의유저를감지했으면", "Count Users"}, - {"오브젝트가반응했으면", "Object Interacted"}, - {"유저를감지했으면", "User Detected"}, - {"PVP존이종료했으면", "PVP Zone Ended"}, - {"퀘스트유저를감지하면", "Quest User Detected"}, - }; - - public static string ToPascalCase(string text) { - if (text == null) { - throw new ArgumentNullException(nameof(text)); - } - - text = text.Replace("1st", "First") - .Replace("2nd", "Second") - .Replace("50 Meso", "Fifty Meso"); - var sb = new StringBuilder(); - foreach (char c in text) { - if (!char.IsLetterOrDigit(c)) { - sb.Append(" "); - } else { - sb.Append(c); - } - } - - TextInfo textInfo = new CultureInfo("en-US", false).TextInfo; - return textInfo.ToTitleCase(sb.ToString().ToLower()).Replace(" ", ""); - } - - public static string ToCamelName(string text) { - string pascal = ToPascalCase(text); - return pascal[..1].ToLower() + pascal[1..]; - } - - public static string ToSnakeCase(string text) { - if (text == null) { - throw new ArgumentNullException(nameof(text)); - } - text = text.Replace(" ", ""); - text = text.Replace("NPC", "Npc") - .Replace("NPc", "Npc") - .Replace("PVP", "Pvp") - .Replace("ID", "Id") - .Replace("PC", "Pc") - .Replace("UI", "Ui") - .Replace("Setpc", "SetPc") - .Replace("UnSet", "Unset") - .Replace("Emotionloop", "EmotionLoop"); - - var sb = new StringBuilder(); - sb.Append(char.ToLowerInvariant(text[0])); - for (int i = 1; i < text.Length; ++i) { - char c = text[i]; - if (char.IsUpper(c)) { - sb.Append('_'); - sb.Append(char.ToLowerInvariant(c)); - } else { - sb.Append(c); - } - } - return sb.ToString(); - } - - public static string TranslateAction(string input) { - return ActionLookup.GetValueOrDefault(input, input); - } - - public static string TranslateCondition(string input) { - return ConditionLookup.GetValueOrDefault(input, input); - } -} +using System.Globalization; +using System.Text; + +namespace Maple2.File.Ingest.Utils; + +public static class TriggerTranslate { + public static readonly Dictionary ActionLookup = new() { + {"대화를설정한다", "Set Dialogue"}, + {"랜덤메쉬를설정한다", "Set Random Mesh"}, + {"로그를남긴다", "Write Log"}, + {"로프를설정한다", "Set Rope"}, + {"AGENT를설정한다", "Set Agent"}, + {"NPC를이동시킨다", "Move NPC"}, + {"메쉬를설정한다", "Set Mesh"}, + {"메쉬애니를설정한다", "Set Mesh Animation"}, + {"몬스터를변경한다", "Change Monster"}, + {"몬스터를생성한다", "Spawn Monster"}, + {"몬스터소멸시킨다", "Destroy Monster"}, + {"무작위유저를이동시킨다", "Move Random User"}, + {"버프를걸어준다", "Add Buff"}, + {"버프를삭제한다", "Remove Buff"}, + {"사다리를설정한다", "Set Ladder"}, + {"사운드를설정한다", "Set Sound"}, + {"상태를사용한다", "Use State"}, + {"상태를설정한다", "Set State"}, + {"스킬을설정한다", "Set Skill"}, + {"스킵을설정한다", "Set Skip"}, + {"아이템을생성한다", "Create Item"}, + {"액터를설정한다", "Set Actor"}, + {"업적이벤트를발생시킨다", "Set Achievement"}, + {"연출를설정한다", "Set Direction"}, + {"오브젝트반응설정한다", "Set Interact Object"}, + {"움직이는발판을설정한다", "Set Breakable"}, + {"유저를경로이동시킨다", "Move User Path"}, + {"유저를이동시킨다", "Move User"}, + {"이벤트를설정한다", "Set Event"}, + {"이펙트를설정한다", "Set Effect"}, + {"PVP존을설정한다", "Set Pvp Zone"}, + {"카메라경로를선택한다", "Select Camera Path"}, + {"카메라를선택한다", "Select Camera"}, + {"카메라리셋", "Reset Camera"}, + {"타이머를설정한다", "Set Timer"}, + {"타이머를초기화한다", "Reset Timer"}, + {"포탈을설정한다", "Set Portal"}, + {"연출UI를설정한다", "Set Cinematic UI"}, + {"이벤트UI를설정한다", "Set Event UI"}, + {"공지를한다", "Announce"}, + {"전장점수를준다", "Allocate Battlefield Points"}, + }; + + public static readonly Dictionary ConditionLookup = new() { + {"랜덤조건", "Random Condition"}, + {"NPC를감지했으면", "NPC Detected"}, + {"몬스터가전투상태면", "Monster In Combat"}, + {"몬스터가죽어있으면", "Monster Dead"}, + {"무조건", "Always"}, + {"보너스게임보상받은유저를감지했으면", "Bonus Game Reward Detected"}, + {"시간이경과했으면", "Time Expired"}, + {"여러명의유저를감지했으면", "Count Users"}, + {"오브젝트가반응했으면", "Object Interacted"}, + {"유저를감지했으면", "User Detected"}, + {"PVP존이종료했으면", "PVP Zone Ended"}, + {"퀘스트유저를감지하면", "Quest User Detected"}, + }; + + public static string ToPascalCase(string text) { + if (text == null) { + throw new ArgumentNullException(nameof(text)); + } + + text = text.Replace("1st", "First") + .Replace("2nd", "Second") + .Replace("50 Meso", "Fifty Meso"); + var sb = new StringBuilder(); + foreach (char c in text) { + if (!char.IsLetterOrDigit(c)) { + sb.Append(" "); + } else { + sb.Append(c); + } + } + + TextInfo textInfo = new CultureInfo("en-US", false).TextInfo; + return textInfo.ToTitleCase(sb.ToString().ToLower()).Replace(" ", ""); + } + + public static string ToCamelName(string text) { + string pascal = ToPascalCase(text); + return pascal[..1].ToLower() + pascal[1..]; + } + + public static string ToSnakeCase(string text) { + if (text == null) { + throw new ArgumentNullException(nameof(text)); + } + text = text.Replace(" ", ""); + text = text.Replace("NPC", "Npc") + .Replace("NPc", "Npc") + .Replace("PVP", "Pvp") + .Replace("ID", "Id") + .Replace("PC", "Pc") + .Replace("UI", "Ui") + .Replace("Setpc", "SetPc") + .Replace("UnSet", "Unset") + .Replace("Emotionloop", "EmotionLoop"); + + var sb = new StringBuilder(); + sb.Append(char.ToLowerInvariant(text[0])); + for (int i = 1; i < text.Length; ++i) { + char c = text[i]; + if (char.IsUpper(c)) { + sb.Append('_'); + sb.Append(char.ToLowerInvariant(c)); + } else { + sb.Append(c); + } + } + return sb.ToString(); + } + + public static string TranslateAction(string input) { + return ActionLookup.GetValueOrDefault(input, input); + } + + public static string TranslateCondition(string input) { + return ConditionLookup.GetValueOrDefault(input, input); + } +} diff --git a/Maple2.Model/Common/Byte3.cs b/Maple2.Model/Common/Byte3.cs index 36a613f3e..20e2edf78 100644 --- a/Maple2.Model/Common/Byte3.cs +++ b/Maple2.Model/Common/Byte3.cs @@ -1,11 +1,11 @@ -using System.Numerics; -using System.Runtime.InteropServices; - -namespace Maple2.Model.Common; - -[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 3)] -public readonly record struct Byte3(byte X, byte Y, byte Z) { - public static implicit operator Byte3(Vector3 vector) { - return new Byte3((byte) MathF.Round(vector.X), (byte) MathF.Round(vector.Y), (byte) MathF.Round(vector.Z)); - } -} +using System.Numerics; +using System.Runtime.InteropServices; + +namespace Maple2.Model.Common; + +[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 3)] +public readonly record struct Byte3(byte X, byte Y, byte Z) { + public static implicit operator Byte3(Vector3 vector) { + return new Byte3((byte) MathF.Round(vector.X), (byte) MathF.Round(vector.Y), (byte) MathF.Round(vector.Z)); + } +} diff --git a/Maple2.Model/Common/Color.cs b/Maple2.Model/Common/Color.cs index 0706ccb84..259a7244b 100644 --- a/Maple2.Model/Common/Color.cs +++ b/Maple2.Model/Common/Color.cs @@ -1,57 +1,57 @@ -using System.Runtime.InteropServices; -using System.Text.Json.Serialization; - -namespace Maple2.Model.Common; - -[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 4)] -public readonly record struct Color(byte Blue, byte Green, byte Red, byte Alpha) { - public override string ToString() => $"ARGB({Alpha:X2}, {Red:X2}, {Green:X2}, {Blue:X2})"; -} - -[StructLayout(LayoutKind.Sequential, Size = 8)] -public readonly struct SkinColor { - public Color Primary { get; } - public Color Secondary { get; } - - public SkinColor(Color color) { - Primary = color; - Secondary = color; - } - - [JsonConstructor] - public SkinColor(Color primary, Color secondary) { - Primary = primary; - Secondary = secondary; - } - - public override string ToString() => $"Primary:{Primary}|Secondary:{Secondary}"; -} - -[StructLayout(LayoutKind.Sequential, Size = 20)] -public readonly struct EquipColor { - public Color Primary { get; } - public Color Secondary { get; } - public Color Tertiary { get; } - public int Index { get; } - public int PaletteId { get; } - - public EquipColor(Color color) { - Primary = color; - Secondary = color; - Tertiary = color; - Index = -1; - PaletteId = 0; - } - - [JsonConstructor] - public EquipColor(Color primary, Color secondary, Color tertiary, int paletteId, int index = -1) { - Primary = primary; - Secondary = secondary; - Tertiary = tertiary; - PaletteId = paletteId; - Index = index; - } - - public override string ToString() => - $"Primary:{Primary}|Secondary:{Secondary}|Tertiary:{Tertiary}|Index:{Index}"; -} +using System.Runtime.InteropServices; +using System.Text.Json.Serialization; + +namespace Maple2.Model.Common; + +[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 4)] +public readonly record struct Color(byte Blue, byte Green, byte Red, byte Alpha) { + public override string ToString() => $"ARGB({Alpha:X2}, {Red:X2}, {Green:X2}, {Blue:X2})"; +} + +[StructLayout(LayoutKind.Sequential, Size = 8)] +public readonly struct SkinColor { + public Color Primary { get; } + public Color Secondary { get; } + + public SkinColor(Color color) { + Primary = color; + Secondary = color; + } + + [JsonConstructor] + public SkinColor(Color primary, Color secondary) { + Primary = primary; + Secondary = secondary; + } + + public override string ToString() => $"Primary:{Primary}|Secondary:{Secondary}"; +} + +[StructLayout(LayoutKind.Sequential, Size = 20)] +public readonly struct EquipColor { + public Color Primary { get; } + public Color Secondary { get; } + public Color Tertiary { get; } + public int Index { get; } + public int PaletteId { get; } + + public EquipColor(Color color) { + Primary = color; + Secondary = color; + Tertiary = color; + Index = -1; + PaletteId = 0; + } + + [JsonConstructor] + public EquipColor(Color primary, Color secondary, Color tertiary, int paletteId, int index = -1) { + Primary = primary; + Secondary = secondary; + Tertiary = tertiary; + PaletteId = paletteId; + Index = index; + } + + public override string ToString() => + $"Primary:{Primary}|Secondary:{Secondary}|Tertiary:{Tertiary}|Index:{Index}"; +} diff --git a/Maple2.Model/Common/ServerTableNames.cs b/Maple2.Model/Common/ServerTableNames.cs index 7a328840f..d228b349a 100644 --- a/Maple2.Model/Common/ServerTableNames.cs +++ b/Maple2.Model/Common/ServerTableNames.cs @@ -1,27 +1,27 @@ -namespace Maple2.Model.Common; - -public static class ServerTableNames { - public const string INSTANCE_FIELD = "instancefield.xml"; - public const string SCRIPT_CONDITION = "*scriptCondition.xml"; - public const string SCRIPT_FUNCTION = "*scriptFunction.xml"; - public const string SCRIPT_EVENT = "scriptEventCondition.xml"; - public const string JOB_CONDITION = "jobConditionTable.xml"; - public const string BONUS_GAME = "bonusGame*.xml"; - public const string GLOBAL_DROP_ITEM_BOX = "globalItemDrop*.xml"; - public const string USER_STAT = "userStat*.xml"; - public const string INDIVIDUAL_DROP_ITEM = "individualItemDrop.xml"; - public const string PRESTIGE_EXP = "prestigeExpTable.xml"; - public const string PRESTIGE_ID_EXP = "prestigeIdExpTable.xml"; - public const string TIME_EVENT = "timeEventData.xml"; - public const string GAME_EVENT = "gameEvent.xml"; - public const string OX_QUIZ = "oxQuiz.xml"; - public const string ITEM_MERGE = "itemMergeOptionBase.xml"; - public const string SHOP = "shop_game_info.xml"; - public const string SHOP_ITEM = "shop_game.xml"; - public const string BEAUTY_SHOP = "shop_beauty.xml"; - public const string MERET_MARKET = "shop_merat_custom.xml"; - public const string FISH = "fish*.xml"; - public const string COMBINE_SPAWN = "combineSpawn*.xml"; - public const string ENCHANT_OPTION = "enchantOption.xml"; - public const string UNLIMITED_ENCHANT_OPTION = "unlimitedEnchantOption.xml"; -} +namespace Maple2.Model.Common; + +public static class ServerTableNames { + public const string INSTANCE_FIELD = "instancefield.xml"; + public const string SCRIPT_CONDITION = "*scriptCondition.xml"; + public const string SCRIPT_FUNCTION = "*scriptFunction.xml"; + public const string SCRIPT_EVENT = "scriptEventCondition.xml"; + public const string JOB_CONDITION = "jobConditionTable.xml"; + public const string BONUS_GAME = "bonusGame*.xml"; + public const string GLOBAL_DROP_ITEM_BOX = "globalItemDrop*.xml"; + public const string USER_STAT = "userStat*.xml"; + public const string INDIVIDUAL_DROP_ITEM = "individualItemDrop.xml"; + public const string PRESTIGE_EXP = "prestigeExpTable.xml"; + public const string PRESTIGE_ID_EXP = "prestigeIdExpTable.xml"; + public const string TIME_EVENT = "timeEventData.xml"; + public const string GAME_EVENT = "gameEvent.xml"; + public const string OX_QUIZ = "oxQuiz.xml"; + public const string ITEM_MERGE = "itemMergeOptionBase.xml"; + public const string SHOP = "shop_game_info.xml"; + public const string SHOP_ITEM = "shop_game.xml"; + public const string BEAUTY_SHOP = "shop_beauty.xml"; + public const string MERET_MARKET = "shop_merat_custom.xml"; + public const string FISH = "fish*.xml"; + public const string COMBINE_SPAWN = "combineSpawn*.xml"; + public const string ENCHANT_OPTION = "enchantOption.xml"; + public const string UNLIMITED_ENCHANT_OPTION = "unlimitedEnchantOption.xml"; +} diff --git a/Maple2.Model/Common/TableNames.cs b/Maple2.Model/Common/TableNames.cs index 6df45fdaf..4810c0e73 100644 --- a/Maple2.Model/Common/TableNames.cs +++ b/Maple2.Model/Common/TableNames.cs @@ -1,87 +1,87 @@ -namespace Maple2.Model.Common; - -public static class TableNames { - // Common table names - public const string CHAT_EMOTICON = "chatemoticon.xml"; - public const string DEFAULT_ITEMS = "defaultitems.xml"; - public const string ITEM_BREAK_INGREDIENT = "itembreakingredient.xml"; - public const string ITEM_GEMSTONE_UPGRADE = "itemgemstoneupgrade.xml"; - public const string ITEM_EXTRACTION = "itemextraction.xml"; - public const string JOB = "job.xml"; - public const string MAGIC_PATH = "magicpath.xml"; - public const string INSTRUMENT_CATEGORY_INFO = "instrumentcategoryinfo.xml"; - public const string INTERACT_OBJECT = "interactobject*.xml"; - public const string ITEM_LAPENSHARD_UPGRADE = "itemlapenshardupgrade.xml"; - public const string ITEM_SOCKET = "itemsocket.xml"; - public const string MASTERY_RECIPE = "masteryreceipe.xml"; - public const string MASTERY = "mastery.xml"; - public const string GUILD = "guild*.xml"; - public const string VIP = "vip*.xml"; - public const string INDIVIDUAL_ITEM_DROP = "individualitemdrop*.xml"; - public const string COLOR_PALETTE = "colorpalette.xml"; - public const string MERET_MARKET_CATEGORY = "meretmarketcategory.xml"; - public const string SHOP_BEAUTY_COUPON = "shop_beautycoupon.xml"; - public const string SHOP_FURNISHING = "na/shop_*.xml"; - public const string GACHA_INFO = "gacha_info.xml"; - public const string NAME_TAG_SYMBOL = "nametagsymbol.xml"; - public const string EXP = "exp*.xml"; - public const string COMMON_EXP = "commonexp.xml"; - public const string UGC_DESIGN = "ugcdesign.xml"; - public const string MASTERY_UGC_HOUSING = "masteryugchousing.xml"; - public const string UGC_HOUSING_POINT_REWARD = "ugchousingpointreward.xml"; - public const string LEARNING_QUEST = "learningquest.xml"; - public const string BLACK_MARKET_TABLE = "blackmarkettable.xml"; - public const string CHANGE_JOB = "changejob.xml"; - public const string CHAPTER_BOOK = "chapterbook.xml"; - public const string FIELD_MISSION = "fieldmission.xml"; - public const string WORLD_MAP = "newworldmap.xml"; - public const string SURVIVAL_SKIN_INFO = "maplesurvivalskininfo.xml"; - public const string BANNER = "banner.xml"; - public const string WEDDING = "wedding*.xml"; - public const string REWARD_CONTENT = "rewardcontent*.xml"; - public const string SEASON_DATA = "seasondata*.xml"; - public const string SMART_PUSH = "smartpush.xml"; - public const string AUTO_ACTION = "autoactionpricepackage.xml"; - - // Prestige / Adventure - public const string PRESTIGE_LEVEL_ABILITY = "adventurelevelability.xml"; - public const string PRESTIGE_LEVEL_REWARD = "adventurelevelreward.xml"; - public const string PRESTIGE_MISSION = "adventurelevelmission.xml"; - - // Fishing - public const string FISHING_ROD = "fishingrod.xml"; - - // Scrolls - public const string ENCHANT_SCROLL = "enchantscroll.xml"; - public const string ITEM_REMAKE_SCROLL = "itemremakescroll.xml"; - public const string ITEM_REPACKING_SCROLL = "itemrepackingscroll.xml"; - public const string ITEM_SOCKET_SCROLL = "itemsocketscroll.xml"; - public const string ITEM_EXCHANGE_SCROLL = "itemexchangescrolltable.xml"; - - // Item Options - public const string ITEM_OPTION_CONSTANT = "itemoptionconstant.xml"; - public const string ITEM_OPTION_RANDOM = "itemoptionrandom.xml"; - public const string ITEM_OPTION_STATIC = "itemoptionstatic.xml"; - public const string ITEM_OPTION_PICK = "itemoptionpick.xml"; - public const string ITEM_OPTION_VARIATION = "itemoptionvariation.xml"; - public const string ITEM_OPTION_VARIATION_ACC = "itemoptionvariation_acc.xml"; - public const string ITEM_OPTION_VARIATION_ARMOR = "itemoptionvariation_armor.xml"; - public const string ITEM_OPTION_VARIATION_PET = "itemoptionvariation_pet.xml"; - public const string ITEM_OPTION_VARIATION_WEAPON = "itemoptionvariation_weapon.xml"; - - // Set Items - public const string SET_ITEM = "setitem*.xml"; - - // Dungeon - public const string DUNGEON_ROOM = "dungeonroom.xml"; - public const string DUNGEON_RANK_REWARD = "dungeonrankreward.xml"; - public const string DUNGEON_CONFIG = "dungeonconfig.xml"; - public const string DUNGEON_MISSION = "dungeonmission.xml"; - - public static readonly Dictionary ItemOptionVariationTableNames = new Dictionary { - { "acc", ITEM_OPTION_VARIATION_ACC }, - { "armor", ITEM_OPTION_VARIATION_ARMOR }, - { "pet", ITEM_OPTION_VARIATION_PET }, - { "weapon", ITEM_OPTION_VARIATION_WEAPON }, - }; -} +namespace Maple2.Model.Common; + +public static class TableNames { + // Common table names + public const string CHAT_EMOTICON = "chatemoticon.xml"; + public const string DEFAULT_ITEMS = "defaultitems.xml"; + public const string ITEM_BREAK_INGREDIENT = "itembreakingredient.xml"; + public const string ITEM_GEMSTONE_UPGRADE = "itemgemstoneupgrade.xml"; + public const string ITEM_EXTRACTION = "itemextraction.xml"; + public const string JOB = "job.xml"; + public const string MAGIC_PATH = "magicpath.xml"; + public const string INSTRUMENT_CATEGORY_INFO = "instrumentcategoryinfo.xml"; + public const string INTERACT_OBJECT = "interactobject*.xml"; + public const string ITEM_LAPENSHARD_UPGRADE = "itemlapenshardupgrade.xml"; + public const string ITEM_SOCKET = "itemsocket.xml"; + public const string MASTERY_RECIPE = "masteryreceipe.xml"; + public const string MASTERY = "mastery.xml"; + public const string GUILD = "guild*.xml"; + public const string VIP = "vip*.xml"; + public const string INDIVIDUAL_ITEM_DROP = "individualitemdrop*.xml"; + public const string COLOR_PALETTE = "colorpalette.xml"; + public const string MERET_MARKET_CATEGORY = "meretmarketcategory.xml"; + public const string SHOP_BEAUTY_COUPON = "shop_beautycoupon.xml"; + public const string SHOP_FURNISHING = "na/shop_*.xml"; + public const string GACHA_INFO = "gacha_info.xml"; + public const string NAME_TAG_SYMBOL = "nametagsymbol.xml"; + public const string EXP = "exp*.xml"; + public const string COMMON_EXP = "commonexp.xml"; + public const string UGC_DESIGN = "ugcdesign.xml"; + public const string MASTERY_UGC_HOUSING = "masteryugchousing.xml"; + public const string UGC_HOUSING_POINT_REWARD = "ugchousingpointreward.xml"; + public const string LEARNING_QUEST = "learningquest.xml"; + public const string BLACK_MARKET_TABLE = "blackmarkettable.xml"; + public const string CHANGE_JOB = "changejob.xml"; + public const string CHAPTER_BOOK = "chapterbook.xml"; + public const string FIELD_MISSION = "fieldmission.xml"; + public const string WORLD_MAP = "newworldmap.xml"; + public const string SURVIVAL_SKIN_INFO = "maplesurvivalskininfo.xml"; + public const string BANNER = "banner.xml"; + public const string WEDDING = "wedding*.xml"; + public const string REWARD_CONTENT = "rewardcontent*.xml"; + public const string SEASON_DATA = "seasondata*.xml"; + public const string SMART_PUSH = "smartpush.xml"; + public const string AUTO_ACTION = "autoactionpricepackage.xml"; + + // Prestige / Adventure + public const string PRESTIGE_LEVEL_ABILITY = "adventurelevelability.xml"; + public const string PRESTIGE_LEVEL_REWARD = "adventurelevelreward.xml"; + public const string PRESTIGE_MISSION = "adventurelevelmission.xml"; + + // Fishing + public const string FISHING_ROD = "fishingrod.xml"; + + // Scrolls + public const string ENCHANT_SCROLL = "enchantscroll.xml"; + public const string ITEM_REMAKE_SCROLL = "itemremakescroll.xml"; + public const string ITEM_REPACKING_SCROLL = "itemrepackingscroll.xml"; + public const string ITEM_SOCKET_SCROLL = "itemsocketscroll.xml"; + public const string ITEM_EXCHANGE_SCROLL = "itemexchangescrolltable.xml"; + + // Item Options + public const string ITEM_OPTION_CONSTANT = "itemoptionconstant.xml"; + public const string ITEM_OPTION_RANDOM = "itemoptionrandom.xml"; + public const string ITEM_OPTION_STATIC = "itemoptionstatic.xml"; + public const string ITEM_OPTION_PICK = "itemoptionpick.xml"; + public const string ITEM_OPTION_VARIATION = "itemoptionvariation.xml"; + public const string ITEM_OPTION_VARIATION_ACC = "itemoptionvariation_acc.xml"; + public const string ITEM_OPTION_VARIATION_ARMOR = "itemoptionvariation_armor.xml"; + public const string ITEM_OPTION_VARIATION_PET = "itemoptionvariation_pet.xml"; + public const string ITEM_OPTION_VARIATION_WEAPON = "itemoptionvariation_weapon.xml"; + + // Set Items + public const string SET_ITEM = "setitem*.xml"; + + // Dungeon + public const string DUNGEON_ROOM = "dungeonroom.xml"; + public const string DUNGEON_RANK_REWARD = "dungeonrankreward.xml"; + public const string DUNGEON_CONFIG = "dungeonconfig.xml"; + public const string DUNGEON_MISSION = "dungeonmission.xml"; + + public static readonly Dictionary ItemOptionVariationTableNames = new Dictionary { + { "acc", ITEM_OPTION_VARIATION_ACC }, + { "armor", ITEM_OPTION_VARIATION_ARMOR }, + { "pet", ITEM_OPTION_VARIATION_PET }, + { "weapon", ITEM_OPTION_VARIATION_WEAPON }, + }; +} diff --git a/Maple2.Model/Common/Vector.cs b/Maple2.Model/Common/Vector.cs index bcdb76b83..198533640 100644 --- a/Maple2.Model/Common/Vector.cs +++ b/Maple2.Model/Common/Vector.cs @@ -1,87 +1,87 @@ -using System.Numerics; -using System.Runtime.InteropServices; - -namespace Maple2.Model.Common; - -[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 4)] -public readonly record struct Vector3B(sbyte X, sbyte Y, sbyte Z) { - private const float BLOCK_SIZE = 150f; - - public Vector3B(int X, int Y, int Z) : this((sbyte) X, (sbyte) Y, (sbyte) Z) { } - - public static Vector3B ConvertFromInt(int value) { - // Ensure the input is within the 24-bit range - if (value is < 0 or > 0xFFFFFF) - throw new ArgumentOutOfRangeException(nameof(value), "Value must be between 0 and 0xFFFFFF"); - - // Extract each byte and interpret as signed 8-bit values - sbyte z = unchecked((sbyte) ((value >> 16) & 0xFF)); - sbyte y = unchecked((sbyte) ((value >> 8) & 0xFF)); - sbyte x = unchecked((sbyte) (value & 0xFF)); - return new Vector3B(x, y, z); - } - - public static implicit operator Vector3B(Vector3 vector) { - return new Vector3B( - (sbyte) MathF.Round(vector.X / BLOCK_SIZE), - (sbyte) MathF.Round(vector.Y / BLOCK_SIZE), - (sbyte) MathF.Round(vector.Z / BLOCK_SIZE) - ); - } - - public static implicit operator Vector3(Vector3B vector) { - return new Vector3( - vector.X * BLOCK_SIZE, - vector.Y * BLOCK_SIZE, - vector.Z * BLOCK_SIZE - ); - } - - public static Vector3B operator +(in Vector3B a, in Vector3B b) => - new((sbyte) (a.X + b.X), (sbyte) (a.Y + b.Y), (sbyte) (a.Z + b.Z)); - - public int ConvertToInt() { - // Convert x, y, and z to a single 24-bit integer - return ((Z & 0xFF) << 16) | ((Y & 0xFF) << 8) | (X & 0xFF); - } - - // We override GetHashCode because only 3/4 bytes are relevant. - public override int GetHashCode() { - return X << 16 | Y << 8 | (byte) Z; - } -} - -[StructLayout(LayoutKind.Sequential, Pack = 2, Size = 6)] -public readonly record struct Vector3S(short X, short Y, short Z) { - // This offset is used to correct rounding errors due to floating point arithmetic. - private const float OFFSET = 0.001f; - - public Vector3 Vector3 => new Vector3(X, Y, Z); - - public static implicit operator Vector3S(Vector3 vector) { - return new Vector3S( - (short) MathF.Round(vector.X), - (short) MathF.Round(vector.Y), - (short) MathF.Round(vector.Z) - ); - } - - public static implicit operator Vector3(Vector3S vector) { - return new Vector3( - vector.X + (vector.X >= 0 ? OFFSET : -OFFSET), - vector.Y + (vector.Y >= 0 ? OFFSET : -OFFSET), - vector.Z + OFFSET - ); - } - - public static Vector3S operator +(in Vector3S a, in Vector3S b) => - new Vector3S((short) (a.X + b.X), (short) (a.Y + b.Y), (short) (a.Z + b.Z)); - public static Vector3S operator -(in Vector3S a, in Vector3S b) => - new Vector3S((short) (a.X - b.X), (short) (a.Y - b.Y), (short) (a.Z - b.Z)); - public static Vector3S operator *(in Vector3S a, in Vector3S b) => - new Vector3S((short) (a.X * b.X), (short) (a.Y * b.Y), (short) (a.Z * b.Z)); - public static Vector3S operator /(in Vector3S a, in Vector3S b) => - new Vector3S((short) (a.X / b.X), (short) (a.Y / b.Y), (short) (a.Z / b.Z)); - - public override string ToString() => $"<{X}, {Y}, {Z}>"; -} +using System.Numerics; +using System.Runtime.InteropServices; + +namespace Maple2.Model.Common; + +[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 4)] +public readonly record struct Vector3B(sbyte X, sbyte Y, sbyte Z) { + private const float BLOCK_SIZE = 150f; + + public Vector3B(int X, int Y, int Z) : this((sbyte) X, (sbyte) Y, (sbyte) Z) { } + + public static Vector3B ConvertFromInt(int value) { + // Ensure the input is within the 24-bit range + if (value is < 0 or > 0xFFFFFF) + throw new ArgumentOutOfRangeException(nameof(value), "Value must be between 0 and 0xFFFFFF"); + + // Extract each byte and interpret as signed 8-bit values + sbyte z = unchecked((sbyte) ((value >> 16) & 0xFF)); + sbyte y = unchecked((sbyte) ((value >> 8) & 0xFF)); + sbyte x = unchecked((sbyte) (value & 0xFF)); + return new Vector3B(x, y, z); + } + + public static implicit operator Vector3B(Vector3 vector) { + return new Vector3B( + (sbyte) MathF.Round(vector.X / BLOCK_SIZE), + (sbyte) MathF.Round(vector.Y / BLOCK_SIZE), + (sbyte) MathF.Round(vector.Z / BLOCK_SIZE) + ); + } + + public static implicit operator Vector3(Vector3B vector) { + return new Vector3( + vector.X * BLOCK_SIZE, + vector.Y * BLOCK_SIZE, + vector.Z * BLOCK_SIZE + ); + } + + public static Vector3B operator +(in Vector3B a, in Vector3B b) => + new((sbyte) (a.X + b.X), (sbyte) (a.Y + b.Y), (sbyte) (a.Z + b.Z)); + + public int ConvertToInt() { + // Convert x, y, and z to a single 24-bit integer + return ((Z & 0xFF) << 16) | ((Y & 0xFF) << 8) | (X & 0xFF); + } + + // We override GetHashCode because only 3/4 bytes are relevant. + public override int GetHashCode() { + return X << 16 | Y << 8 | (byte) Z; + } +} + +[StructLayout(LayoutKind.Sequential, Pack = 2, Size = 6)] +public readonly record struct Vector3S(short X, short Y, short Z) { + // This offset is used to correct rounding errors due to floating point arithmetic. + private const float OFFSET = 0.001f; + + public Vector3 Vector3 => new Vector3(X, Y, Z); + + public static implicit operator Vector3S(Vector3 vector) { + return new Vector3S( + (short) MathF.Round(vector.X), + (short) MathF.Round(vector.Y), + (short) MathF.Round(vector.Z) + ); + } + + public static implicit operator Vector3(Vector3S vector) { + return new Vector3( + vector.X + (vector.X >= 0 ? OFFSET : -OFFSET), + vector.Y + (vector.Y >= 0 ? OFFSET : -OFFSET), + vector.Z + OFFSET + ); + } + + public static Vector3S operator +(in Vector3S a, in Vector3S b) => + new Vector3S((short) (a.X + b.X), (short) (a.Y + b.Y), (short) (a.Z + b.Z)); + public static Vector3S operator -(in Vector3S a, in Vector3S b) => + new Vector3S((short) (a.X - b.X), (short) (a.Y - b.Y), (short) (a.Z - b.Z)); + public static Vector3S operator *(in Vector3S a, in Vector3S b) => + new Vector3S((short) (a.X * b.X), (short) (a.Y * b.Y), (short) (a.Z * b.Z)); + public static Vector3S operator /(in Vector3S a, in Vector3S b) => + new Vector3S((short) (a.X / b.X), (short) (a.Y / b.Y), (short) (a.Z / b.Z)); + + public override string ToString() => $"<{X}, {Y}, {Z}>"; +} diff --git a/Maple2.Model/Enum/Achievement.cs b/Maple2.Model/Enum/Achievement.cs index 85c5f9a5e..6f8c87c27 100644 --- a/Maple2.Model/Enum/Achievement.cs +++ b/Maple2.Model/Enum/Achievement.cs @@ -1,30 +1,30 @@ -namespace Maple2.Model.Enum; - -public enum AchievementStatus : byte { - InProgress = 2, - Completed = 3, -} - -public enum AchievementCategory { - None = 0, - Combat = 1, - Adventure = 2, - Life = 3, -} - -public enum AchievementRewardType { - none = 0, - item = 1, - title = 2, - statpoint = 3, - skillpoint = 4, - shop_weapon = 5, - shop_build = 6, - shop_ride = 7, - itemcoloring = 8, - beauty_makeup = 9, - beauty_skin = 10, - beauty_hair = 11, - dynamicaction = 12, - etc = 13, -} +namespace Maple2.Model.Enum; + +public enum AchievementStatus : byte { + InProgress = 2, + Completed = 3, +} + +public enum AchievementCategory { + None = 0, + Combat = 1, + Adventure = 2, + Life = 3, +} + +public enum AchievementRewardType { + none = 0, + item = 1, + title = 2, + statpoint = 3, + skillpoint = 4, + shop_weapon = 5, + shop_build = 6, + shop_ride = 7, + itemcoloring = 8, + beauty_makeup = 9, + beauty_skin = 10, + beauty_hair = 11, + dynamicaction = 12, + etc = 13, +} diff --git a/Maple2.Model/Enum/ActorState.cs b/Maple2.Model/Enum/ActorState.cs index efb2574c5..6dbfa31e3 100644 --- a/Maple2.Model/Enum/ActorState.cs +++ b/Maple2.Model/Enum/ActorState.cs @@ -1,473 +1,473 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Enum; - -public enum ActorState : byte { - [Description("gosNone")] - None = 0, - [Description("gosIdle")] - Idle = 1, - [Description("gosWalk")] - Walk = 2, - [Description("gosCrawl")] - Crawl = 3, - [Description("gosLand")] - Land = 4, - [Description("gosFall")] - Fall = 5, - [Description("gosJump")] - Jump = 6, - [Description("gosJumpTo")] - JumpTo = 7, - [Description("gosLadder")] - Ladder = 8, - [Description("gosRope")] - Rope = 9, - [Description("gosSit")] - Sit = 10, - [Description("gosEmotion")] - Emotion = 11, - [Description("gosDead")] - Dead = 12, - [Description("gosHit")] - Hit = 13, - [Description("gosWaittingSelect")] - WaitingSelect = 14, - [Description("gosEmotionIdle")] - EmotionIdle = 15, - [Description("gosPcSkill")] - PcSkill = 16, - [Description("gosSpawn")] - Spawn = 17, - [Description("gosStun")] - Stun = 18, - [Description("gosDash")] - Dash = 20, - [Description("gosPush")] - Push = 21, - [Description("gosTalk")] - Talk = 22, - [Description("gosRegen")] - Regen = 23, - [Description("gosRevival")] - Revival = 24, - [Description("gosInteraction")] - Interaction = 25, - [Description("gosInteractionNpc")] - InteractionNpc = 26, - [Description("gosSwim")] - Swim = 27, - [Description("gosSwimDash")] - SwimDash = 28, - [Description("gosClimb")] - Climb = 29, - [Description("gosGlide")] - Glide = 30, - [Description("gosFallDamage")] - FallDamage = 31, - [Description("gosHold")] - Hold = 32, - [Description("gosRide")] - Ride = 33, - [Description("gosTransform")] - Transform = 34, - [Description("gosPuppet")] - Puppet = 35, - [Description("gosFloat")] - Float = 36, - [Description("gosTaxiCall")] - TaxiCall = 37, - [Description("gosSummon")] - Summon = 38, - [Description("gosGotoHome")] - GotoHome = 39, - [Description("gosPvPWinLose")] - PvPWinLose = 40, - [Description("gosUseFurniture")] - UseFurniture = 41, - [Description("gosCashCall")] - CashCall = 42, - [Description("gosGrabTarget")] - GrabTarget = 43, - [Description("gosRecall")] - Recall = 44, - [Description("gosFishing")] - Fishing = 45, - [Description("gosFishingFloat")] - FishingFloat = 46, - [Description("gosPlayInstrument")] - PlayInstrument = 47, - [Description("gosSummonRevive")] - SummonRevive = 48, - [Description("gosSummonExpire")] - SummonExpire = 49, - [Description("gosSummonItemPick")] - SummonItemPick = 50, - [Description("gosWarp")] - Warp = 51, - [Description("gosTrace")] - Trace = 52, - [Description("gosTriggerEmotion")] - TriggerEmotion = 53, - [Description("gosHomeConvenient")] - HomeConvenient = 54, - [Description("gosHomemade")] - Homemade = 55, - [Description("gosSummonPortal")] - SummonPortal = 56, - [Description("gosAutoInteraction")] - AutoInteraction = 57, - [Description("gosCoupleEmotion")] - CoupleEmotion = 58, - [Description("gosBanner")] - Banner = 59, - [Description("gosMicroGameRps")] - MicroGameRps = 60, - [Description("gosReact")] - React = 61, - [Description("gosTreeWatering")] - TreeWatering = 62, - [Description("gosObserver")] - Observer = 63, - [Description("gosNurturing")] - Nurturing = 64, - [Description("gosSkillMagicControl")] - SkillMagicControl = 65, - [Description("gosGroggy")] - Groggy = 66, - [Description("gosRescuee")] - Rescuee = 67, - [Description("gosMicroGameCoupleDance")] - MicroGameCoupleDance = 69, - [Description("gosTriggerFollowNpc")] - TriggerFollowNpc = 70, - [Description("gosWeddingEmotion")] - WeddingEmotion = 71, - [Description("gosMAX")] - Max = 72, -} - -public enum ActorSubState : byte { - [Description("SubState_None")] - None = 0, - - [Description("StateIdle_Idle")] - Idle_Idle = 1, - [Description("StateIdle_Bore_A")] - Idle_Bore_A = 2, - [Description("StateIdle_Bore_B")] - Idle_Bore_B = 3, - [Description("StateIdle_Bore_C")] - Idle_Bore_C = 4, - [Description("StateIdle_Bore_D")] - Idle_Bore_D = 5, - [Description("StateIdle_Talk")] - Idle_Talk = 6, - [Description("StateIdle_Water")] - Idle_Water = 7, - [Description("StateIdle_QuestCraft")] - Idle_QuestCraft = 8, - [Description("StateIdle_UGCCraft")] - Idle_UGCCraft = 9, - [Description("StateIdle_Fitting")] - Idle_Fitting = 10, - [Description("StateIdle_Fitting_Basic")] - Idle_Fitting_Basic = 11, - [Description("StateIdle_Stunt")] - Idle_Stunt = 12, - [Description("StateIdle_CreateCharComplete")] - Idle_CreateCharComplete = 13, - [Description("StateIdle_CharacterList")] - Idle_CharacterList = 14, - [Description("StateIdle_Happy")] - Idle_Happy = 15, - [Description("StateIdle_UseSkill")] - Idle_UseSkill = 16, - [Description("StateIdle_AutoRevive")] - Idle_AutoRevive = 17, - [Description("StateIdle_NotUseSkill")] - Idle_NotUseSkill = 18, - [Description("StateIdle_Nutrient")] - Idle_Nutrient = 19, - [Description("StateIdle_CharacterSelect_Bore")] - Idle_CharacterSelect_Bore = 20, - [Description("StateIdle_CharacterSelect_Bore_Idle")] - Idle_CharacterSelect_Bore_Idle = 21, - [Description("StateIdle_ListenMusic")] - Idle_ListenMusic = 22, - - [Description("StateWalk_Stunt")] - Walk_Stunt = 23, - [Description("StateWalk_Running")] - Walk_Running = 24, - [Description("StateWalk_Walking")] - Walk_Walking = 25, - [Description("StateWalk_Booster")] - Walk_Booster = 26, - - [Description("StateCrawl_Idle")] - Crawl_Idle = 27, - [Description("StateCrawl_Crawling")] - Crawl_Crawling = 28, - - [Description("StateDash_ForwardDash")] - Dash_ForwardDash = 29, - [Description("StateDash_Falling")] - Dash_Falling = 30, - [Description("StateDash_Landing")] - Dash_Landing = 31, - [Description("StateDash_Hit")] - Dash_Hit = 32, - - [Description("StateJump_Jump")] - Jump_Jump = 33, - [Description("StateJump_JumpSpecial")] - Jump_JumpSpecial = 34, - [Description("StateJump_Jump2")] - Jump_Jump2 = 35, - [Description("StateJump_JumpSpecial2")] - Jump_JumpSpecial2 = 36, - [Description("StateJump_JumpRandom")] - Jump_JumpRandom = 37, - [Description("StateJump_Hit")] - Jump_Hit = 38, - [Description("StateJump_Stunt")] - Jump_Stunt = 39, - - [Description("StateJumpTo_Idle")] - JumpTo_Idle = 40, - [Description("StateJumpTo_Done")] - JumpTo_Done = 41, - - [Description("StateLadder_Idle")] - Ladder_Idle = 42, - [Description("StateLadder_Up")] - Ladder_Up = 43, - [Description("StateLadder_Down")] - Ladder_Down = 44, - [Description("StateLadder_UpLand")] - Ladder_UpLand = 45, - [Description("StateLadder_DownLand")] - Ladder_DownLand = 46, - [Description("StateLadder_UpTake")] - Ladder_UpTake = 47, - [Description("StateLadder_DownTake")] - Ladder_DownTake = 48, - [Description("StateLadder_MiddleTake")] - Ladder_MiddleTake = 49, - [Description("StateLadder_Fall")] - Ladder_Fall = 50, - - [Description("StateRope_Idle")] - Rope_Idle = 51, - [Description("StateRope_Up")] - Rope_Up = 52, - [Description("StateRope_Down")] - Rope_Down = 53, - [Description("StateRope_Middle")] - Rope_Middle = 54, - [Description("StateRope_Turn")] - Rope_Turn = 55, - [Description("StateRope_Take")] - Rope_Take = 56, - [Description("StateRope_Fall")] - Rope_Fall = 57, - - [Description("StateSkill_Default")] - Skill_Default = 58, - [Description("StateSkill_HoldAttack")] - Skill_HoldAttack = 59, - [Description("StateSkill_MagicControl")] - Skill_MagicControl = 60, - - [Description("StateStun_LieStart")] - Stun_LieStart = 61, - [Description("StateStun_LieKeep")] - Stun_LieKeep = 62, - [Description("StateStun_LieStop")] - Stun_LieStop = 63, - [Description("StateStun_Standing")] - Stun_Standing = 64, - [Description("StateStun_Freezing")] - Stun_Freezing = 65, - [Description("StateStun_Snare")] - Stun_Snare = 66, - [Description("StateStun_Vomit")] - Stun_Vomit = 67, - [Description("StateStun_Frozen")] - Stun_Frozen = 68, - [Description("StateStun_Stuck")] - Stun_Stuck = 69, - [Description("StateStun_Custom")] - Stun_Custom = 70, - - [Description("StateTalk_Start")] - Talk_Start = 71, - [Description("StateTalk_Loop")] - Talk_Loop = 72, - [Description("StateTalk_Idle")] - Talk_Idle = 73, - [Description("StateTalk_EnchantSuccess")] - Talk_EnchantSuccess = 74, - [Description("StateTalk_EnchantFail")] - Talk_EnchantFail = 75, - - [Description("StateWaitingSelect_CHANGE_CAP")] - WaitingSelect_ChangeCap = 76, - [Description("StateWaitingSelect_CHANGE_IDLE")] - WaitingSelect_ChangeIdle = 77, - [Description("StateWaitingSelect_CHANGE_BODY")] - WaitingSelect_ChangeBody = 78, - [Description("StateWaitingSelect_CHANGE_HEAD")] - WaitingSelect_ChangeHead = 79, - [Description("StateWaitingSelect_CHANGE_HAIR")] - WaitingSelect_ChangeHair = 80, - [Description("StateWaitingSelect_CHANGE_GLOVE")] - WaitingSelect_ChangeGlove = 81, - [Description("StateWaitingSelect_CHANGE_MANTLE")] - WaitingSelect_ChangeMantle = 82, - [Description("StateWaitingSelect_CHANGE_SHOES")] - WaitingSelect_ChangeShoes = 83, - [Description("StateWaitingSelect_CHANGE_WEAPON")] - WaitingSelect_ChangeWeapon = 84, - [Description("StateWaitingSelect_CHANGE_WEAPON_IDLE")] - WaitingSelect_ChangeWeaponIdle = 85, - - [Description("StateClimb_Idle")] - Climb_Idle = 86, - [Description("StateClimb_Up")] - Climb_Up = 87, - [Description("StateClimb_UpLeft")] - Climb_UpLeft = 88, - [Description("StateClimb_UpRight")] - Climb_UpRight = 89, - [Description("StateClimb_Down")] - Climb_Down = 90, - [Description("StateClimb_DownLeft")] - Climb_DownLeft = 91, - [Description("StateClimb_DownRight")] - Climb_DownRight = 92, - [Description("StateClimb_Left")] - Climb_Left = 93, - [Description("StateClimb_Right")] - Climb_Right = 94, - [Description("StateClimb_UpLand")] - Climb_UpLand = 95, - [Description("StateClimb_UpTake")] - Climb_UpTake = 96, - [Description("StateClimb_DownTake")] - Climb_DownTake = 97, - [Description("StateClimb_DownLand")] - Climb_DownLand = 98, - - [Description("StateGlide_Idle")] - Glide_Idle = 99, - [Description("StateGlide_Run")] - Glide_Run = 100, - - [Description("StateTaxiCall_Call")] - TaxiCall_Call = 101, - [Description("StateTaxiCall_Arrive")] - TaxiCall_Arrive = 102, - [Description("StateTaxiCall_Wait1")] - TaxiCall_Wait1 = 103, - [Description("StateTaxiCall_Wait2")] - TaxiCall_Wait2 = 104, - [Description("StateTaxiCall_Leave")] - TaxiCall_Leave = 105, - [Description("StateTaxiCall_End")] - TaxiCall_End = 106, - - [Description("StateCashCall_Call")] - CashCall_Call = 107, - [Description("StateCashCall_Arrive")] - CashCall_Arrive = 108, - [Description("StateCashCall_Leave")] - CashCall_Leave = 109, - [Description("StateCashCall_End")] - CashCall_End = 110, - - [Description("StateEmotionIdle_Idle")] - EmotionIdle_Idle = 111, - [Description("StateEmotionIdle_Bore_A")] - EmotionIdle_Bore_A = 112, - [Description("StateEmotionIdle_Bore_B")] - EmotionIdle_Bore_B = 113, - [Description("StateEmotionIdle_Bore_C")] - EmotionIdle_Bore_C = 114, - - [Description("StatePvPWinLose_Win")] - PvPWinLose_Win = 115, - [Description("StatePvPWinLose_Lose")] - PvPWinLose_Lose = 116, - - [Description("StateFishing_Start")] - Fishing_Start = 117, - [Description("StateFishing_Idle")] - Fishing_Idle = 118, - [Description("StateFishing_Bore")] - Fishing_Bore = 119, - [Description("StateFishing_FishFighting")] - Fishing_FishFighting = 120, - [Description("StateFishing_Catch")] - Fishing_Catch = 121, - - [Description("StateFishingFloat_Idle")] - FishingFloat_Idle = 122, - [Description("StateFishingFloat_Fishing")] - FishingFloat_Fishing = 123, - [Description("StateFishingFloat_Catch")] - FishingFloat_Catch = 124, - - [Description("StatePlayInstrument_Ready")] - PlayInstrument_Ready = 125, - [Description("StatePlayInstrument_Playing_Direct")] - PlayInstrument_Playing_Direct = 126, - [Description("StatePlayInstrument_Playing_Score_Solo")] - PlayInstrument_Playing_Score_Solo = 127, - [Description("StatePlayInstrument_Ready_Score_Ensemble")] - PlayInstrument_Ready_Score_Ensemble = 128, - [Description("StatePlayInstrument_Playing_Score_Ensemble")] - PlayInstrument_Playing_Score_Ensemble = 129, - - [Description("StateSummon_React")] - Summon_React = 130, - [Description("StateSummon_Pet")] - Summon_Pet = 131, - - [Description("StateSwim_Swim")] - Swim_Swim = 132, - [Description("StateSwim_Stunt")] - Swim_Stunt = 133, - - [Description("StateLand_Land")] - Land_Land = 134, - [Description("StateLand_Stunt")] - Land_Stunt = 135, - - [Description("StateHomeConvenient_Call")] - HomeConvenient_Call = 136, - [Description("StateHomeConvenient_End")] - HomeConvenient_End = 137, - - [Description("StateInteract_Intearct")] - Interact_Interact = 138, - [Description("StateInteract_Success")] - Interact_Success = 139, - [Description("StateInteract_Fail")] - Interact_Fail = 140, - - [Description("StateHomemade_Try")] - Homemade_Try = 142, - [Description("StateHomemade_Harvest")] - Homemade_Harvest = 143, - [Description("StateHomemade_Success")] - Homemade_Success = 144, - [Description("StateHomemade_Fail")] - Homemade_Fail = 145, - - [Description("SubState_Max")] - Max = 179, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Enum; + +public enum ActorState : byte { + [Description("gosNone")] + None = 0, + [Description("gosIdle")] + Idle = 1, + [Description("gosWalk")] + Walk = 2, + [Description("gosCrawl")] + Crawl = 3, + [Description("gosLand")] + Land = 4, + [Description("gosFall")] + Fall = 5, + [Description("gosJump")] + Jump = 6, + [Description("gosJumpTo")] + JumpTo = 7, + [Description("gosLadder")] + Ladder = 8, + [Description("gosRope")] + Rope = 9, + [Description("gosSit")] + Sit = 10, + [Description("gosEmotion")] + Emotion = 11, + [Description("gosDead")] + Dead = 12, + [Description("gosHit")] + Hit = 13, + [Description("gosWaittingSelect")] + WaitingSelect = 14, + [Description("gosEmotionIdle")] + EmotionIdle = 15, + [Description("gosPcSkill")] + PcSkill = 16, + [Description("gosSpawn")] + Spawn = 17, + [Description("gosStun")] + Stun = 18, + [Description("gosDash")] + Dash = 20, + [Description("gosPush")] + Push = 21, + [Description("gosTalk")] + Talk = 22, + [Description("gosRegen")] + Regen = 23, + [Description("gosRevival")] + Revival = 24, + [Description("gosInteraction")] + Interaction = 25, + [Description("gosInteractionNpc")] + InteractionNpc = 26, + [Description("gosSwim")] + Swim = 27, + [Description("gosSwimDash")] + SwimDash = 28, + [Description("gosClimb")] + Climb = 29, + [Description("gosGlide")] + Glide = 30, + [Description("gosFallDamage")] + FallDamage = 31, + [Description("gosHold")] + Hold = 32, + [Description("gosRide")] + Ride = 33, + [Description("gosTransform")] + Transform = 34, + [Description("gosPuppet")] + Puppet = 35, + [Description("gosFloat")] + Float = 36, + [Description("gosTaxiCall")] + TaxiCall = 37, + [Description("gosSummon")] + Summon = 38, + [Description("gosGotoHome")] + GotoHome = 39, + [Description("gosPvPWinLose")] + PvPWinLose = 40, + [Description("gosUseFurniture")] + UseFurniture = 41, + [Description("gosCashCall")] + CashCall = 42, + [Description("gosGrabTarget")] + GrabTarget = 43, + [Description("gosRecall")] + Recall = 44, + [Description("gosFishing")] + Fishing = 45, + [Description("gosFishingFloat")] + FishingFloat = 46, + [Description("gosPlayInstrument")] + PlayInstrument = 47, + [Description("gosSummonRevive")] + SummonRevive = 48, + [Description("gosSummonExpire")] + SummonExpire = 49, + [Description("gosSummonItemPick")] + SummonItemPick = 50, + [Description("gosWarp")] + Warp = 51, + [Description("gosTrace")] + Trace = 52, + [Description("gosTriggerEmotion")] + TriggerEmotion = 53, + [Description("gosHomeConvenient")] + HomeConvenient = 54, + [Description("gosHomemade")] + Homemade = 55, + [Description("gosSummonPortal")] + SummonPortal = 56, + [Description("gosAutoInteraction")] + AutoInteraction = 57, + [Description("gosCoupleEmotion")] + CoupleEmotion = 58, + [Description("gosBanner")] + Banner = 59, + [Description("gosMicroGameRps")] + MicroGameRps = 60, + [Description("gosReact")] + React = 61, + [Description("gosTreeWatering")] + TreeWatering = 62, + [Description("gosObserver")] + Observer = 63, + [Description("gosNurturing")] + Nurturing = 64, + [Description("gosSkillMagicControl")] + SkillMagicControl = 65, + [Description("gosGroggy")] + Groggy = 66, + [Description("gosRescuee")] + Rescuee = 67, + [Description("gosMicroGameCoupleDance")] + MicroGameCoupleDance = 69, + [Description("gosTriggerFollowNpc")] + TriggerFollowNpc = 70, + [Description("gosWeddingEmotion")] + WeddingEmotion = 71, + [Description("gosMAX")] + Max = 72, +} + +public enum ActorSubState : byte { + [Description("SubState_None")] + None = 0, + + [Description("StateIdle_Idle")] + Idle_Idle = 1, + [Description("StateIdle_Bore_A")] + Idle_Bore_A = 2, + [Description("StateIdle_Bore_B")] + Idle_Bore_B = 3, + [Description("StateIdle_Bore_C")] + Idle_Bore_C = 4, + [Description("StateIdle_Bore_D")] + Idle_Bore_D = 5, + [Description("StateIdle_Talk")] + Idle_Talk = 6, + [Description("StateIdle_Water")] + Idle_Water = 7, + [Description("StateIdle_QuestCraft")] + Idle_QuestCraft = 8, + [Description("StateIdle_UGCCraft")] + Idle_UGCCraft = 9, + [Description("StateIdle_Fitting")] + Idle_Fitting = 10, + [Description("StateIdle_Fitting_Basic")] + Idle_Fitting_Basic = 11, + [Description("StateIdle_Stunt")] + Idle_Stunt = 12, + [Description("StateIdle_CreateCharComplete")] + Idle_CreateCharComplete = 13, + [Description("StateIdle_CharacterList")] + Idle_CharacterList = 14, + [Description("StateIdle_Happy")] + Idle_Happy = 15, + [Description("StateIdle_UseSkill")] + Idle_UseSkill = 16, + [Description("StateIdle_AutoRevive")] + Idle_AutoRevive = 17, + [Description("StateIdle_NotUseSkill")] + Idle_NotUseSkill = 18, + [Description("StateIdle_Nutrient")] + Idle_Nutrient = 19, + [Description("StateIdle_CharacterSelect_Bore")] + Idle_CharacterSelect_Bore = 20, + [Description("StateIdle_CharacterSelect_Bore_Idle")] + Idle_CharacterSelect_Bore_Idle = 21, + [Description("StateIdle_ListenMusic")] + Idle_ListenMusic = 22, + + [Description("StateWalk_Stunt")] + Walk_Stunt = 23, + [Description("StateWalk_Running")] + Walk_Running = 24, + [Description("StateWalk_Walking")] + Walk_Walking = 25, + [Description("StateWalk_Booster")] + Walk_Booster = 26, + + [Description("StateCrawl_Idle")] + Crawl_Idle = 27, + [Description("StateCrawl_Crawling")] + Crawl_Crawling = 28, + + [Description("StateDash_ForwardDash")] + Dash_ForwardDash = 29, + [Description("StateDash_Falling")] + Dash_Falling = 30, + [Description("StateDash_Landing")] + Dash_Landing = 31, + [Description("StateDash_Hit")] + Dash_Hit = 32, + + [Description("StateJump_Jump")] + Jump_Jump = 33, + [Description("StateJump_JumpSpecial")] + Jump_JumpSpecial = 34, + [Description("StateJump_Jump2")] + Jump_Jump2 = 35, + [Description("StateJump_JumpSpecial2")] + Jump_JumpSpecial2 = 36, + [Description("StateJump_JumpRandom")] + Jump_JumpRandom = 37, + [Description("StateJump_Hit")] + Jump_Hit = 38, + [Description("StateJump_Stunt")] + Jump_Stunt = 39, + + [Description("StateJumpTo_Idle")] + JumpTo_Idle = 40, + [Description("StateJumpTo_Done")] + JumpTo_Done = 41, + + [Description("StateLadder_Idle")] + Ladder_Idle = 42, + [Description("StateLadder_Up")] + Ladder_Up = 43, + [Description("StateLadder_Down")] + Ladder_Down = 44, + [Description("StateLadder_UpLand")] + Ladder_UpLand = 45, + [Description("StateLadder_DownLand")] + Ladder_DownLand = 46, + [Description("StateLadder_UpTake")] + Ladder_UpTake = 47, + [Description("StateLadder_DownTake")] + Ladder_DownTake = 48, + [Description("StateLadder_MiddleTake")] + Ladder_MiddleTake = 49, + [Description("StateLadder_Fall")] + Ladder_Fall = 50, + + [Description("StateRope_Idle")] + Rope_Idle = 51, + [Description("StateRope_Up")] + Rope_Up = 52, + [Description("StateRope_Down")] + Rope_Down = 53, + [Description("StateRope_Middle")] + Rope_Middle = 54, + [Description("StateRope_Turn")] + Rope_Turn = 55, + [Description("StateRope_Take")] + Rope_Take = 56, + [Description("StateRope_Fall")] + Rope_Fall = 57, + + [Description("StateSkill_Default")] + Skill_Default = 58, + [Description("StateSkill_HoldAttack")] + Skill_HoldAttack = 59, + [Description("StateSkill_MagicControl")] + Skill_MagicControl = 60, + + [Description("StateStun_LieStart")] + Stun_LieStart = 61, + [Description("StateStun_LieKeep")] + Stun_LieKeep = 62, + [Description("StateStun_LieStop")] + Stun_LieStop = 63, + [Description("StateStun_Standing")] + Stun_Standing = 64, + [Description("StateStun_Freezing")] + Stun_Freezing = 65, + [Description("StateStun_Snare")] + Stun_Snare = 66, + [Description("StateStun_Vomit")] + Stun_Vomit = 67, + [Description("StateStun_Frozen")] + Stun_Frozen = 68, + [Description("StateStun_Stuck")] + Stun_Stuck = 69, + [Description("StateStun_Custom")] + Stun_Custom = 70, + + [Description("StateTalk_Start")] + Talk_Start = 71, + [Description("StateTalk_Loop")] + Talk_Loop = 72, + [Description("StateTalk_Idle")] + Talk_Idle = 73, + [Description("StateTalk_EnchantSuccess")] + Talk_EnchantSuccess = 74, + [Description("StateTalk_EnchantFail")] + Talk_EnchantFail = 75, + + [Description("StateWaitingSelect_CHANGE_CAP")] + WaitingSelect_ChangeCap = 76, + [Description("StateWaitingSelect_CHANGE_IDLE")] + WaitingSelect_ChangeIdle = 77, + [Description("StateWaitingSelect_CHANGE_BODY")] + WaitingSelect_ChangeBody = 78, + [Description("StateWaitingSelect_CHANGE_HEAD")] + WaitingSelect_ChangeHead = 79, + [Description("StateWaitingSelect_CHANGE_HAIR")] + WaitingSelect_ChangeHair = 80, + [Description("StateWaitingSelect_CHANGE_GLOVE")] + WaitingSelect_ChangeGlove = 81, + [Description("StateWaitingSelect_CHANGE_MANTLE")] + WaitingSelect_ChangeMantle = 82, + [Description("StateWaitingSelect_CHANGE_SHOES")] + WaitingSelect_ChangeShoes = 83, + [Description("StateWaitingSelect_CHANGE_WEAPON")] + WaitingSelect_ChangeWeapon = 84, + [Description("StateWaitingSelect_CHANGE_WEAPON_IDLE")] + WaitingSelect_ChangeWeaponIdle = 85, + + [Description("StateClimb_Idle")] + Climb_Idle = 86, + [Description("StateClimb_Up")] + Climb_Up = 87, + [Description("StateClimb_UpLeft")] + Climb_UpLeft = 88, + [Description("StateClimb_UpRight")] + Climb_UpRight = 89, + [Description("StateClimb_Down")] + Climb_Down = 90, + [Description("StateClimb_DownLeft")] + Climb_DownLeft = 91, + [Description("StateClimb_DownRight")] + Climb_DownRight = 92, + [Description("StateClimb_Left")] + Climb_Left = 93, + [Description("StateClimb_Right")] + Climb_Right = 94, + [Description("StateClimb_UpLand")] + Climb_UpLand = 95, + [Description("StateClimb_UpTake")] + Climb_UpTake = 96, + [Description("StateClimb_DownTake")] + Climb_DownTake = 97, + [Description("StateClimb_DownLand")] + Climb_DownLand = 98, + + [Description("StateGlide_Idle")] + Glide_Idle = 99, + [Description("StateGlide_Run")] + Glide_Run = 100, + + [Description("StateTaxiCall_Call")] + TaxiCall_Call = 101, + [Description("StateTaxiCall_Arrive")] + TaxiCall_Arrive = 102, + [Description("StateTaxiCall_Wait1")] + TaxiCall_Wait1 = 103, + [Description("StateTaxiCall_Wait2")] + TaxiCall_Wait2 = 104, + [Description("StateTaxiCall_Leave")] + TaxiCall_Leave = 105, + [Description("StateTaxiCall_End")] + TaxiCall_End = 106, + + [Description("StateCashCall_Call")] + CashCall_Call = 107, + [Description("StateCashCall_Arrive")] + CashCall_Arrive = 108, + [Description("StateCashCall_Leave")] + CashCall_Leave = 109, + [Description("StateCashCall_End")] + CashCall_End = 110, + + [Description("StateEmotionIdle_Idle")] + EmotionIdle_Idle = 111, + [Description("StateEmotionIdle_Bore_A")] + EmotionIdle_Bore_A = 112, + [Description("StateEmotionIdle_Bore_B")] + EmotionIdle_Bore_B = 113, + [Description("StateEmotionIdle_Bore_C")] + EmotionIdle_Bore_C = 114, + + [Description("StatePvPWinLose_Win")] + PvPWinLose_Win = 115, + [Description("StatePvPWinLose_Lose")] + PvPWinLose_Lose = 116, + + [Description("StateFishing_Start")] + Fishing_Start = 117, + [Description("StateFishing_Idle")] + Fishing_Idle = 118, + [Description("StateFishing_Bore")] + Fishing_Bore = 119, + [Description("StateFishing_FishFighting")] + Fishing_FishFighting = 120, + [Description("StateFishing_Catch")] + Fishing_Catch = 121, + + [Description("StateFishingFloat_Idle")] + FishingFloat_Idle = 122, + [Description("StateFishingFloat_Fishing")] + FishingFloat_Fishing = 123, + [Description("StateFishingFloat_Catch")] + FishingFloat_Catch = 124, + + [Description("StatePlayInstrument_Ready")] + PlayInstrument_Ready = 125, + [Description("StatePlayInstrument_Playing_Direct")] + PlayInstrument_Playing_Direct = 126, + [Description("StatePlayInstrument_Playing_Score_Solo")] + PlayInstrument_Playing_Score_Solo = 127, + [Description("StatePlayInstrument_Ready_Score_Ensemble")] + PlayInstrument_Ready_Score_Ensemble = 128, + [Description("StatePlayInstrument_Playing_Score_Ensemble")] + PlayInstrument_Playing_Score_Ensemble = 129, + + [Description("StateSummon_React")] + Summon_React = 130, + [Description("StateSummon_Pet")] + Summon_Pet = 131, + + [Description("StateSwim_Swim")] + Swim_Swim = 132, + [Description("StateSwim_Stunt")] + Swim_Stunt = 133, + + [Description("StateLand_Land")] + Land_Land = 134, + [Description("StateLand_Stunt")] + Land_Stunt = 135, + + [Description("StateHomeConvenient_Call")] + HomeConvenient_Call = 136, + [Description("StateHomeConvenient_End")] + HomeConvenient_End = 137, + + [Description("StateInteract_Intearct")] + Interact_Interact = 138, + [Description("StateInteract_Success")] + Interact_Success = 139, + [Description("StateInteract_Fail")] + Interact_Fail = 140, + + [Description("StateHomemade_Try")] + Homemade_Try = 142, + [Description("StateHomemade_Harvest")] + Homemade_Harvest = 143, + [Description("StateHomemade_Success")] + Homemade_Success = 144, + [Description("StateHomemade_Fail")] + Homemade_Fail = 145, + + [Description("SubState_Max")] + Max = 179, +} diff --git a/Maple2.Model/Enum/Admin.cs b/Maple2.Model/Enum/Admin.cs index 9f4c46111..f6235d526 100644 --- a/Maple2.Model/Enum/Admin.cs +++ b/Maple2.Model/Enum/Admin.cs @@ -1,83 +1,83 @@ -namespace Maple2.Model.Enum; - -public enum ReportCategory : byte { - Player = 0, - Chat = 1, - Poster = 2, - ItemDesign = 3, - Home = 4, - Pet = 7, -} - -[Flags] -public enum PlayerReportFlag { - None = 0, - CharacterPortrait = 1, - Clothes = 2, - Hacking = 4, - Behavior = 8, - Misc = 128, -} - -[Flags] -public enum HomeReportFlag { - None = 0, - ItemDesign = 1, - ItemPlacement = 2, - HomeName = 4, - Copyright = 8, -} - -[Flags] -public enum PosterReportFlag { - None = 0, - Copyright = 1, - Swearing = 2, - Derogatory = 4, - CommercialAdvertisement = 8, -} - -[Flags] -public enum DesignItemReportFlag { - None = 0, - Design = 1, - Name = 2, - Description = 4, - Copyright = 8, - Copying = 16, -} - -[Flags] -public enum ChatReportFlag { - None = 0, - Swearing = 1, - Derogatory = 2, - RMT = 4, - Spam = 8, -} - -[Flags] -public enum PetReportFlag { - None = 0, - Name = 1, -} - - -[Flags] -public enum AdminPermissions { - None = 0, - Alert = 1, - StringBoard = 2, - EventManagement = 4, - Ban = 8, - SpawnItem = 16, - SpawnNpc = 32, - Debug = 64, - Quest = 128, - Warp = 256, - Find = 512, - PlayerCommands = 1024, - - GameMaster = Warp | Ban | Alert | StringBoard | EventManagement | PlayerCommands, - Admin = int.MaxValue, -} +namespace Maple2.Model.Enum; + +public enum ReportCategory : byte { + Player = 0, + Chat = 1, + Poster = 2, + ItemDesign = 3, + Home = 4, + Pet = 7, +} + +[Flags] +public enum PlayerReportFlag { + None = 0, + CharacterPortrait = 1, + Clothes = 2, + Hacking = 4, + Behavior = 8, + Misc = 128, +} + +[Flags] +public enum HomeReportFlag { + None = 0, + ItemDesign = 1, + ItemPlacement = 2, + HomeName = 4, + Copyright = 8, +} + +[Flags] +public enum PosterReportFlag { + None = 0, + Copyright = 1, + Swearing = 2, + Derogatory = 4, + CommercialAdvertisement = 8, +} + +[Flags] +public enum DesignItemReportFlag { + None = 0, + Design = 1, + Name = 2, + Description = 4, + Copyright = 8, + Copying = 16, +} + +[Flags] +public enum ChatReportFlag { + None = 0, + Swearing = 1, + Derogatory = 2, + RMT = 4, + Spam = 8, +} + +[Flags] +public enum PetReportFlag { + None = 0, + Name = 1, +} + + +[Flags] +public enum AdminPermissions { + None = 0, + Alert = 1, + StringBoard = 2, + EventManagement = 4, + Ban = 8, + SpawnItem = 16, + SpawnNpc = 32, + Debug = 64, + Quest = 128, + Warp = 256, + Find = 512, + PlayerCommands = 1024, + + GameMaster = Warp | Ban | Alert | StringBoard | EventManagement | PlayerCommands, + Admin = int.MaxValue, +} diff --git a/Maple2.Model/Enum/AllianceType.cs b/Maple2.Model/Enum/AllianceType.cs index e47db56e4..32f9057e7 100644 --- a/Maple2.Model/Enum/AllianceType.cs +++ b/Maple2.Model/Enum/AllianceType.cs @@ -1,26 +1,26 @@ -using System.ComponentModel; - -namespace Maple2.Model.Enum; - -public enum AllianceType : short { - [Description("none")] - AnyFaction = 0, - [Description("darkwind")] - DarkWind = 1, - [Description("mapleunion")] - MapleAlliance = 2, - [Description("lumiknight")] - Lumiknights = 3, - [Description("triaroyalguard")] - RoyalGuard = 4, - [Description("greenhood")] - GreenHoods = 5, - [Description("mapleunion_kritiasexped")] - KritiasMapleAlliance = 6, - [Description("lumiknight_kritiasexped")] - KritiasLumiknights = 7, - [Description("greenhood_kritiasexped")] - KritiasGreenHoods = 8, - // [Description("georg")] - // georg = 9, -} +using System.ComponentModel; + +namespace Maple2.Model.Enum; + +public enum AllianceType : short { + [Description("none")] + AnyFaction = 0, + [Description("darkwind")] + DarkWind = 1, + [Description("mapleunion")] + MapleAlliance = 2, + [Description("lumiknight")] + Lumiknights = 3, + [Description("triaroyalguard")] + RoyalGuard = 4, + [Description("greenhood")] + GreenHoods = 5, + [Description("mapleunion_kritiasexped")] + KritiasMapleAlliance = 6, + [Description("lumiknight_kritiasexped")] + KritiasLumiknights = 7, + [Description("greenhood_kritiasexped")] + KritiasGreenHoods = 8, + // [Description("georg")] + // georg = 9, +} diff --git a/Maple2.Model/Enum/AttendGift.cs b/Maple2.Model/Enum/AttendGift.cs index 93a456e0a..284a0bcee 100644 --- a/Maple2.Model/Enum/AttendGift.cs +++ b/Maple2.Model/Enum/AttendGift.cs @@ -1,14 +1,14 @@ -namespace Maple2.Model.Enum; - -public enum AttendGiftCurrencyType : byte { - None = 0, - Meso = 1, - Meret = 2, -} - -public enum AttendGiftRequirement { - None, - NotUserValue, - UserValue, - ItemId, -} +namespace Maple2.Model.Enum; + +public enum AttendGiftCurrencyType : byte { + None = 0, + Meso = 1, + Meret = 2, +} + +public enum AttendGiftRequirement { + None, + NotUserValue, + UserValue, + ItemId, +} diff --git a/Maple2.Model/Enum/AttributePointSource.cs b/Maple2.Model/Enum/AttributePointSource.cs index 3caf44d46..27df84a92 100644 --- a/Maple2.Model/Enum/AttributePointSource.cs +++ b/Maple2.Model/Enum/AttributePointSource.cs @@ -1,8 +1,8 @@ -namespace Maple2.Model.Enum; - -public enum AttributePointSource { - Trophy = 1, - Quest = 2, - Exploration = 3, - Prestige = 4, -} +namespace Maple2.Model.Enum; + +public enum AttributePointSource { + Trophy = 1, + Quest = 2, + Exploration = 3, + Prestige = 4, +} diff --git a/Maple2.Model/Enum/BadgeType.cs b/Maple2.Model/Enum/BadgeType.cs index 12c5c8599..ebfec1c48 100644 --- a/Maple2.Model/Enum/BadgeType.cs +++ b/Maple2.Model/Enum/BadgeType.cs @@ -1,16 +1,16 @@ -namespace Maple2.Model.Enum; - -public enum BadgeType : byte { - None = 0, - Transparency = 1, - Damage = 2, - ChatBubble = 3, - NameTag = 4, - Tombstone = 5, - SwimTube = 6, - Buddy = 7, - Fishing = 8, - AutoGather = 9, - Effect = 10, - PetSkin = 11, -} +namespace Maple2.Model.Enum; + +public enum BadgeType : byte { + None = 0, + Transparency = 1, + Damage = 2, + ChatBubble = 3, + NameTag = 4, + Tombstone = 5, + SwimTube = 6, + Buddy = 7, + Fishing = 8, + AutoGather = 9, + Effect = 10, + PetSkin = 11, +} diff --git a/Maple2.Model/Enum/BasicAttribute.cs b/Maple2.Model/Enum/BasicAttribute.cs index 5436a46e5..e49a93977 100644 --- a/Maple2.Model/Enum/BasicAttribute.cs +++ b/Maple2.Model/Enum/BasicAttribute.cs @@ -1,39 +1,39 @@ -namespace Maple2.Model.Enum; - -public enum BasicAttribute : byte { - Strength = 0, - Dexterity = 1, - Intelligence = 2, - Luck = 3, - Health = 4, - HpRegen = 5, - HpRegenInterval = 6, - Spirit = 7, - SpRegen = 8, - SpRegenInterval = 9, - Stamina = 10, - StaminaRegen = 11, - StaminaRegenInterval = 12, - AttackSpeed = 13, - MovementSpeed = 14, - Accuracy = 15, - Evasion = 16, - CriticalRate = 17, - CriticalDamage = 18, - CriticalEvasion = 19, - Defense = 20, - PerfectGuard = 21, - JumpHeight = 22, - PhysicalAtk = 23, - MagicalAtk = 24, - PhysicalRes = 25, - MagicalRes = 26, - MinWeaponAtk = 27, - MaxWeaponAtk = 28, - Damage = 29, - Unknown = 30, // "Damage" - Piercing = 31, - MountSpeed = 32, - BonusAtk = 33, - PetBonusAtk = 34, -} +namespace Maple2.Model.Enum; + +public enum BasicAttribute : byte { + Strength = 0, + Dexterity = 1, + Intelligence = 2, + Luck = 3, + Health = 4, + HpRegen = 5, + HpRegenInterval = 6, + Spirit = 7, + SpRegen = 8, + SpRegenInterval = 9, + Stamina = 10, + StaminaRegen = 11, + StaminaRegenInterval = 12, + AttackSpeed = 13, + MovementSpeed = 14, + Accuracy = 15, + Evasion = 16, + CriticalRate = 17, + CriticalDamage = 18, + CriticalEvasion = 19, + Defense = 20, + PerfectGuard = 21, + JumpHeight = 22, + PhysicalAtk = 23, + MagicalAtk = 24, + PhysicalRes = 25, + MagicalRes = 26, + MinWeaponAtk = 27, + MaxWeaponAtk = 28, + Damage = 29, + Unknown = 30, // "Damage" + Piercing = 31, + MountSpeed = 32, + BonusAtk = 33, + PetBonusAtk = 34, +} diff --git a/Maple2.Model/Enum/BeautyShop.cs b/Maple2.Model/Enum/BeautyShop.cs index a3b50e1e9..2544f6474 100644 --- a/Maple2.Model/Enum/BeautyShop.cs +++ b/Maple2.Model/Enum/BeautyShop.cs @@ -1,17 +1,17 @@ -namespace Maple2.Model.Enum; - -public enum BeautyShopType : byte { - Default = 1, - Random = 2, - Modify = 3, // Dye, Skin, Mirror - Save = 4, -} - -public enum BeautyShopCategory { - Hair = 1, - Makeup = 2, - Face = 3, - Skin = 4, - Dye = 5, - Mirror = 6, -} +namespace Maple2.Model.Enum; + +public enum BeautyShopType : byte { + Default = 1, + Random = 2, + Modify = 3, // Dye, Skin, Mirror + Save = 4, +} + +public enum BeautyShopCategory { + Hair = 1, + Makeup = 2, + Face = 3, + Skin = 4, + Dye = 5, + Mirror = 6, +} diff --git a/Maple2.Model/Enum/BlackMarketSort.cs b/Maple2.Model/Enum/BlackMarketSort.cs index c4adcd376..1e0426e63 100644 --- a/Maple2.Model/Enum/BlackMarketSort.cs +++ b/Maple2.Model/Enum/BlackMarketSort.cs @@ -1,8 +1,8 @@ -namespace Maple2.Model.Enum; - -public enum BlackMarketSort : byte { - LevelAscending = 11, - LevelDescending = 12, - PriceAscending = 21, - PriceDescending = 22, -} +namespace Maple2.Model.Enum; + +public enum BlackMarketSort : byte { + LevelAscending = 11, + LevelDescending = 12, + PriceAscending = 21, + PriceDescending = 22, +} diff --git a/Maple2.Model/Enum/BlueMarbleSlotType.cs b/Maple2.Model/Enum/BlueMarbleSlotType.cs index 9232a5809..78f054c95 100644 --- a/Maple2.Model/Enum/BlueMarbleSlotType.cs +++ b/Maple2.Model/Enum/BlueMarbleSlotType.cs @@ -1,14 +1,14 @@ -namespace Maple2.Model.Enum; - -public enum BlueMarbleSlotType : short { - Item = 0, - Lose = 1, - Backward = 2, - Forward = 3, - GoToStarting = 4, - Roll = 5, - Start = 6, - Trap = 7, - WorldTour = 8, - Paradise = 9, -} +namespace Maple2.Model.Enum; + +public enum BlueMarbleSlotType : short { + Item = 0, + Lose = 1, + Backward = 2, + Forward = 3, + GoToStarting = 4, + Roll = 5, + Start = 6, + Trap = 7, + WorldTour = 8, + Paradise = 9, +} diff --git a/Maple2.Model/Enum/BlueprintType.cs b/Maple2.Model/Enum/BlueprintType.cs index 164fd4bad..47b579373 100644 --- a/Maple2.Model/Enum/BlueprintType.cs +++ b/Maple2.Model/Enum/BlueprintType.cs @@ -1,6 +1,6 @@ -namespace Maple2.Model.Enum; - -public enum BlueprintType { - Copy = 0, - Original = 1, -} +namespace Maple2.Model.Enum; + +public enum BlueprintType { + Copy = 0, + Original = 1, +} diff --git a/Maple2.Model/Enum/BreakableState.cs b/Maple2.Model/Enum/BreakableState.cs index d4e238342..772608591 100644 --- a/Maple2.Model/Enum/BreakableState.cs +++ b/Maple2.Model/Enum/BreakableState.cs @@ -1,9 +1,9 @@ -namespace Maple2.Model.Enum; - -public enum BreakableState : byte { - Show = 2, - Break = 3, - Hide = 4, - Unknown5 = 5, - Unknown6 = 6, -} +namespace Maple2.Model.Enum; + +public enum BreakableState : byte { + Show = 2, + Break = 3, + Hide = 4, + Unknown5 = 5, + Unknown6 = 6, +} diff --git a/Maple2.Model/Enum/BuddyType.cs b/Maple2.Model/Enum/BuddyType.cs index 11c92c041..216889cb5 100644 --- a/Maple2.Model/Enum/BuddyType.cs +++ b/Maple2.Model/Enum/BuddyType.cs @@ -1,9 +1,9 @@ -namespace Maple2.Model.Enum; - -[Flags] -public enum BuddyType : byte { - Default = 0, - InRequest = 1, - OutRequest = 2, - Blocked = 4, -} +namespace Maple2.Model.Enum; + +[Flags] +public enum BuddyType : byte { + Default = 0, + InRequest = 1, + OutRequest = 2, + Blocked = 4, +} diff --git a/Maple2.Model/Enum/Buff.cs b/Maple2.Model/Enum/Buff.cs index 541d559a3..9d367f97c 100644 --- a/Maple2.Model/Enum/Buff.cs +++ b/Maple2.Model/Enum/Buff.cs @@ -1,98 +1,98 @@ -namespace Maple2.Model.Enum; - -public enum BuffType { - None = 0, - Buff = 1, - Debuff = 2, - Debuff2 = 3, // Also a debuff? -} - -[Flags] -public enum BuffSubType { - None = 0, - Buff = 1, // Used for lots of random things - Status = 2, - Damage = 4, - Motion = 8, - Recovery = 16, - Consumable = 32, // Healing, Souvenir, ??? - PcBang = 64, - Fishing = 128, - Guild = 256, - Lapenta = 512, - Prestige = 1024, -} - -public enum BuffCategory { - None = 0, - Unknown1 = 1, - Unknown2 = 2, - Unknown4 = 4, - EnemyDot = 6, - Stunned = 7, // ? - Slow = 8, - BossResistance = 9, - Unknown99 = 99, - MonsterStunned = 1007, // ? - Unknown2001 = 2001, -} - -public enum BuffEventType { - None = 0, - AutoFish = 1, - SafeRiding = 2, - AmphibiousRide = 3, - AutoPerform = 4, -} - -public enum BuffKeepCondition { - TimerDuration = 0, // ? - SkillDuration = 1, // ? - TimerDurationTrackCooldown = 5, // ? - UnlimitedDuration = 99, -} - -public enum BuffResetCondition { - ResetEndTick = 0, - PersistEndTick = 1, // end tick does not reset - Reset2 = 2, // behaves the same as Reset ?? - Replace = 3, // Removes old buff and adds a new -} - -public enum BuffDotCondition { - Default = 0, // Maybe activate on enable? - OnInterval = 1, - Stack = 2, -} - -[Flags] -public enum BuffFlag { - None = 0, - UpdateBuff = 1, - UpdateShield = 2, -} - -public enum InvokeEffectType : byte { - ReduceCooldown = 1, - IncreaseSkillDamage = 2, - IncreaseEffectDuration = 3, - IncreaseDotDamage = 5, - // 20 (90050324) - // 21 (90050324) // Triggered from 10200081 (Adrenaline Rush) - IncreaseEvasionDebuff = 23, - IncreaseCritEvasionDebuff = 26, - // 34 (90050853) // Adds 9% weapon attack to the triggered effect of Explosive Panic or Explosive Fervor cannons. - // 35 (90050854) // Triggered from 90050852 - Increases weapon attack by 4.5% when still for at least 1.5 sec." - // 38 (90050806) // Adds 2% physical damage to the triggered effect of the Barbaric Extreme Longsword. - // 40 (90050827) // Adds 5.2% magic damage to the triggered effect of the Holy Extreme Scepter. - ReduceSpiritCost = 56, - // 57 (90050351) // Triggered from 10500061 (Sharp Eyes) - - IncreaseHealing = 58, -} - -public enum BuffCompulsionEventType : byte { - None = 0, - CritChanceOverride = 1, - EvasionChanceOverride = 2, - BlockChance = 3, -} +namespace Maple2.Model.Enum; + +public enum BuffType { + None = 0, + Buff = 1, + Debuff = 2, + Debuff2 = 3, // Also a debuff? +} + +[Flags] +public enum BuffSubType { + None = 0, + Buff = 1, // Used for lots of random things + Status = 2, + Damage = 4, + Motion = 8, + Recovery = 16, + Consumable = 32, // Healing, Souvenir, ??? + PcBang = 64, + Fishing = 128, + Guild = 256, + Lapenta = 512, + Prestige = 1024, +} + +public enum BuffCategory { + None = 0, + Unknown1 = 1, + Unknown2 = 2, + Unknown4 = 4, + EnemyDot = 6, + Stunned = 7, // ? + Slow = 8, + BossResistance = 9, + Unknown99 = 99, + MonsterStunned = 1007, // ? + Unknown2001 = 2001, +} + +public enum BuffEventType { + None = 0, + AutoFish = 1, + SafeRiding = 2, + AmphibiousRide = 3, + AutoPerform = 4, +} + +public enum BuffKeepCondition { + TimerDuration = 0, // ? + SkillDuration = 1, // ? + TimerDurationTrackCooldown = 5, // ? + UnlimitedDuration = 99, +} + +public enum BuffResetCondition { + ResetEndTick = 0, + PersistEndTick = 1, // end tick does not reset + Reset2 = 2, // behaves the same as Reset ?? + Replace = 3, // Removes old buff and adds a new +} + +public enum BuffDotCondition { + Default = 0, // Maybe activate on enable? + OnInterval = 1, + Stack = 2, +} + +[Flags] +public enum BuffFlag { + None = 0, + UpdateBuff = 1, + UpdateShield = 2, +} + +public enum InvokeEffectType : byte { + ReduceCooldown = 1, + IncreaseSkillDamage = 2, + IncreaseEffectDuration = 3, + IncreaseDotDamage = 5, + // 20 (90050324) + // 21 (90050324) // Triggered from 10200081 (Adrenaline Rush) + IncreaseEvasionDebuff = 23, + IncreaseCritEvasionDebuff = 26, + // 34 (90050853) // Adds 9% weapon attack to the triggered effect of Explosive Panic or Explosive Fervor cannons. + // 35 (90050854) // Triggered from 90050852 - Increases weapon attack by 4.5% when still for at least 1.5 sec." + // 38 (90050806) // Adds 2% physical damage to the triggered effect of the Barbaric Extreme Longsword. + // 40 (90050827) // Adds 5.2% magic damage to the triggered effect of the Holy Extreme Scepter. + ReduceSpiritCost = 56, + // 57 (90050351) // Triggered from 10500061 (Sharp Eyes) - + IncreaseHealing = 58, +} + +public enum BuffCompulsionEventType : byte { + None = 0, + CritChanceOverride = 1, + EvasionChanceOverride = 2, + BlockChance = 3, +} diff --git a/Maple2.Model/Enum/CaughtFishType.cs b/Maple2.Model/Enum/CaughtFishType.cs index 0020bffdb..bd4dcd5d2 100644 --- a/Maple2.Model/Enum/CaughtFishType.cs +++ b/Maple2.Model/Enum/CaughtFishType.cs @@ -1,12 +1,12 @@ -using System.ComponentModel; - -namespace Maple2.Model.Enum; - -public enum CaughtFishType : short { - [Description("Fishing mastery increased by {0}.")] - Default = 1, - [Description("You caught your first {0}, increasing your fishing mastery by {1}.")] - FirstKind = 2, - [Description("You caught a prize {0}, increasing your fishing mastery by {1}.")] - Prize = 3, -} +using System.ComponentModel; + +namespace Maple2.Model.Enum; + +public enum CaughtFishType : short { + [Description("Fishing mastery increased by {0}.")] + Default = 1, + [Description("You caught your first {0}, increasing your fishing mastery by {1}.")] + FirstKind = 2, + [Description("You caught a prize {0}, increasing your fishing mastery by {1}.")] + Prize = 3, +} diff --git a/Maple2.Model/Enum/ChatType.cs b/Maple2.Model/Enum/ChatType.cs index 129b88abd..6e1f7667a 100644 --- a/Maple2.Model/Enum/ChatType.cs +++ b/Maple2.Model/Enum/ChatType.cs @@ -1,59 +1,59 @@ -using System.ComponentModel; - -namespace Maple2.Model.Enum; - -public enum ChatType { - [Description("s_html_chat_normal")] - Normal = 0, - // 1: s_html_chat_channel - // 2: s_html_chat_channel - [Description("s_html_chat_whisper_from")] - WhisperFrom = 3, - [Description("s_html_chat_whisper_to")] - WhisperTo = 4, - [Description("s_html_chat_notice: Unable to send whisper.")] - WhisperFail = 5, - [Description("s_html_chat_notice: {0} has rejected your whispers.")] - WhisperReject = 6, - [Description("s_html_chat_party")] - Party = 7, - [Description("s_html_chat_guild")] - Guild = 8, - [Description("s_html_chat_img_notice")] - Notice = 9, - Command = 10, - [Description("s_html_chat_world")] - World = 11, - [Description("s_html_chat_channel")] - Channel = 12, - MeretNoticeAlert = 13, // 14, StringId=53 => Feature 296[MeratMarketClosing] - [Description("s_html_chat_system_notice")] - SystemNotice = 15, - [Description("s_html_chat_super")] - Super = 16, - NoticeAlert = 17, // 26 - [Description("s_html_chat_guild_mega_phone")] - GuildNotice = 18, - [Description("s_html_chat_system")] - System = 19, // Guild chat color without [Guild] prefix - [Description("s_html_chat_club")] - Club = 20, - [Description("s_html_chat_ugc_event: It's party time. Click on this message to come to my home and join in the {4} event!")] - UgcEvent = 22, - Wedding = 25, -} - -public enum AnnounceType { - [Description("{0} has succeeded in enchanting {1}.")] - s_itemenchant_success_notice = 1, - [Description("{0} rolled {2} on their {1}!")] - s_itemremake_chat_maxoption = 2, - [Description("{0} has earned {2} from {1}.")] - s_msg_item_open_item_announce_to_world = 3, - [Description("{0} got {1} from the Festival House.")] - s_card_reverse_game_reward_notice = 4, - [Description("{0} rolled {2} on their {1}!")] - s_item_merge_chat_maxoption = 5, - [Description("{0} analyzed {1} and received {2}.")] - s_msg_item_identified_item_announce_to_world = 6, -} +using System.ComponentModel; + +namespace Maple2.Model.Enum; + +public enum ChatType { + [Description("s_html_chat_normal")] + Normal = 0, + // 1: s_html_chat_channel + // 2: s_html_chat_channel + [Description("s_html_chat_whisper_from")] + WhisperFrom = 3, + [Description("s_html_chat_whisper_to")] + WhisperTo = 4, + [Description("s_html_chat_notice: Unable to send whisper.")] + WhisperFail = 5, + [Description("s_html_chat_notice: {0} has rejected your whispers.")] + WhisperReject = 6, + [Description("s_html_chat_party")] + Party = 7, + [Description("s_html_chat_guild")] + Guild = 8, + [Description("s_html_chat_img_notice")] + Notice = 9, + Command = 10, + [Description("s_html_chat_world")] + World = 11, + [Description("s_html_chat_channel")] + Channel = 12, + MeretNoticeAlert = 13, // 14, StringId=53 => Feature 296[MeratMarketClosing] + [Description("s_html_chat_system_notice")] + SystemNotice = 15, + [Description("s_html_chat_super")] + Super = 16, + NoticeAlert = 17, // 26 + [Description("s_html_chat_guild_mega_phone")] + GuildNotice = 18, + [Description("s_html_chat_system")] + System = 19, // Guild chat color without [Guild] prefix + [Description("s_html_chat_club")] + Club = 20, + [Description("s_html_chat_ugc_event: It's party time. Click on this message to come to my home and join in the {4} event!")] + UgcEvent = 22, + Wedding = 25, +} + +public enum AnnounceType { + [Description("{0} has succeeded in enchanting {1}.")] + s_itemenchant_success_notice = 1, + [Description("{0} rolled {2} on their {1}!")] + s_itemremake_chat_maxoption = 2, + [Description("{0} has earned {2} from {1}.")] + s_msg_item_open_item_announce_to_world = 3, + [Description("{0} got {1} from the Festival House.")] + s_card_reverse_game_reward_notice = 4, + [Description("{0} rolled {2} on their {1}!")] + s_item_merge_chat_maxoption = 5, + [Description("{0} analyzed {1} and received {2}.")] + s_msg_item_identified_item_announce_to_world = 6, +} diff --git a/Maple2.Model/Enum/Club.cs b/Maple2.Model/Enum/Club.cs index a3c7a06aa..7b57ebac5 100644 --- a/Maple2.Model/Enum/Club.cs +++ b/Maple2.Model/Enum/Club.cs @@ -1,13 +1,13 @@ -namespace Maple2.Model.Enum; - -public enum ClubState : byte { - Staged = 1, - Established = 2, -} - -public enum ClubResponse { - Accept = 0, - Reject = 76, - Fail = 77, - Disband = 207, -} +namespace Maple2.Model.Enum; + +public enum ClubState : byte { + Staged = 1, + Established = 2, +} + +public enum ClubResponse { + Accept = 0, + Reject = 76, + Fail = 77, + Disband = 207, +} diff --git a/Maple2.Model/Enum/CombineSpawnGroupType.cs b/Maple2.Model/Enum/CombineSpawnGroupType.cs index e3648c353..6d3c31f2d 100644 --- a/Maple2.Model/Enum/CombineSpawnGroupType.cs +++ b/Maple2.Model/Enum/CombineSpawnGroupType.cs @@ -1,7 +1,7 @@ -namespace Maple2.Model.Enum; - -public enum CombineSpawnGroupType { - none, - npc, - interactObject, -} +namespace Maple2.Model.Enum; + +public enum CombineSpawnGroupType { + none, + npc, + interactObject, +} diff --git a/Maple2.Model/Enum/CompareType.cs b/Maple2.Model/Enum/CompareType.cs index 4ae5d4ba3..062a88c59 100644 --- a/Maple2.Model/Enum/CompareType.cs +++ b/Maple2.Model/Enum/CompareType.cs @@ -1,14 +1,14 @@ -namespace Maple2.Model.Enum; - -public enum CompareType { - Equals = 0, - Less = 1, - LessEquals = 2, - Greater = 3, - GreaterEquals = 4, -} - -public enum CompareStatValueType { - CurrentPercentage = 0, - TotalValue = 1, -} +namespace Maple2.Model.Enum; + +public enum CompareType { + Equals = 0, + Less = 1, + LessEquals = 2, + Greater = 3, + GreaterEquals = 4, +} + +public enum CompareStatValueType { + CurrentPercentage = 0, + TotalValue = 1, +} diff --git a/Maple2.Model/Enum/ConditionType.cs b/Maple2.Model/Enum/ConditionType.cs index c9cc69e76..1530a6c85 100644 --- a/Maple2.Model/Enum/ConditionType.cs +++ b/Maple2.Model/Enum/ConditionType.cs @@ -1,327 +1,327 @@ -// ReSharper disable InconsistentNaming - -namespace Maple2.Model.Enum; - -public enum ConditionType { - unknown = 0, - empty = 1, - item_pickup = 2, - item_exist = 3, - item_inven = 4, - item_break = 5, - item_add = 6, - item_destroy = 7, - item_collect = 8, - item_gear_score = 9, - item_collect_revise = 10, - npc = 11, - npc_no_damage = 12, - npc_timeattack = 13, - npc_lasthit = 14, - npc_lasthit_buff = 15, - npc_lasthit_time = 16, - spawner = 17, - npc_race = 18, - killcount = 19, - npc_field_boss = 20, - npc_field_elite = 21, - npc_dungeon_boss = 22, - npc_assist_bonus = 23, - npc_event_tag = 24, - dialogue = 25, - talk_in = 26, - interact_npc = 27, - interact_object = 28, - interact_object_rep = 29, - breakable_object = 30, - vibrate_object = 31, - controller = 32, - controller_mine = 33, - controller_other = 34, - splash_plant = 35, - map = 36, - explore = 37, - continent = 38, - explore_continent = 39, - level = 40, - level_up = 41, - exp = 42, - exp_rate = 43, - adventure_level = 44, - adventure_level_up = 45, - quest_clear = 46, - quest = 47, - quest_revise_achieve = 48, - quest_clear_by_chapter = 49, - quest_daily = 50, - quest_guide = 51, - quest_accept = 52, - field_mission = 53, - mission_point = 54, - fame_point = 55, - fame_grade = 56, - quest_field = 57, - quest_field_first_clear = 58, - quest_alliance = 59, - quest_alliance_by_grade = 60, - mission_attack_system = 61, - skyfortress_system = 62, - repeat_quest_clear = 63, - playtime = 64, - stay_map = 65, - survive_map = 66, - stay_cube = 67, - survive_cube = 68, - job = 69, - job_change = 70, - subjob_change = 71, - meso_donation = 72, - meso = 73, - get_karma_token = 74, - get_honor_token = 75, - get_lu_token = 76, - get_habi_token = 77, - get_reverse_coin = 79, - get_mentor_token = 80, - get_mentee_token = 81, - get_star_point = 82, - use_merat = 83, - skill = 84, - skill_die = 85, - skill_damage_npc = 86, - buff = 87, - taxifind = 88, - taxiuse = 89, - taxifee = 90, - best_taxi_use = 91, - best_taxi_fee = 92, - hero_achieve = 93, - trophy_point = 94, - hero_achieve_grade = 95, - revise_achieve_single_grade = 96, - revise_achieve_multi_grade = 97, - revival = 98, - hit_tombstone = 99, - shop_buy = 100, - shop_buy_karma_token = 101, - shop_buy_honor_token = 102, - shop_sell = 103, - shop_buy_lu_token = 104, - shop_buy_habi_token = 105, - shop_buy_reverse_coin = 106, - shop_buy_mentro_token = 107, - shop_buy_mentee_token = 108, - shop_buy_star_point = 109, - limited_bundle_buy = 110, - pvp_win = 111, - pvp_kill = 112, - pvp_die = 113, - guildpvp_win = 114, - guildpvp_kill = 115, - guildpvp_die = 116, - pvp_win_score = 117, - pvp_win_time = 118, - pvp_participation = 119, - shadow_world_kill = 120, - shadow_world_die = 121, - enchant_result = 122, - beauty_add = 123, - beauty_change = 124, - beauty_change_color = 125, - beauty_random = 126, - beauty_style_add = 127, - beauty_style_apply = 128, - trigger = 129, - minigame_clear = 130, - useropen_minigame_clear = 131, - guild_join = 132, - guild_join_req = 133, - guild_championship = 134, - guild_exp = 135, - guild_trophy = 136, - guild_attendance = 137, - guild_donate = 138, - run = 139, - swim = 140, - climb = 141, - glide = 142, - riding = 143, - crawl = 144, - fall = 145, - holdtime = 146, - ropetime = 147, - laddertime = 148, - emotiontime = 149, - swimtime = 150, - playinstrument_time = 151, - play_ensenble_time = 152, - emotion = 153, - couple_dance_event = 154, - item_move = 155, - buy_house = 156, - extend_house = 157, - install_item = 158, - uninstall_item = 159, - rotate_cube = 160, - interior_exp = 161, - interior_exp_offset = 162, - interior_level = 163, - interior_point = 164, - enter_otherhouse = 165, - buy_cube = 166, - create_blueprint = 167, - send_mail = 168, - resolve_panelty = 169, - change_equip = 170, - change_ugc_equip = 171, - equip_exist = 172, - change_profile = 173, - banner = 174, - commend_home = 175, - home_doctor = 176, - home_bank = 177, - home_goto = 178, - item_design = 179, - fall_survive = 180, - fall_die = 181, - fall_damage = 182, - attendance = 183, - dungeon_key_use = 184, - dungeon_reward = 185, - dungeon_reward_group = 186, - dungeon_random_bonus = 187, - dungeon_help_beginner = 188, - dungeon_help_beginner_helper = 189, - dungeon_help_beginner_helpee = 190, - dungeon_rank_clear_group = 191, - dungeon_rank_clear = 192, - dungeon_rank = 193, - dungeon_clear = 194, - dungeon_clear_group = 195, - dungeon_first_clear = 196, - dungeon_round_clear = 197, - maid_get_item = 198, - maid_salary = 199, - maid_jackpot = 200, - maid_affinity = 201, - maid_profile = 202, - jump = 203, - fish = 204, - fish_success_bait = 205, - fish_fail = 206, - fish_big = 207, - fish_collect = 208, - fish_goldmedal = 209, - auto_fishing = 210, - music_play_score = 211, - music_play_score_by_name = 212, - music_play_score_time = 213, - music_play_instrument_time = 214, - music_play_instrument_mastery = 215, - music_play_ensemble = 216, - music_play_ensemble_in = 217, - music_concert_cheer_up = 218, - openItemBox = 219, - openStoryBook = 220, - festival_event = 221, - install_billboard = 222, - smart_push = 223, - item_remake_option = 224, - item_remake_option_record = 225, - pet_remake_option = 226, - pet_remake_option_record = 227, - pvp_win_with_buff = 228, - pvp_win_with_grade = 229, - pvp_win_perfect = 230, - gemstone_upgrade = 231, - gemstone_upgrade_success = 232, - gemstone_upgrade_fail = 233, - gemstone_upgrade_try = 234, - gemstone_puton = 235, - gemstone_putoff = 236, - skin_gemstone_puton = 237, - skin_gemstone_putoff = 238, - equip_gemstone_puton = 239, - equip_gemstone_putoff = 240, - socket_unlock = 241, - socket_unlock_success = 242, - socket_unlock_fail = 243, - socket_unlock_try = 244, - character_ability_learn = 245, - character_ability_reset = 246, - mastery_grade = 247, - set_mastery_grade = 248, - music_play_grade = 249, - fisher_grade = 250, - mastery_harvest = 251, - mastery_harvest_try = 252, - mastery_harvest_otherhouse = 253, - mastery_harvest_guildhouse = 254, - mastery_manufacturing = 255, - mastery_farming = 256, - mastery_farming_try = 257, - mastery_gathering = 258, - mastery_gathering_try = 259, - club_join = 260, - buddy_request = 261, - chat = 262, - guild_trigger = 263, - pet_collect = 264, - pet_first_collect = 265, - pet_enchant = 266, - pet_enchant_exp = 267, - pet_taming = 268, - pet_catch_category = 269, - pet_catch_grade = 270, - pet_catch_id = 271, - pet_evolution_point_by_rank = 272, - pet_evolution_by_rank = 273, - game_helper_service = 274, - idip_app_attendance = 275, - idip_live_broadcast = 276, - idip_adventure_bar = 277, - vipgm = 278, - item_merge_success = 279, - donation_item = 280, - donation_type = 281, - play_rps = 282, - play_rps_win = 283, - play_rps_lose = 284, - play_rps_draw = 285, - user_find = 286, - survival_enter = 287, - survival_kill = 288, - survival_kill_outside = 289, - survival_kill_use_skill = 290, - survival_total_kill_use_skill = 291, - survival_total_kill_use_single_skill = 292, - survival_double_kill = 293, - survival_win_without_interact = 294, - survival_win_without_npckill = 295, - survival_win_use_one_skill = 296, - survival_rank_with_kill = 297, - survival_breakable_object = 298, - survival_npc_kill = 299, - survival_item_get = 300, - survival_buy_gold_pass = 301, - worldchampion_hit = 302, - worldchampion_damage = 303, - worldchampion_reward = 304, - nurturing_play = 305, - nurturing_eat = 306, - nurturing_growth = 307, - lapenshard_upgrade_try = 308, - lapenshard_upgrade_fail = 309, - lapenshard_upgrade_success = 310, - lapenshard_upgrade_result = 311, - wedding_propose = 312, - wedding_propose_decline = 313, - wedding_propose_declined = 314, - wedding_hall_reserve = 315, - wedding_hall_change = 316, - wedding_hall_cancel = 317, - wedding_guest = 318, - wedding_divorce = 319, - wedding_complete = 320, - unlimited_enchant = 321, -} +// ReSharper disable InconsistentNaming + +namespace Maple2.Model.Enum; + +public enum ConditionType { + unknown = 0, + empty = 1, + item_pickup = 2, + item_exist = 3, + item_inven = 4, + item_break = 5, + item_add = 6, + item_destroy = 7, + item_collect = 8, + item_gear_score = 9, + item_collect_revise = 10, + npc = 11, + npc_no_damage = 12, + npc_timeattack = 13, + npc_lasthit = 14, + npc_lasthit_buff = 15, + npc_lasthit_time = 16, + spawner = 17, + npc_race = 18, + killcount = 19, + npc_field_boss = 20, + npc_field_elite = 21, + npc_dungeon_boss = 22, + npc_assist_bonus = 23, + npc_event_tag = 24, + dialogue = 25, + talk_in = 26, + interact_npc = 27, + interact_object = 28, + interact_object_rep = 29, + breakable_object = 30, + vibrate_object = 31, + controller = 32, + controller_mine = 33, + controller_other = 34, + splash_plant = 35, + map = 36, + explore = 37, + continent = 38, + explore_continent = 39, + level = 40, + level_up = 41, + exp = 42, + exp_rate = 43, + adventure_level = 44, + adventure_level_up = 45, + quest_clear = 46, + quest = 47, + quest_revise_achieve = 48, + quest_clear_by_chapter = 49, + quest_daily = 50, + quest_guide = 51, + quest_accept = 52, + field_mission = 53, + mission_point = 54, + fame_point = 55, + fame_grade = 56, + quest_field = 57, + quest_field_first_clear = 58, + quest_alliance = 59, + quest_alliance_by_grade = 60, + mission_attack_system = 61, + skyfortress_system = 62, + repeat_quest_clear = 63, + playtime = 64, + stay_map = 65, + survive_map = 66, + stay_cube = 67, + survive_cube = 68, + job = 69, + job_change = 70, + subjob_change = 71, + meso_donation = 72, + meso = 73, + get_karma_token = 74, + get_honor_token = 75, + get_lu_token = 76, + get_habi_token = 77, + get_reverse_coin = 79, + get_mentor_token = 80, + get_mentee_token = 81, + get_star_point = 82, + use_merat = 83, + skill = 84, + skill_die = 85, + skill_damage_npc = 86, + buff = 87, + taxifind = 88, + taxiuse = 89, + taxifee = 90, + best_taxi_use = 91, + best_taxi_fee = 92, + hero_achieve = 93, + trophy_point = 94, + hero_achieve_grade = 95, + revise_achieve_single_grade = 96, + revise_achieve_multi_grade = 97, + revival = 98, + hit_tombstone = 99, + shop_buy = 100, + shop_buy_karma_token = 101, + shop_buy_honor_token = 102, + shop_sell = 103, + shop_buy_lu_token = 104, + shop_buy_habi_token = 105, + shop_buy_reverse_coin = 106, + shop_buy_mentro_token = 107, + shop_buy_mentee_token = 108, + shop_buy_star_point = 109, + limited_bundle_buy = 110, + pvp_win = 111, + pvp_kill = 112, + pvp_die = 113, + guildpvp_win = 114, + guildpvp_kill = 115, + guildpvp_die = 116, + pvp_win_score = 117, + pvp_win_time = 118, + pvp_participation = 119, + shadow_world_kill = 120, + shadow_world_die = 121, + enchant_result = 122, + beauty_add = 123, + beauty_change = 124, + beauty_change_color = 125, + beauty_random = 126, + beauty_style_add = 127, + beauty_style_apply = 128, + trigger = 129, + minigame_clear = 130, + useropen_minigame_clear = 131, + guild_join = 132, + guild_join_req = 133, + guild_championship = 134, + guild_exp = 135, + guild_trophy = 136, + guild_attendance = 137, + guild_donate = 138, + run = 139, + swim = 140, + climb = 141, + glide = 142, + riding = 143, + crawl = 144, + fall = 145, + holdtime = 146, + ropetime = 147, + laddertime = 148, + emotiontime = 149, + swimtime = 150, + playinstrument_time = 151, + play_ensenble_time = 152, + emotion = 153, + couple_dance_event = 154, + item_move = 155, + buy_house = 156, + extend_house = 157, + install_item = 158, + uninstall_item = 159, + rotate_cube = 160, + interior_exp = 161, + interior_exp_offset = 162, + interior_level = 163, + interior_point = 164, + enter_otherhouse = 165, + buy_cube = 166, + create_blueprint = 167, + send_mail = 168, + resolve_panelty = 169, + change_equip = 170, + change_ugc_equip = 171, + equip_exist = 172, + change_profile = 173, + banner = 174, + commend_home = 175, + home_doctor = 176, + home_bank = 177, + home_goto = 178, + item_design = 179, + fall_survive = 180, + fall_die = 181, + fall_damage = 182, + attendance = 183, + dungeon_key_use = 184, + dungeon_reward = 185, + dungeon_reward_group = 186, + dungeon_random_bonus = 187, + dungeon_help_beginner = 188, + dungeon_help_beginner_helper = 189, + dungeon_help_beginner_helpee = 190, + dungeon_rank_clear_group = 191, + dungeon_rank_clear = 192, + dungeon_rank = 193, + dungeon_clear = 194, + dungeon_clear_group = 195, + dungeon_first_clear = 196, + dungeon_round_clear = 197, + maid_get_item = 198, + maid_salary = 199, + maid_jackpot = 200, + maid_affinity = 201, + maid_profile = 202, + jump = 203, + fish = 204, + fish_success_bait = 205, + fish_fail = 206, + fish_big = 207, + fish_collect = 208, + fish_goldmedal = 209, + auto_fishing = 210, + music_play_score = 211, + music_play_score_by_name = 212, + music_play_score_time = 213, + music_play_instrument_time = 214, + music_play_instrument_mastery = 215, + music_play_ensemble = 216, + music_play_ensemble_in = 217, + music_concert_cheer_up = 218, + openItemBox = 219, + openStoryBook = 220, + festival_event = 221, + install_billboard = 222, + smart_push = 223, + item_remake_option = 224, + item_remake_option_record = 225, + pet_remake_option = 226, + pet_remake_option_record = 227, + pvp_win_with_buff = 228, + pvp_win_with_grade = 229, + pvp_win_perfect = 230, + gemstone_upgrade = 231, + gemstone_upgrade_success = 232, + gemstone_upgrade_fail = 233, + gemstone_upgrade_try = 234, + gemstone_puton = 235, + gemstone_putoff = 236, + skin_gemstone_puton = 237, + skin_gemstone_putoff = 238, + equip_gemstone_puton = 239, + equip_gemstone_putoff = 240, + socket_unlock = 241, + socket_unlock_success = 242, + socket_unlock_fail = 243, + socket_unlock_try = 244, + character_ability_learn = 245, + character_ability_reset = 246, + mastery_grade = 247, + set_mastery_grade = 248, + music_play_grade = 249, + fisher_grade = 250, + mastery_harvest = 251, + mastery_harvest_try = 252, + mastery_harvest_otherhouse = 253, + mastery_harvest_guildhouse = 254, + mastery_manufacturing = 255, + mastery_farming = 256, + mastery_farming_try = 257, + mastery_gathering = 258, + mastery_gathering_try = 259, + club_join = 260, + buddy_request = 261, + chat = 262, + guild_trigger = 263, + pet_collect = 264, + pet_first_collect = 265, + pet_enchant = 266, + pet_enchant_exp = 267, + pet_taming = 268, + pet_catch_category = 269, + pet_catch_grade = 270, + pet_catch_id = 271, + pet_evolution_point_by_rank = 272, + pet_evolution_by_rank = 273, + game_helper_service = 274, + idip_app_attendance = 275, + idip_live_broadcast = 276, + idip_adventure_bar = 277, + vipgm = 278, + item_merge_success = 279, + donation_item = 280, + donation_type = 281, + play_rps = 282, + play_rps_win = 283, + play_rps_lose = 284, + play_rps_draw = 285, + user_find = 286, + survival_enter = 287, + survival_kill = 288, + survival_kill_outside = 289, + survival_kill_use_skill = 290, + survival_total_kill_use_skill = 291, + survival_total_kill_use_single_skill = 292, + survival_double_kill = 293, + survival_win_without_interact = 294, + survival_win_without_npckill = 295, + survival_win_use_one_skill = 296, + survival_rank_with_kill = 297, + survival_breakable_object = 298, + survival_npc_kill = 299, + survival_item_get = 300, + survival_buy_gold_pass = 301, + worldchampion_hit = 302, + worldchampion_damage = 303, + worldchampion_reward = 304, + nurturing_play = 305, + nurturing_eat = 306, + nurturing_growth = 307, + lapenshard_upgrade_try = 308, + lapenshard_upgrade_fail = 309, + lapenshard_upgrade_success = 310, + lapenshard_upgrade_result = 311, + wedding_propose = 312, + wedding_propose_decline = 313, + wedding_propose_declined = 314, + wedding_hall_reserve = 315, + wedding_hall_change = 316, + wedding_hall_cancel = 317, + wedding_guest = 318, + wedding_divorce = 319, + wedding_complete = 320, + unlimited_enchant = 321, +} diff --git a/Maple2.Model/Enum/ConfigurableCubeType.cs b/Maple2.Model/Enum/ConfigurableCubeType.cs index 2b4a22526..2ecf56e0f 100644 --- a/Maple2.Model/Enum/ConfigurableCubeType.cs +++ b/Maple2.Model/Enum/ConfigurableCubeType.cs @@ -1,8 +1,8 @@ -namespace Maple2.Model.Enum; -// ReSharper disable InconsistentNaming - -public enum ConfigurableCubeType { - None = 0, - UGCNotice = 1, - UGCPortal = 2, -} +namespace Maple2.Model.Enum; +// ReSharper disable InconsistentNaming + +public enum ConfigurableCubeType { + None = 0, + UGCNotice = 1, + UGCPortal = 2, +} diff --git a/Maple2.Model/Enum/CubePortalDestination.cs b/Maple2.Model/Enum/CubePortalDestination.cs index ecb98eedd..47fdcd949 100644 --- a/Maple2.Model/Enum/CubePortalDestination.cs +++ b/Maple2.Model/Enum/CubePortalDestination.cs @@ -1,7 +1,7 @@ -namespace Maple2.Model.Enum; - -public enum CubePortalDestination : byte { - PortalInHome = 0, - SelectedMap = 1, - FriendHome = 2, -} +namespace Maple2.Model.Enum; + +public enum CubePortalDestination : byte { + PortalInHome = 0, + SelectedMap = 1, + FriendHome = 2, +} diff --git a/Maple2.Model/Enum/CurrencyType.cs b/Maple2.Model/Enum/CurrencyType.cs index 916f06ff2..b470f2e00 100644 --- a/Maple2.Model/Enum/CurrencyType.cs +++ b/Maple2.Model/Enum/CurrencyType.cs @@ -1,20 +1,20 @@ -namespace Maple2.Model.Enum; - -public enum CurrencyType : byte { - None = 0, - ValorToken = 3, - Treva = 4, - Rue = 5, - HaviFruit = 6, - ReverseCoin = 9, - MentorToken = 10, - MenteeToken = 11, - StarPoint = 12, - MesoToken = 13, -} - -public enum SmartPushCurrencyType { - None = 0, - Meso = 1, - Meret = 2, -} +namespace Maple2.Model.Enum; + +public enum CurrencyType : byte { + None = 0, + ValorToken = 3, + Treva = 4, + Rue = 5, + HaviFruit = 6, + ReverseCoin = 9, + MentorToken = 10, + MenteeToken = 11, + StarPoint = 12, + MesoToken = 13, +} + +public enum SmartPushCurrencyType { + None = 0, + Meso = 1, + Meret = 2, +} diff --git a/Maple2.Model/Enum/DamageType.cs b/Maple2.Model/Enum/DamageType.cs index d907afc11..fde69cbb8 100644 --- a/Maple2.Model/Enum/DamageType.cs +++ b/Maple2.Model/Enum/DamageType.cs @@ -1,9 +1,9 @@ -namespace Maple2.Model.Enum; - -public enum DamageType : byte { - Normal = 0, - Critical = 1, - Miss = 2, - Block = 3, - // 8 is mapped to 3 -} +namespace Maple2.Model.Enum; + +public enum DamageType : byte { + Normal = 0, + Critical = 1, + Miss = 2, + Block = 3, + // 8 is mapped to 3 +} diff --git a/Maple2.Model/Enum/Death.cs b/Maple2.Model/Enum/Death.cs index 2f4eee71e..322f64b89 100644 --- a/Maple2.Model/Enum/Death.cs +++ b/Maple2.Model/Enum/Death.cs @@ -1,7 +1,7 @@ -namespace Maple2.Model.Enum; - -public enum DeathState : short { - Alive = 0, - FirstDeath = 1, - Metal = 2, -} +namespace Maple2.Model.Enum; + +public enum DeathState : short { + Alive = 0, + FirstDeath = 1, + Metal = 2, +} diff --git a/Maple2.Model/Enum/DropType.cs b/Maple2.Model/Enum/DropType.cs index 0139a445d..920b7d6bd 100644 --- a/Maple2.Model/Enum/DropType.cs +++ b/Maple2.Model/Enum/DropType.cs @@ -1,7 +1,7 @@ -namespace Maple2.Model.Enum; - -public enum DropType : byte { - Default = 0, - Unknown = 1, - Player = 2, -} +namespace Maple2.Model.Enum; + +public enum DropType : byte { + Default = 0, + Unknown = 1, + Player = 2, +} diff --git a/Maple2.Model/Enum/Dungeon.cs b/Maple2.Model/Enum/Dungeon.cs index 2b2a104c9..b4b44fc9b 100644 --- a/Maple2.Model/Enum/Dungeon.cs +++ b/Maple2.Model/Enum/Dungeon.cs @@ -1,165 +1,165 @@ -using System.ComponentModel; - -namespace Maple2.Model.Enum; - -public enum DungeonPlayType { - none = 0, - limitReward = 1, - limitEnter = 2, -} - -public enum DungeonGroupType { - none = 0, - normal = 1, - raid = 2, - chaosRaid = 3, - reverseRaid = 4, - lapenta = 5, - guildRaid = 6, - darkStream = 7, - worldBossDungeon = 8, - item = 9, - vip = 10, - @event = 11, - fameChallenge = 12, - colosseum = 13, - turka = 14, -} - -public enum DungeonCooldownType { - none = 0, - dayOfWeeks = 1, - nextDay = 2, -} - -public enum DungeonTimerType { - none = 0, - clock = 1, - gauge = 2, -} - -public enum DungeonBossRankingType { - None = 0, - Kill = 1, - Damage = 2, - KillRumble = 3, - MultiKill = 4, - Colosseum = 5, -} - -public enum DungeonRequireRole { - None = 0, - Support = 1, - Tank = 2, -} - -public enum DungeonEnterLimit : byte { - Rookie = 0, - Veteran = 1, - MinLevel = 12, - Achievement = 13, - Vip = 14, - Gearscore = 15, - DungeonClear = 16, - Buff = 17, - RecommendedWeapon = 18, -} - -public enum DungeonRoomModify : byte { - [Description("s_room_dungeon_give_reward - You got your dungeon rewards.")] - GiveReward = 1, - [Description("s_room_dungeon_give_dungeonHelperReward - You got a Dungeon Helper reward.")] - GiveDungeonHelperReward = 2, - [Description("s_room_dungeon_reward_addExtraCount - The number of rewards has increased.")] - AddExtraCount = 3, - [Description("s_room_dungeon_record_notify_change_expert - You're now a veteran in {0}. Collect Dungeon Helper rewards by clearing the dungeon with rookies!")] - ChangeToExpert = 4, -} - -public enum DungeonAccumulationRecordType { - [Description("s_dungeon_record_accum_damage - Total Damage: {0}")] - TotalDamage = 0, - [Description("s_dungeon_record_accum_heal - Total Healing: {0}")] - TotalHealing = 1, - [Description("s_dungeon_record_accum_hit_count - Total Hit Count: {0}")] - TotalHitCount = 2, - [Description("s_dungeon_record_boss_last_hit - Boss Final Blows: {0}")] - BossFinalBlows = 3, - [Description("s_dungeon_record_accum_move_distance - Total Move Distance: {0}")] - TotalMoveDistance = 4, - [Description("s_dungeon_record_accum_critical_damage - Total Critical Damage: {0}")] - TotalCriticalDamage = 5, - [Description("s_dungeon_record_max_critial_damage - Maximum Critical Damage: {0}")] - MaximumCriticalDamage = 6, - [Description("s_dungeon_record_accum_monster_kill - Defeated Monsters: {0}")] - DefeatedMonsters = 7, - [Description("s_dungeon_record_accum_be_hit_count - Incoming Damage: {0}")] - IncomingDamage = 8, - [Description("s_dungeon_record_accum_default_skill_damage - Basic Attack Damage: {0}")] - BasicAttackDamage = 9, -} - -public enum DungeonState : byte { - None = 0, - [Description("s_room_dungeon_clear - Dungeon Cleared.")] - Clear = 1, - [Description("s_room_dungeon_fail - Dungeon Failed.")] - Fail = 2, -} - -public enum DungeonMissionRank { - None = -1, - F = 0, - C = 1, - B = 2, - A = 3, - S = 4, - SPlus = 5, -} - -public enum DungeonRewardType : byte { - Meso = 1, - Exp = 2, - Prestige = 3, -} - -[Flags] -public enum DungeonBonusFlag { - None = 1, - [Description("s_dungeon_reward_dungeon_reward_count - Dungeon Clears: {0}")] - Clear = 2, - [Description("s_dungeon_reward_bonus_event - Event Bonus")] - Event = 4, - [Description("s_dungeon_reward_mission_rank - Rank Bonus")] - MissionRank = 8, - [Description("s_dungeon_reward_dungeon_helper - Dungeon Helper Bonus")] - Helper = 16, - [Description("s_dungeon_reward_dungeon_helper_event - Mutual Help Event")] - MutualHelp = 32, - [Description("s_dungeon_reward_mentor - Mentor Bonus")] - Mentor = 64, - [Description("s_dungeon_reward_mentee - Returning Player Bonus")] - Mentee = 128, - [Description("s_dungeon_reward_mentee_party_gift - Mentee Party Gift")] - MenteePartyGift = 256, - [Description("s_dungeon_reward_united_weekly - Weekly Bonus")] - UnitedWeekly = 512, -} - -public enum DungeonMissionType { - LastHitNpc, - PlayTime, - DamageBySkill, - DeathCount, - GainBuff, - LimitUserCount, - Trigger, - DamageToNpc, -} - -[Flags] -public enum DungeonRecordFlag : byte { - None = 0, - Veteran = 1, - Favorite = 2, -} +using System.ComponentModel; + +namespace Maple2.Model.Enum; + +public enum DungeonPlayType { + none = 0, + limitReward = 1, + limitEnter = 2, +} + +public enum DungeonGroupType { + none = 0, + normal = 1, + raid = 2, + chaosRaid = 3, + reverseRaid = 4, + lapenta = 5, + guildRaid = 6, + darkStream = 7, + worldBossDungeon = 8, + item = 9, + vip = 10, + @event = 11, + fameChallenge = 12, + colosseum = 13, + turka = 14, +} + +public enum DungeonCooldownType { + none = 0, + dayOfWeeks = 1, + nextDay = 2, +} + +public enum DungeonTimerType { + none = 0, + clock = 1, + gauge = 2, +} + +public enum DungeonBossRankingType { + None = 0, + Kill = 1, + Damage = 2, + KillRumble = 3, + MultiKill = 4, + Colosseum = 5, +} + +public enum DungeonRequireRole { + None = 0, + Support = 1, + Tank = 2, +} + +public enum DungeonEnterLimit : byte { + Rookie = 0, + Veteran = 1, + MinLevel = 12, + Achievement = 13, + Vip = 14, + Gearscore = 15, + DungeonClear = 16, + Buff = 17, + RecommendedWeapon = 18, +} + +public enum DungeonRoomModify : byte { + [Description("s_room_dungeon_give_reward - You got your dungeon rewards.")] + GiveReward = 1, + [Description("s_room_dungeon_give_dungeonHelperReward - You got a Dungeon Helper reward.")] + GiveDungeonHelperReward = 2, + [Description("s_room_dungeon_reward_addExtraCount - The number of rewards has increased.")] + AddExtraCount = 3, + [Description("s_room_dungeon_record_notify_change_expert - You're now a veteran in {0}. Collect Dungeon Helper rewards by clearing the dungeon with rookies!")] + ChangeToExpert = 4, +} + +public enum DungeonAccumulationRecordType { + [Description("s_dungeon_record_accum_damage - Total Damage: {0}")] + TotalDamage = 0, + [Description("s_dungeon_record_accum_heal - Total Healing: {0}")] + TotalHealing = 1, + [Description("s_dungeon_record_accum_hit_count - Total Hit Count: {0}")] + TotalHitCount = 2, + [Description("s_dungeon_record_boss_last_hit - Boss Final Blows: {0}")] + BossFinalBlows = 3, + [Description("s_dungeon_record_accum_move_distance - Total Move Distance: {0}")] + TotalMoveDistance = 4, + [Description("s_dungeon_record_accum_critical_damage - Total Critical Damage: {0}")] + TotalCriticalDamage = 5, + [Description("s_dungeon_record_max_critial_damage - Maximum Critical Damage: {0}")] + MaximumCriticalDamage = 6, + [Description("s_dungeon_record_accum_monster_kill - Defeated Monsters: {0}")] + DefeatedMonsters = 7, + [Description("s_dungeon_record_accum_be_hit_count - Incoming Damage: {0}")] + IncomingDamage = 8, + [Description("s_dungeon_record_accum_default_skill_damage - Basic Attack Damage: {0}")] + BasicAttackDamage = 9, +} + +public enum DungeonState : byte { + None = 0, + [Description("s_room_dungeon_clear - Dungeon Cleared.")] + Clear = 1, + [Description("s_room_dungeon_fail - Dungeon Failed.")] + Fail = 2, +} + +public enum DungeonMissionRank { + None = -1, + F = 0, + C = 1, + B = 2, + A = 3, + S = 4, + SPlus = 5, +} + +public enum DungeonRewardType : byte { + Meso = 1, + Exp = 2, + Prestige = 3, +} + +[Flags] +public enum DungeonBonusFlag { + None = 1, + [Description("s_dungeon_reward_dungeon_reward_count - Dungeon Clears: {0}")] + Clear = 2, + [Description("s_dungeon_reward_bonus_event - Event Bonus")] + Event = 4, + [Description("s_dungeon_reward_mission_rank - Rank Bonus")] + MissionRank = 8, + [Description("s_dungeon_reward_dungeon_helper - Dungeon Helper Bonus")] + Helper = 16, + [Description("s_dungeon_reward_dungeon_helper_event - Mutual Help Event")] + MutualHelp = 32, + [Description("s_dungeon_reward_mentor - Mentor Bonus")] + Mentor = 64, + [Description("s_dungeon_reward_mentee - Returning Player Bonus")] + Mentee = 128, + [Description("s_dungeon_reward_mentee_party_gift - Mentee Party Gift")] + MenteePartyGift = 256, + [Description("s_dungeon_reward_united_weekly - Weekly Bonus")] + UnitedWeekly = 512, +} + +public enum DungeonMissionType { + LastHitNpc, + PlayTime, + DamageBySkill, + DeathCount, + GainBuff, + LimitUserCount, + Trigger, + DamageToNpc, +} + +[Flags] +public enum DungeonRecordFlag : byte { + None = 0, + Veteran = 1, + Favorite = 2, +} diff --git a/Maple2.Model/Enum/EnchantResult.cs b/Maple2.Model/Enum/EnchantResult.cs index 8ac6a05b2..20d2b7e66 100644 --- a/Maple2.Model/Enum/EnchantResult.cs +++ b/Maple2.Model/Enum/EnchantResult.cs @@ -1,22 +1,22 @@ -using System.ComponentModel; - -namespace Maple2.Model.Enum; - -// These could be flags but not sure -public enum EnchantResult : byte { - None = 0, - Success = 1, - Fail = 2, - LevelDown = 3, - FailDamage = 4, - FailWithProtect = 5, -} - -public enum EnchantFailType : short { - [Description("s_enchant_fail_desc0 - ''")] - None = 0, - [Description("s_enchant_fail_desc1 - Failure will reduce this item's enchantment level.")] - LevelDown = 1, - [Description("s_enchant_fail_desc2 - Failure will render this item Unstable.\\nUnstable gear can no longer be enchanted.")] - Unstabilize = 2, -} +using System.ComponentModel; + +namespace Maple2.Model.Enum; + +// These could be flags but not sure +public enum EnchantResult : byte { + None = 0, + Success = 1, + Fail = 2, + LevelDown = 3, + FailDamage = 4, + FailWithProtect = 5, +} + +public enum EnchantFailType : short { + [Description("s_enchant_fail_desc0 - ''")] + None = 0, + [Description("s_enchant_fail_desc1 - Failure will reduce this item's enchantment level.")] + LevelDown = 1, + [Description("s_enchant_fail_desc2 - Failure will render this item Unstable.\\nUnstable gear can no longer be enchanted.")] + Unstabilize = 2, +} diff --git a/Maple2.Model/Enum/EnchantScrollType.cs b/Maple2.Model/Enum/EnchantScrollType.cs index 95a248db9..cd1d96a08 100644 --- a/Maple2.Model/Enum/EnchantScrollType.cs +++ b/Maple2.Model/Enum/EnchantScrollType.cs @@ -1,9 +1,9 @@ -namespace Maple2.Model.Enum; - -public enum EnchantScrollType : short { - Enchant = 1, - Restore = 2, - Random = 3, - Rune = 4, - Stabilize = 5, -} +namespace Maple2.Model.Enum; + +public enum EnchantScrollType : short { + Enchant = 1, + Restore = 2, + Random = 3, + Rune = 4, + Stabilize = 5, +} diff --git a/Maple2.Model/Enum/EnchantType.cs b/Maple2.Model/Enum/EnchantType.cs index cf52a1ba2..6bc7573b9 100644 --- a/Maple2.Model/Enum/EnchantType.cs +++ b/Maple2.Model/Enum/EnchantType.cs @@ -1,13 +1,13 @@ -namespace Maple2.Model.Enum; - -public enum EnchantType : byte { - None = 0, - Ophelia = 1, - Peachy = 2, -} - -public enum EnchantDamageType { - None = 0, - Destable = 1, - Unstable = 2, // ?? -} +namespace Maple2.Model.Enum; + +public enum EnchantType : byte { + None = 0, + Ophelia = 1, + Peachy = 2, +} + +public enum EnchantDamageType { + None = 0, + Destable = 1, + Unstable = 2, // ?? +} diff --git a/Maple2.Model/Enum/EquipSlot.cs b/Maple2.Model/Enum/EquipSlot.cs index 68b20e376..7e82eaa77 100644 --- a/Maple2.Model/Enum/EquipSlot.cs +++ b/Maple2.Model/Enum/EquipSlot.cs @@ -1,50 +1,50 @@ -// ReSharper disable InconsistentNaming - -using System.ComponentModel; - -namespace Maple2.Model.Enum; - -public enum EquipSlot : sbyte { - [Description("Skin")] - SK = 0, // Male Skin/Female Skin - [Description("Hair")] - HR = 1, - [Description("Face")] - FA = 2, - [Description("Face Decal")] - FD = 3, - [Description("Left Hand")] - LH = 4, - [Description("Right Hand")] - RH = 5, - [Description("Cap")] - CP = 6, - [Description("Mantle")] - MT = 7, - [Description("Clothes")] - CL = 8, - [Description("Pants")] - PA = 9, - [Description("Gloves")] - GL = 10, - [Description("Shoes")] - SH = 11, - [Description("Face Accessory")] - FH = 12, - [Description("Eyewear")] - EY = 13, - [Description("Earring")] - EA = 14, - [Description("Pendant")] - PD = 15, - [Description("Ring")] - RI = 16, - [Description("Belt")] - BE = 17, - [Description("Ear")] - ER = 18, - [Description("Off Hand")] - OH = 19, // Cannot equip in off-hand (LH/RH) - [Description("Unknown")] - Unknown = 20, -} +// ReSharper disable InconsistentNaming + +using System.ComponentModel; + +namespace Maple2.Model.Enum; + +public enum EquipSlot : sbyte { + [Description("Skin")] + SK = 0, // Male Skin/Female Skin + [Description("Hair")] + HR = 1, + [Description("Face")] + FA = 2, + [Description("Face Decal")] + FD = 3, + [Description("Left Hand")] + LH = 4, + [Description("Right Hand")] + RH = 5, + [Description("Cap")] + CP = 6, + [Description("Mantle")] + MT = 7, + [Description("Clothes")] + CL = 8, + [Description("Pants")] + PA = 9, + [Description("Gloves")] + GL = 10, + [Description("Shoes")] + SH = 11, + [Description("Face Accessory")] + FH = 12, + [Description("Eyewear")] + EY = 13, + [Description("Earring")] + EA = 14, + [Description("Pendant")] + PD = 15, + [Description("Ring")] + RI = 16, + [Description("Belt")] + BE = 17, + [Description("Ear")] + ER = 18, + [Description("Off Hand")] + OH = 19, // Cannot equip in off-hand (LH/RH) + [Description("Unknown")] + Unknown = 20, +} diff --git a/Maple2.Model/Enum/ExpMessageCode.cs b/Maple2.Model/Enum/ExpMessageCode.cs index 7cbd6e63b..91026c0ad 100644 --- a/Maple2.Model/Enum/ExpMessageCode.cs +++ b/Maple2.Model/Enum/ExpMessageCode.cs @@ -1,44 +1,44 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Enum; - -public enum ExpMessageCode : ushort { - [Description("You got {0} experience.")] - s_msg_take_exp = 0, - /* These all use the same string but different values - * quest = 1010 - * mission = 1053 - * expDrop = 1062 - * dungeonRelative = 1085 - * miniGame = 3007 - * gathering = 3103 - * manufacturing = 3107 - * petTaming = 3200 - * guildUserExp = 3301 - * monster = 60001 - */ - [Description("You got {0} experience from fishing.")] - s_msg_take_fishing_exp = 1059, - [Description("You got {0} experience for playing an instrument.")] - s_msg_take_play_instrument_exp = 1063, - [Description("You got {0} experience from Maple Arcade.")] - s_msg_take_arcade_exp = 1071, - [Description("You got {0} experience from opening a wooden treasure chest.")] - s_msg_take_normal_chest_exp = 1091, - [Description("You got {0} experience from opening a golden treasure chest.")] - s_msg_take_normal_rare_exp = 1092, - [Description("You got {0} experience from opening a golden treasure chest for the first time.")] - s_msg_take_normal_rare_first_exp = 1093, - [Description("You got {1} experience for discovering the {0} taxi stop.")] - s_msg_take_taxi_exp = 3000, - [Description("You got {1} experience for discovering {0}.")] - s_msg_take_map_exp = 3001, // 3002 for hidden maps - [Description("You got {1} experience for discovering a new area, {0}.")] - s_msg_take_telescope_exp = 3003, - [Description("You got {0} bonus experience.")] - s_msg_take_assist_bonus_exp = 60002, - [Description("You got an experience bonus for helping defeat a powerful foe. (EXP +{0})")] - s_msg_take_assist_bonus_exp_system = 60003, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Enum; + +public enum ExpMessageCode : ushort { + [Description("You got {0} experience.")] + s_msg_take_exp = 0, + /* These all use the same string but different values + * quest = 1010 + * mission = 1053 + * expDrop = 1062 + * dungeonRelative = 1085 + * miniGame = 3007 + * gathering = 3103 + * manufacturing = 3107 + * petTaming = 3200 + * guildUserExp = 3301 + * monster = 60001 + */ + [Description("You got {0} experience from fishing.")] + s_msg_take_fishing_exp = 1059, + [Description("You got {0} experience for playing an instrument.")] + s_msg_take_play_instrument_exp = 1063, + [Description("You got {0} experience from Maple Arcade.")] + s_msg_take_arcade_exp = 1071, + [Description("You got {0} experience from opening a wooden treasure chest.")] + s_msg_take_normal_chest_exp = 1091, + [Description("You got {0} experience from opening a golden treasure chest.")] + s_msg_take_normal_rare_exp = 1092, + [Description("You got {0} experience from opening a golden treasure chest for the first time.")] + s_msg_take_normal_rare_first_exp = 1093, + [Description("You got {1} experience for discovering the {0} taxi stop.")] + s_msg_take_taxi_exp = 3000, + [Description("You got {1} experience for discovering {0}.")] + s_msg_take_map_exp = 3001, // 3002 for hidden maps + [Description("You got {1} experience for discovering a new area, {0}.")] + s_msg_take_telescope_exp = 3003, + [Description("You got {0} bonus experience.")] + s_msg_take_assist_bonus_exp = 60002, + [Description("You got an experience bonus for helping defeat a powerful foe. (EXP +{0})")] + s_msg_take_assist_bonus_exp_system = 60003, +} diff --git a/Maple2.Model/Enum/ExpType.cs b/Maple2.Model/Enum/ExpType.cs index f79b7c4b0..e3615ad7a 100644 --- a/Maple2.Model/Enum/ExpType.cs +++ b/Maple2.Model/Enum/ExpType.cs @@ -1,63 +1,63 @@ -// ReSharper disable InconsistentNaming - -namespace Maple2.Model.Enum; - -public enum ExpType { - none, - unknown, - mapCommon, - mapHidden, - taxi, - telescope, - rareChestFirst, - rareChest, - normalChest, - expDrop, - musicMastery1, - musicMastery2, - musicMastery3, - musicMastery4, - arcade, - fishing, - rest, - bloodMineRank1, - bloodMineRank2, - bloodMineRank3, - bloodMineRankOther, - redDuelWin, - redDuelLose, - btiTeamWin, - btiTeamLose, - rankDuelWin, - rankDuelLose, - gathering, - manufacturing, - miniGame, - userMiniGame, - userMiniGameExtra, - dungeonRelative, - guildUserExp, - petTaming, - construct, - mapleSurvival, - quest, - epicQuest, - mission, - dailyGuildQuest, - weeklyGuildQuest, - dailymission, - dailymissionLevelUp, - questSkyFortress, - randomDungeonBonus, - monster, - assist, - assistBonus, - - dropItem, - darkStream, - dungeonClear, - dungeonBoss, - monsterBoss, - monsterElite, - questEtc, -} +// ReSharper disable InconsistentNaming + +namespace Maple2.Model.Enum; + +public enum ExpType { + none, + unknown, + mapCommon, + mapHidden, + taxi, + telescope, + rareChestFirst, + rareChest, + normalChest, + expDrop, + musicMastery1, + musicMastery2, + musicMastery3, + musicMastery4, + arcade, + fishing, + rest, + bloodMineRank1, + bloodMineRank2, + bloodMineRank3, + bloodMineRankOther, + redDuelWin, + redDuelLose, + btiTeamWin, + btiTeamLose, + rankDuelWin, + rankDuelLose, + gathering, + manufacturing, + miniGame, + userMiniGame, + userMiniGameExtra, + dungeonRelative, + guildUserExp, + petTaming, + construct, + mapleSurvival, + quest, + epicQuest, + mission, + dailyGuildQuest, + weeklyGuildQuest, + dailymission, + dailymissionLevelUp, + questSkyFortress, + randomDungeonBonus, + monster, + assist, + assistBonus, + + dropItem, + darkStream, + dungeonClear, + dungeonBoss, + monsterBoss, + monsterElite, + questEtc, +} diff --git a/Maple2.Model/Enum/FieldProperty.cs b/Maple2.Model/Enum/FieldProperty.cs index a423d96b5..68035745d 100644 --- a/Maple2.Model/Enum/FieldProperty.cs +++ b/Maple2.Model/Enum/FieldProperty.cs @@ -1,26 +1,26 @@ -namespace Maple2.Model.Enum; - -public enum FieldProperty : byte { - Gravity = 1, - MusicConcert = 2, - HidePlayer = 3, - LockPlayer = 4, - UserTagSymbol = 5, - SightRange = 6, - Weather = 7, - AmbientLight = 8, - DirectionalLight = 9, - LocalCamera = 10, - PhotoStudio = 11, -} - -public enum WeatherType : byte { - None = 0, - Snow = 1, - HeavySnow = 2, - Rain = 3, - HeavyRain = 4, - SandStorm = 5, - CherryBlossom = 6, - LeafFall = 7, -} +namespace Maple2.Model.Enum; + +public enum FieldProperty : byte { + Gravity = 1, + MusicConcert = 2, + HidePlayer = 3, + LockPlayer = 4, + UserTagSymbol = 5, + SightRange = 6, + Weather = 7, + AmbientLight = 8, + DirectionalLight = 9, + LocalCamera = 10, + PhotoStudio = 11, +} + +public enum WeatherType : byte { + None = 0, + Snow = 1, + HeavySnow = 2, + Rain = 3, + HeavyRain = 4, + SandStorm = 5, + CherryBlossom = 6, + LeafFall = 7, +} diff --git a/Maple2.Model/Enum/FieldType.cs b/Maple2.Model/Enum/FieldType.cs index 40778c535..6ef40b1cb 100644 --- a/Maple2.Model/Enum/FieldType.cs +++ b/Maple2.Model/Enum/FieldType.cs @@ -1,7 +1,7 @@ -namespace Maple2.Model.Enum; - -public enum FieldType : byte { - Default = 0, - Random = 1, // Random Rooms like Pocket Realms - Dungeon = 2, -} +namespace Maple2.Model.Enum; + +public enum FieldType : byte { + Default = 0, + Random = 1, // Random Rooms like Pocket Realms + Dungeon = 2, +} diff --git a/Maple2.Model/Enum/FishingItemType.cs b/Maple2.Model/Enum/FishingItemType.cs index ec57bf8c0..26e7094ab 100644 --- a/Maple2.Model/Enum/FishingItemType.cs +++ b/Maple2.Model/Enum/FishingItemType.cs @@ -1,8 +1,8 @@ -namespace Maple2.Model.Enum; - -public enum FishingItemType { - Trash = 0, - LightBox = 1, - HeavyBox = 2, - Skin = 3, -} +namespace Maple2.Model.Enum; + +public enum FishingItemType { + Trash = 0, + LightBox = 1, + HeavyBox = 2, + Skin = 3, +} diff --git a/Maple2.Model/Enum/FurnishingCurrencyType.cs b/Maple2.Model/Enum/FurnishingCurrencyType.cs index 3bae5ca9c..efa00aa3f 100644 --- a/Maple2.Model/Enum/FurnishingCurrencyType.cs +++ b/Maple2.Model/Enum/FurnishingCurrencyType.cs @@ -1,6 +1,6 @@ -namespace Maple2.Model.Enum; - -public enum FurnishingCurrencyType : byte { - Meso = 1, - Meret = 3, -} +namespace Maple2.Model.Enum; + +public enum FurnishingCurrencyType : byte { + Meso = 1, + Meret = 3, +} diff --git a/Maple2.Model/Enum/GameEventType.cs b/Maple2.Model/Enum/GameEventType.cs index 45a5d13ea..a10c321a0 100644 --- a/Maple2.Model/Enum/GameEventType.cs +++ b/Maple2.Model/Enum/GameEventType.cs @@ -1,119 +1,119 @@ - -// ReSharper disable InconsistentNaming -namespace Maple2.Model.Enum; - -public enum GameEventType { - SAHotTime, - StringBoard, - QuestAdditionalReward, - FindDungeonHelperBonus, - DungeonHelpBonusReward, - MiniGameReward, - FieldBossReward, - TimeEventHotTime, - RandomRoomProb, - SaleEnchant, - SaleRemake, - SaleGemStoneUpgrade, - SaleBeautyShop, - AdventureLevelBonusExpRate, - SaleHousing, - DungeonOpenPeriod, - PetComposeSale, - MasteryReward, - TransferItemBound, - DTReward, - StringBoardLink, - ConditionCount, - SaleAutoFishing, - SaleAutoPlayInstrument, - SaleLapenshardUpgradeRGB, - SaleChat, - DungeonBonusReward, - UserCondition, - PlayGift, - UGCMapSaleCoupon, - PremiumMarketSale, - CharacterFirstConnect, - LevelUpPackage, - DailyLoginReward, - FreeAirTaxi, - MesoRevival, - EventFieldPopup, - AttendGift, - ReturnUser, - NewUser, - ReturnUserYearRound, - ReturnUserCandidate, - DungeonExtraReward, - ItemBuffDungeon, - BlueMarble, - NpcEventDrop, - LoginNotice, - SaleUgcDesign, - EpicRestart, - LobbyMap, - DungeonEnterTimeLimit, - ReverseRaidDungeonOpen, - SurvivalHotField, - SurvivalDoubleHotField, - SurvivalHotTime, - BurningLevelup, - GuildPartyDungeonReward, - RankDuelReward, - GuildSkillSale, - StampEvent, - WorldmapIcon, - MasteryReduce, - SpawnNpc, - BlackMarketFeeDiscount, - GuildVsGameOpen, - UGCMapContractSale, - UGCMapExtensionSale, - ReverseOxQuiz, - CustomTriggerString, - PetMastery, - MeratMarketOpenTab, - ItemBox, - PetBattleExp, - GuildPersonalSkillSale, - Gallery, - DungoneBuff, - TimeRunEvent, - GuildChampionship, - ShutdownTriggerSkipAction, - Festival, - FieldEffect, - Maview, - GameLog, - Snowman, - reactor, - CollectItemGroup, - RPS, - TreeWatering, - CoupleDance, - LuckyChance, - ArcadeOpen, - BingoEvent, - LimitedQuantity, - MapleSurvivalOpenPeriod, - ShutdownMapleSurvival, - FinishPayback, - ExchangeScrollSale, - SendMail, - TrafficOptimizer, - StarShop, - NpcShopShowItem, - FieldBuff, - LongTermAttendGift, - MassiveConstructionEvent, - ConstructShowItem, - MiniGameBonusReward, - GuildChampionShip, - GuildEventFund, - GuildExp, - ActiveUser, - SaleSkillUpgrade, - DungeonBuff, - QuestTag, -} + +// ReSharper disable InconsistentNaming +namespace Maple2.Model.Enum; + +public enum GameEventType { + SAHotTime, + StringBoard, + QuestAdditionalReward, + FindDungeonHelperBonus, + DungeonHelpBonusReward, + MiniGameReward, + FieldBossReward, + TimeEventHotTime, + RandomRoomProb, + SaleEnchant, + SaleRemake, + SaleGemStoneUpgrade, + SaleBeautyShop, + AdventureLevelBonusExpRate, + SaleHousing, + DungeonOpenPeriod, + PetComposeSale, + MasteryReward, + TransferItemBound, + DTReward, + StringBoardLink, + ConditionCount, + SaleAutoFishing, + SaleAutoPlayInstrument, + SaleLapenshardUpgradeRGB, + SaleChat, + DungeonBonusReward, + UserCondition, + PlayGift, + UGCMapSaleCoupon, + PremiumMarketSale, + CharacterFirstConnect, + LevelUpPackage, + DailyLoginReward, + FreeAirTaxi, + MesoRevival, + EventFieldPopup, + AttendGift, + ReturnUser, + NewUser, + ReturnUserYearRound, + ReturnUserCandidate, + DungeonExtraReward, + ItemBuffDungeon, + BlueMarble, + NpcEventDrop, + LoginNotice, + SaleUgcDesign, + EpicRestart, + LobbyMap, + DungeonEnterTimeLimit, + ReverseRaidDungeonOpen, + SurvivalHotField, + SurvivalDoubleHotField, + SurvivalHotTime, + BurningLevelup, + GuildPartyDungeonReward, + RankDuelReward, + GuildSkillSale, + StampEvent, + WorldmapIcon, + MasteryReduce, + SpawnNpc, + BlackMarketFeeDiscount, + GuildVsGameOpen, + UGCMapContractSale, + UGCMapExtensionSale, + ReverseOxQuiz, + CustomTriggerString, + PetMastery, + MeratMarketOpenTab, + ItemBox, + PetBattleExp, + GuildPersonalSkillSale, + Gallery, + DungoneBuff, + TimeRunEvent, + GuildChampionship, + ShutdownTriggerSkipAction, + Festival, + FieldEffect, + Maview, + GameLog, + Snowman, + reactor, + CollectItemGroup, + RPS, + TreeWatering, + CoupleDance, + LuckyChance, + ArcadeOpen, + BingoEvent, + LimitedQuantity, + MapleSurvivalOpenPeriod, + ShutdownMapleSurvival, + FinishPayback, + ExchangeScrollSale, + SendMail, + TrafficOptimizer, + StarShop, + NpcShopShowItem, + FieldBuff, + LongTermAttendGift, + MassiveConstructionEvent, + ConstructShowItem, + MiniGameBonusReward, + GuildChampionShip, + GuildEventFund, + GuildExp, + ActiveUser, + SaleSkillUpgrade, + DungeonBuff, + QuestTag, +} diff --git a/Maple2.Model/Enum/GameEventUserValueType.cs b/Maple2.Model/Enum/GameEventUserValueType.cs index 9d74d5eb9..75312f978 100644 --- a/Maple2.Model/Enum/GameEventUserValueType.cs +++ b/Maple2.Model/Enum/GameEventUserValueType.cs @@ -1,36 +1,36 @@ -// ReSharper disable InconsistentNaming - -namespace Maple2.Model.Enum; - -public enum GameEventUserValueType { - // Attendance Event - AttendanceActive = 100, //?? maybe. String is "True" - AttendanceCompletedTimestamp = 101, - AttendanceRewardsClaimed = 102, - AttendanceEarlyParticipationRemaining = 103, - AttendanceAccumulatedTime = 106, - - // DTReward - DTRewardStartTime = 700, // start time - DTRewardCurrentTime = 701, // current item accumulated time - DTRewardRewardIndex = 702, // unk value seen is "1" - DTRewardTotalTime = 703, // TOTAL accumulated time - - // Blue Marble / Mapleopoly - MapleopolyTotalSlotCount = 800, - MapleopolyFreeRollAmount = 801, - MapleopolyTotalTrips = 802, // unsure - - // Gallery Event - GalleryCardFlipCount = 1600, - GalleryClaimReward = 1601, - - // Rock Paper Scissors Event - RPSDailyMatches = 1800, - RPSRewardsClaimed = 1801, - - // Bingo - TODO: These are not the actual confirmed values. Just using it as a way to store this data for now. - BingoUid = 4000, - BingoRewardsClaimed = 4001, - BingoNumbersChecked = 4002, -} +// ReSharper disable InconsistentNaming + +namespace Maple2.Model.Enum; + +public enum GameEventUserValueType { + // Attendance Event + AttendanceActive = 100, //?? maybe. String is "True" + AttendanceCompletedTimestamp = 101, + AttendanceRewardsClaimed = 102, + AttendanceEarlyParticipationRemaining = 103, + AttendanceAccumulatedTime = 106, + + // DTReward + DTRewardStartTime = 700, // start time + DTRewardCurrentTime = 701, // current item accumulated time + DTRewardRewardIndex = 702, // unk value seen is "1" + DTRewardTotalTime = 703, // TOTAL accumulated time + + // Blue Marble / Mapleopoly + MapleopolyTotalSlotCount = 800, + MapleopolyFreeRollAmount = 801, + MapleopolyTotalTrips = 802, // unsure + + // Gallery Event + GalleryCardFlipCount = 1600, + GalleryClaimReward = 1601, + + // Rock Paper Scissors Event + RPSDailyMatches = 1800, + RPSRewardsClaimed = 1801, + + // Bingo - TODO: These are not the actual confirmed values. Just using it as a way to store this data for now. + BingoUid = 4000, + BingoRewardsClaimed = 4001, + BingoNumbersChecked = 4002, +} diff --git a/Maple2.Model/Enum/GameRankingType.cs b/Maple2.Model/Enum/GameRankingType.cs index c780fc12b..c3d79f761 100644 --- a/Maple2.Model/Enum/GameRankingType.cs +++ b/Maple2.Model/Enum/GameRankingType.cs @@ -1,37 +1,37 @@ -namespace Maple2.Model.Enum; - -public enum GameRankingType { - PersonalGuildTrophy = 12, - GuildTrophy = 14, - PersonalTrophy = 22, - Trophy = 24, - DarkDescentPersonal = 31, - DarkDescentPersonalPreviousSeason = 32, - DarkDescent = 33, - DarkDescentPreviousSeason = 34, - PvpPersonal = 41, - PvpPersonalPreviousSeason = 42, - Pvp = 43, - PvpPreviousSeason = 44, - UgcPersonal = 61, - UgcPersonalPreviousSeason = 62, - Ugc = 63, - UgcPreviousSeason = 64, - RaidPersonalClear = 71, - RaidClear = 72, - RaidEarlyVictory = 81, - RaidShortestTime = 91, - ArcadePersonal = 101, - ArcadePersonalPreviousSeason = 102, - Arcade = 103, - ArcadePreviousSeason = 104, - FortressRumblePersonalSRankClear = 141, - FortressRumbleSRankClear = 142, - FortressRumbleEarlyVictory = 151, - FortressRumblePersonalShortestTime = 161, - FortressRumbleShortestTime = 162, - ColosseumPersonal = 201, - ColosseumPersonalPreviousSeason = 202, - Colosseum = 203, - ColosseumPreviousSeason = 204, -} +namespace Maple2.Model.Enum; + +public enum GameRankingType { + PersonalGuildTrophy = 12, + GuildTrophy = 14, + PersonalTrophy = 22, + Trophy = 24, + DarkDescentPersonal = 31, + DarkDescentPersonalPreviousSeason = 32, + DarkDescent = 33, + DarkDescentPreviousSeason = 34, + PvpPersonal = 41, + PvpPersonalPreviousSeason = 42, + Pvp = 43, + PvpPreviousSeason = 44, + UgcPersonal = 61, + UgcPersonalPreviousSeason = 62, + Ugc = 63, + UgcPreviousSeason = 64, + RaidPersonalClear = 71, + RaidClear = 72, + RaidEarlyVictory = 81, + RaidShortestTime = 91, + ArcadePersonal = 101, + ArcadePersonalPreviousSeason = 102, + Arcade = 103, + ArcadePreviousSeason = 104, + FortressRumblePersonalSRankClear = 141, + FortressRumbleSRankClear = 142, + FortressRumbleEarlyVictory = 151, + FortressRumblePersonalShortestTime = 161, + FortressRumbleShortestTime = 162, + ColosseumPersonal = 201, + ColosseumPersonalPreviousSeason = 202, + Colosseum = 203, + ColosseumPreviousSeason = 204, +} diff --git a/Maple2.Model/Enum/Gender.cs b/Maple2.Model/Enum/Gender.cs index d81dff122..cf4a30296 100644 --- a/Maple2.Model/Enum/Gender.cs +++ b/Maple2.Model/Enum/Gender.cs @@ -1,7 +1,7 @@ -namespace Maple2.Model.Enum; - -public enum Gender : byte { - Male = 0, - Female = 1, - All = 2, -} +namespace Maple2.Model.Enum; + +public enum Gender : byte { + Male = 0, + Female = 1, + All = 2, +} diff --git a/Maple2.Model/Enum/GuildFocus.cs b/Maple2.Model/Enum/GuildFocus.cs index b743e3010..8bbc94b0a 100644 --- a/Maple2.Model/Enum/GuildFocus.cs +++ b/Maple2.Model/Enum/GuildFocus.cs @@ -1,24 +1,24 @@ -namespace Maple2.Model.Enum; - -[Flags] -public enum GuildFocus { - // Guild Focus - Social = 1, - HuntingParties = 2, - TrophyCollection = 4, - Dungeons = 8, - HomeDesign = 16, - Pvp = 32, - WorkshopTemplates = 64, - GuildArcade = 128, - // Active Times - Weekdays = 256, - Mornings = 512, - Weekends = 1024, - Evenings = 2048, - // Member Ages - Teens = 4096, - Thirties = 8192, - Twenties = 16384, - Other = 32768, -} +namespace Maple2.Model.Enum; + +[Flags] +public enum GuildFocus { + // Guild Focus + Social = 1, + HuntingParties = 2, + TrophyCollection = 4, + Dungeons = 8, + HomeDesign = 16, + Pvp = 32, + WorkshopTemplates = 64, + GuildArcade = 128, + // Active Times + Weekdays = 256, + Mornings = 512, + Weekends = 1024, + Evenings = 2048, + // Member Ages + Teens = 4096, + Thirties = 8192, + Twenties = 16384, + Other = 32768, +} diff --git a/Maple2.Model/Enum/GuildNpcType.cs b/Maple2.Model/Enum/GuildNpcType.cs index 7877c658d..5f249e855 100644 --- a/Maple2.Model/Enum/GuildNpcType.cs +++ b/Maple2.Model/Enum/GuildNpcType.cs @@ -1,20 +1,20 @@ -using System.ComponentModel; - -namespace Maple2.Model.Enum; - -public enum GuildNpcType { - [Description("unknown")] - Unknown = 0, - [Description("equip")] - Equip = 1, - [Description("goods")] - Goods = 2, - [Description("gemstone")] - Gemstone = 3, - [Description("itemMerge")] - ItemMerge = 4, - [Description("music")] - Music = 5, - [Description("quest")] - Quest = 6, -} +using System.ComponentModel; + +namespace Maple2.Model.Enum; + +public enum GuildNpcType { + [Description("unknown")] + Unknown = 0, + [Description("equip")] + Equip = 1, + [Description("goods")] + Goods = 2, + [Description("gemstone")] + Gemstone = 3, + [Description("itemMerge")] + ItemMerge = 4, + [Description("music")] + Music = 5, + [Description("quest")] + Quest = 6, +} diff --git a/Maple2.Model/Enum/GuildPermission.cs b/Maple2.Model/Enum/GuildPermission.cs index 066003124..e574e9c13 100644 --- a/Maple2.Model/Enum/GuildPermission.cs +++ b/Maple2.Model/Enum/GuildPermission.cs @@ -1,19 +1,19 @@ -namespace Maple2.Model.Enum; - -[Flags] -public enum GuildPermission { - Default = 1, - InviteMembers = 2, - ExpelMembers = 4, - EditNotice = 8, - Unknown = 16, // Don't know how this is used, but leader should have it. - EditRank = 32, - EditEmblem = 64, - SendMail = 128, - StartPvp = 256, - UseBuff = 512, - StartMiniGame = 1024, - SendAlert = 2048, - - All = Default | InviteMembers | ExpelMembers | EditNotice | Unknown | EditRank | EditEmblem | SendMail | StartPvp | UseBuff | StartMiniGame | SendAlert, -} +namespace Maple2.Model.Enum; + +[Flags] +public enum GuildPermission { + Default = 1, + InviteMembers = 2, + ExpelMembers = 4, + EditNotice = 8, + Unknown = 16, // Don't know how this is used, but leader should have it. + EditRank = 32, + EditEmblem = 64, + SendMail = 128, + StartPvp = 256, + UseBuff = 512, + StartMiniGame = 1024, + SendAlert = 2048, + + All = Default | InviteMembers | ExpelMembers | EditNotice | Unknown | EditRank | EditEmblem | SendMail | StartPvp | UseBuff | StartMiniGame | SendAlert, +} diff --git a/Maple2.Model/Enum/HomePermission.cs b/Maple2.Model/Enum/HomePermission.cs index 4444e83ce..a6691c64f 100644 --- a/Maple2.Model/Enum/HomePermission.cs +++ b/Maple2.Model/Enum/HomePermission.cs @@ -1,23 +1,23 @@ -namespace Maple2.Model.Enum; - -// Setting Values -// 0 = None / PvpEnabled -// 1 = Self -// 2 = Party -public enum HomePermission : byte { - Jump = 0, - Climb = 1, - Skill = 2, - Music = 3, - Potion = 4, - GroundMount = 5, - AirMount = 6, - Pvp = 7, - Unknown = 8, -} - -public enum HomePermissionSetting : byte { - None = 0, - Self = 1, - Party = 2, -} +namespace Maple2.Model.Enum; + +// Setting Values +// 0 = None / PvpEnabled +// 1 = Self +// 2 = Party +public enum HomePermission : byte { + Jump = 0, + Climb = 1, + Skill = 2, + Music = 3, + Potion = 4, + GroundMount = 5, + AirMount = 6, + Pvp = 7, + Unknown = 8, +} + +public enum HomePermissionSetting : byte { + None = 0, + Self = 1, + Party = 2, +} diff --git a/Maple2.Model/Enum/HomeSetting.cs b/Maple2.Model/Enum/HomeSetting.cs index 0ac6b4d57..061d25635 100644 --- a/Maple2.Model/Enum/HomeSetting.cs +++ b/Maple2.Model/Enum/HomeSetting.cs @@ -1,44 +1,44 @@ -namespace Maple2.Model.Enum; - -public enum HomeBackground : byte { - Basic = 0, - GreenMeadow = 1, - MoonlitForest = 2, - RainbowSnowfield = 3, - Skyscraper = 4, - BlueSea = 5, - BarrenMountains = 6, - Space = 7, - Ludibrium = 8, - FutureCity = 9, - TwilightDesert = 10, - LavaCave = 11, - SweetVista = 12, - VastSky = 13, -} - -public enum HomeLighting : byte { - Basic = 0, - Natural = 1, - Warm = 2, - Cool = 3, - Dark = 4, - Soft = 5, - Dusk = 6, - Dawn = 7, - Winter = 8, -} - -public enum HomeCamera : byte { - QuarterView = 0, - SideView = 1, - TopView = 2, - AreaView = 3, -} - -public enum PlotMode : byte { - Normal = 0, - DecorPlanner = 1, - ModelHome = 2, - BlueprintPlanner = 3, -} +namespace Maple2.Model.Enum; + +public enum HomeBackground : byte { + Basic = 0, + GreenMeadow = 1, + MoonlitForest = 2, + RainbowSnowfield = 3, + Skyscraper = 4, + BlueSea = 5, + BarrenMountains = 6, + Space = 7, + Ludibrium = 8, + FutureCity = 9, + TwilightDesert = 10, + LavaCave = 11, + SweetVista = 12, + VastSky = 13, +} + +public enum HomeLighting : byte { + Basic = 0, + Natural = 1, + Warm = 2, + Cool = 3, + Dark = 4, + Soft = 5, + Dusk = 6, + Dawn = 7, + Winter = 8, +} + +public enum HomeCamera : byte { + QuarterView = 0, + SideView = 1, + TopView = 2, + AreaView = 3, +} + +public enum PlotMode : byte { + Normal = 0, + DecorPlanner = 1, + ModelHome = 2, + BlueprintPlanner = 3, +} diff --git a/Maple2.Model/Enum/HousingCategory.cs b/Maple2.Model/Enum/HousingCategory.cs index 496b21843..a7fdf7baa 100644 --- a/Maple2.Model/Enum/HousingCategory.cs +++ b/Maple2.Model/Enum/HousingCategory.cs @@ -1,36 +1,36 @@ -namespace Maple2.Model.Enum; - -public enum HousingCategory { - None = 0, - Bed = 1, - Table = 2, - SofasChairs = 3, - Storage = 4, - WallDecoration = 5, - WallTiles = 6, - Bathroom = 7, - Lighting = 8, - Electronics = 9, - Fences = 10, - NaturalTerrain = 11, - Garden = 12, - SpecialBlocks = 13, - Stairs = 14, - Doors = 15, - CommonTerrain = 16, - Vegetation = 17, - InteriorDecor = 18, - ThemedDecor = 19, - Structures = 20, - Traps = 21, - Maid = 91, - Souvenirs = 92, - UgcBlock = 93, - Event = 94, - UgcBed = 95, - UgcTable = 96, - UgcStairs = 97, - Ranching = 204, - Farming = 205, - Misc = 10000, -} +namespace Maple2.Model.Enum; + +public enum HousingCategory { + None = 0, + Bed = 1, + Table = 2, + SofasChairs = 3, + Storage = 4, + WallDecoration = 5, + WallTiles = 6, + Bathroom = 7, + Lighting = 8, + Electronics = 9, + Fences = 10, + NaturalTerrain = 11, + Garden = 12, + SpecialBlocks = 13, + Stairs = 14, + Doors = 15, + CommonTerrain = 16, + Vegetation = 17, + InteriorDecor = 18, + ThemedDecor = 19, + Structures = 20, + Traps = 21, + Maid = 91, + Souvenirs = 92, + UgcBlock = 93, + Event = 94, + UgcBed = 95, + UgcTable = 96, + UgcStairs = 97, + Ranching = 204, + Farming = 205, + Misc = 10000, +} diff --git a/Maple2.Model/Enum/IndividualItemDropCategory.cs b/Maple2.Model/Enum/IndividualItemDropCategory.cs index 1dc32150d..1baee7113 100644 --- a/Maple2.Model/Enum/IndividualItemDropCategory.cs +++ b/Maple2.Model/Enum/IndividualItemDropCategory.cs @@ -1,16 +1,16 @@ -namespace Maple2.Model.Enum; - -public enum IndividualItemDropCategory { - Default, - Gacha, - Gearbox, - Monster, - Cash, - Event, - EventNpc, - NewGacha, - Pet, - Quest, - QuestMonster, - QuestObject, -} +namespace Maple2.Model.Enum; + +public enum IndividualItemDropCategory { + Default, + Gacha, + Gearbox, + Monster, + Cash, + Event, + EventNpc, + NewGacha, + Pet, + Quest, + QuestMonster, + QuestObject, +} diff --git a/Maple2.Model/Enum/InsigniaConditionType.cs b/Maple2.Model/Enum/InsigniaConditionType.cs index 30548e762..30c1ac680 100644 --- a/Maple2.Model/Enum/InsigniaConditionType.cs +++ b/Maple2.Model/Enum/InsigniaConditionType.cs @@ -1,18 +1,18 @@ -// ReSharper disable InconsistentNaming - -namespace Maple2.Model.Enum; - -public enum InsigniaConditionType { - none, - level, - enchant, - title, - trophy_point, - tencentvip, - vip, - GM, - TGP, - adventure_level, - Burning, - survivallevel, -} +// ReSharper disable InconsistentNaming + +namespace Maple2.Model.Enum; + +public enum InsigniaConditionType { + none, + level, + enchant, + title, + trophy_point, + tencentvip, + vip, + GM, + TGP, + adventure_level, + Burning, + survivallevel, +} diff --git a/Maple2.Model/Enum/InstanceType.cs b/Maple2.Model/Enum/InstanceType.cs index 5e65a2919..cc20fcd91 100644 --- a/Maple2.Model/Enum/InstanceType.cs +++ b/Maple2.Model/Enum/InstanceType.cs @@ -1,20 +1,20 @@ -namespace Maple2.Model.Enum; - -public enum InstanceType : byte { - none = 0, - solo = 1, - channelScale = 2, - massiveEvent = 3, - ugcMap = 4, - GameMaker = 5, //UGD - GuildEvent = 6, - GuildPvp = 7, // Not confirmed - DungeonLobby = 8, - GuildHouse = 9, - GuildVsGame = 10, // Not confirmed - RankingPvp = 11, // Not confirmed - MapleSurvival = 12, // Not confirmed - MapleSurvivalSquad = 13, // Not confirmed - FieldWar = 14, - WeddingHall = 15, -} +namespace Maple2.Model.Enum; + +public enum InstanceType : byte { + none = 0, + solo = 1, + channelScale = 2, + massiveEvent = 3, + ugcMap = 4, + GameMaker = 5, //UGD + GuildEvent = 6, + GuildPvp = 7, // Not confirmed + DungeonLobby = 8, + GuildHouse = 9, + GuildVsGame = 10, // Not confirmed + RankingPvp = 11, // Not confirmed + MapleSurvival = 12, // Not confirmed + MapleSurvivalSquad = 13, // Not confirmed + FieldWar = 14, + WeddingHall = 15, +} diff --git a/Maple2.Model/Enum/Instrument.cs b/Maple2.Model/Enum/Instrument.cs index 54d99bd9a..c1a150e30 100644 --- a/Maple2.Model/Enum/Instrument.cs +++ b/Maple2.Model/Enum/Instrument.cs @@ -1,44 +1,44 @@ -namespace Maple2.Model.Enum; - -public enum Instrument { - Piano = 1, - Misc = 2, - Clarinet = 3, - Harp = 4, - Timapni = 5, - ElectricGuitar = 6, - Bass = 7, - Tomtam = 8, - Violin = 9, - Cello = 10, - PanFlute = 11, - Saxophone = 12, - Trombone = 13, - Trumpet = 14, - Ocarina = 15, - AcousticBass = 16, - Vibraphone = 17, - ElectricPiano = 18, - SteelDrum = 19, - PickBassGuitar = 20, - Oboe = 21, - Pizzicato = 22, - Harpsichord = 23, - Harmonica = 24, - Xylophone = 25, - Recorder = 26, - Celesta = 27, - Cymbal = 28, - BassDrum = 29, - SnareDrum = 30, - Shamisen = 31, - Koto = 32, - Shakuhachi = 33, - TaikoDrum = 34, - FretlessBass = 35, - Marimba = 36, - Flute = 37, - HonkyTonkPiano = 38, - FrenchHorn = 39, - PipeOrgan = 40, -} +namespace Maple2.Model.Enum; + +public enum Instrument { + Piano = 1, + Misc = 2, + Clarinet = 3, + Harp = 4, + Timapni = 5, + ElectricGuitar = 6, + Bass = 7, + Tomtam = 8, + Violin = 9, + Cello = 10, + PanFlute = 11, + Saxophone = 12, + Trombone = 13, + Trumpet = 14, + Ocarina = 15, + AcousticBass = 16, + Vibraphone = 17, + ElectricPiano = 18, + SteelDrum = 19, + PickBassGuitar = 20, + Oboe = 21, + Pizzicato = 22, + Harpsichord = 23, + Harmonica = 24, + Xylophone = 25, + Recorder = 26, + Celesta = 27, + Cymbal = 28, + BassDrum = 29, + SnareDrum = 30, + Shamisen = 31, + Koto = 32, + Shakuhachi = 33, + TaikoDrum = 34, + FretlessBass = 35, + Marimba = 36, + Flute = 37, + HonkyTonkPiano = 38, + FrenchHorn = 39, + PipeOrgan = 40, +} diff --git a/Maple2.Model/Enum/Interact.cs b/Maple2.Model/Enum/Interact.cs index 73e3dc361..256fba750 100644 --- a/Maple2.Model/Enum/Interact.cs +++ b/Maple2.Model/Enum/Interact.cs @@ -1,73 +1,73 @@ -using System.ComponentModel; - -namespace Maple2.Model.Enum; - -public enum InteractType : byte { - Mesh = 1, - Telescope = 2, - Ui = 3, - Web = 4, - DisplayImage = 5, - Gathering = 6, - GuildPoster = 7, - BillBoard = 8, // AdBalloon - WatchTower = 9, -} - -public enum InteractState : byte { - Normal = 0, - Reactable = 1, - Hidden = 2, -} - -// ReSharper disable InconsistentNaming, IdentifierTypo -public enum InteractResult : byte { - none = 0, - [Description("You get a good look at the area.")] - s_interact_find_new_telescope = 0, - [Description("System Error: interact. {0}")] - s_interact_result_unknown = 1, - [Description("You are not in the middle of a quest.")] - s_interact_result_quest = 2, - [Description("Only the party leader has the power to do that.")] - s_interact_result_party = 3, - [Description("That cannot be done on this map.")] - s_tutorial_dialog_limit = 4, - [Description("You don't have permission to do that.")] - s_interact_result_privilege = 5, - [Description("You don't have permission to do that.")] - s_interact_result_auth = 7, - // 12 - [Description("Requires rank {1} {0}.")] // {0} Life Skill Type, {1} Life Skill Rank - s_interact_result_mastery = 13, -} - -public enum GatherResult : short { - Success = 0, - Fail = 1, -} - -public enum InteractCubeState { - None = 0, - InUse = 1, - Available = 2, -} - -public enum InteractCubeControlType { - None, - Farming, - Breeding, - Switch, - Skill, - Nurturing, - Ride, - SpawnNPC, - OpenWeb, - Sensor, - FunctionUI, - Notice, - InstallNPC, - Portal, - SpawnPoint, - PVP, -} +using System.ComponentModel; + +namespace Maple2.Model.Enum; + +public enum InteractType : byte { + Mesh = 1, + Telescope = 2, + Ui = 3, + Web = 4, + DisplayImage = 5, + Gathering = 6, + GuildPoster = 7, + BillBoard = 8, // AdBalloon + WatchTower = 9, +} + +public enum InteractState : byte { + Normal = 0, + Reactable = 1, + Hidden = 2, +} + +// ReSharper disable InconsistentNaming, IdentifierTypo +public enum InteractResult : byte { + none = 0, + [Description("You get a good look at the area.")] + s_interact_find_new_telescope = 0, + [Description("System Error: interact. {0}")] + s_interact_result_unknown = 1, + [Description("You are not in the middle of a quest.")] + s_interact_result_quest = 2, + [Description("Only the party leader has the power to do that.")] + s_interact_result_party = 3, + [Description("That cannot be done on this map.")] + s_tutorial_dialog_limit = 4, + [Description("You don't have permission to do that.")] + s_interact_result_privilege = 5, + [Description("You don't have permission to do that.")] + s_interact_result_auth = 7, + // 12 + [Description("Requires rank {1} {0}.")] // {0} Life Skill Type, {1} Life Skill Rank + s_interact_result_mastery = 13, +} + +public enum GatherResult : short { + Success = 0, + Fail = 1, +} + +public enum InteractCubeState { + None = 0, + InUse = 1, + Available = 2, +} + +public enum InteractCubeControlType { + None, + Farming, + Breeding, + Switch, + Skill, + Nurturing, + Ride, + SpawnNPC, + OpenWeb, + Sensor, + FunctionUI, + Notice, + InstallNPC, + Portal, + SpawnPoint, + PVP, +} diff --git a/Maple2.Model/Enum/InventoryType.cs b/Maple2.Model/Enum/InventoryType.cs index e8bc72624..753fc5386 100644 --- a/Maple2.Model/Enum/InventoryType.cs +++ b/Maple2.Model/Enum/InventoryType.cs @@ -1,22 +1,22 @@ -namespace Maple2.Model.Enum; - -public enum InventoryType : byte { - // Inventory - Gear = 0, - Outfit = 1, - Mount = 2, - Catalyst = 3, - FishingMusic = 4, - Quest = 5, - Gemstone = 6, - Misc = 7, - // PetEquip = 8, - LifeSkill = 9, - Pets = 10, - Consumable = 11, - Currency = 12, - Badge = 13, - //Mushtopia = 14, - Lapenshard = 15, - Fragment = 16, -} +namespace Maple2.Model.Enum; + +public enum InventoryType : byte { + // Inventory + Gear = 0, + Outfit = 1, + Mount = 2, + Catalyst = 3, + FishingMusic = 4, + Quest = 5, + Gemstone = 6, + Misc = 7, + // PetEquip = 8, + LifeSkill = 9, + Pets = 10, + Consumable = 11, + Currency = 12, + Badge = 13, + //Mushtopia = 14, + Lapenshard = 15, + Fragment = 16, +} diff --git a/Maple2.Model/Enum/ItemFunction.cs b/Maple2.Model/Enum/ItemFunction.cs index 39a756ef7..55891c4a7 100644 --- a/Maple2.Model/Enum/ItemFunction.cs +++ b/Maple2.Model/Enum/ItemFunction.cs @@ -1,72 +1,72 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -namespace Maple2.Model.Enum; - -public enum ItemFunction { - None = 0, - AddAdditionalEffect = 1, - AddPetEXP = 2, - BlueprintImport = 3, - CallAirTaxi = 4, - CallBlackMarket = 5, - CallMedic = 6, - CashAttendEvent = 7, - ChangeCharName = 8, - ChangeGender = 9, - ChangeGuildName = 10, - ChatEmoticonAdd = 11, - Client = 12, - CreateReactor = 13, - DefenseGuard = 14, - EnchantScroll = 15, - EnchantSuperProtect = 16, - ExpandInven = 17, - ExpendCharacterSlot = 18, - FestivalEvent = 19, - FishingRod = 20, - HongBao = 21, - InstallBillBoard = 22, - ItemChangeBeauty = 23, - ItemExchangeScroll = 24, - ItemExtraction = 25, - ItemMergeRevert = 26, - ItemRePackingScroll = 27, - ItemRemakeScroll = 28, - ItemSocketScroll = 29, - LevelPotion = 30, - LockItemOptionPet = 31, - MultiRiding = 32, - OpenBank = 33, - OpenCoupleEffectBox = 34, - OpenDungeonItemBox = 35, - OpenGachaBox = 36, - OpenInstrument = 37, - OpenItemBox = 38, - OpenItemBoxLullu = 39, - OpenItemBoxLulluSimple = 40, - OpenItemBoxWithKey = 41, - OpenMassive = 42, - OpenPetNutrient = 43, - OpenUGCEvent = 44, - OpenWebPage = 45, - PetExtraction = 46, - PetTraining = 47, - QuestScroll = 48, - RecallGuild = 49, - RecallParty = 50, - ResetAbilityCoolTime = 51, - SelectItemBox = 52, - StoryBook = 53, - SuperWorldChat = 54, - SurvivalLevelExp = 55, - SurvivalScan = 56, - SurvivalSkin = 57, - TitleScroll = 58, - TreasureMap = 59, - UGCMapPackage = 60, - VIPCoupon = 61, - WeddingExpItem = 62, - RecallWedding = 63, - WeddingChat = 64, - PetStudy = 65, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +namespace Maple2.Model.Enum; + +public enum ItemFunction { + None = 0, + AddAdditionalEffect = 1, + AddPetEXP = 2, + BlueprintImport = 3, + CallAirTaxi = 4, + CallBlackMarket = 5, + CallMedic = 6, + CashAttendEvent = 7, + ChangeCharName = 8, + ChangeGender = 9, + ChangeGuildName = 10, + ChatEmoticonAdd = 11, + Client = 12, + CreateReactor = 13, + DefenseGuard = 14, + EnchantScroll = 15, + EnchantSuperProtect = 16, + ExpandInven = 17, + ExpendCharacterSlot = 18, + FestivalEvent = 19, + FishingRod = 20, + HongBao = 21, + InstallBillBoard = 22, + ItemChangeBeauty = 23, + ItemExchangeScroll = 24, + ItemExtraction = 25, + ItemMergeRevert = 26, + ItemRePackingScroll = 27, + ItemRemakeScroll = 28, + ItemSocketScroll = 29, + LevelPotion = 30, + LockItemOptionPet = 31, + MultiRiding = 32, + OpenBank = 33, + OpenCoupleEffectBox = 34, + OpenDungeonItemBox = 35, + OpenGachaBox = 36, + OpenInstrument = 37, + OpenItemBox = 38, + OpenItemBoxLullu = 39, + OpenItemBoxLulluSimple = 40, + OpenItemBoxWithKey = 41, + OpenMassive = 42, + OpenPetNutrient = 43, + OpenUGCEvent = 44, + OpenWebPage = 45, + PetExtraction = 46, + PetTraining = 47, + QuestScroll = 48, + RecallGuild = 49, + RecallParty = 50, + ResetAbilityCoolTime = 51, + SelectItemBox = 52, + StoryBook = 53, + SuperWorldChat = 54, + SurvivalLevelExp = 55, + SurvivalScan = 56, + SurvivalSkin = 57, + TitleScroll = 58, + TreasureMap = 59, + UGCMapPackage = 60, + VIPCoupon = 61, + WeddingExpItem = 62, + RecallWedding = 63, + WeddingChat = 64, + PetStudy = 65, +} diff --git a/Maple2.Model/Enum/ItemGroup.cs b/Maple2.Model/Enum/ItemGroup.cs index b5b04dcfa..b98b4fd28 100644 --- a/Maple2.Model/Enum/ItemGroup.cs +++ b/Maple2.Model/Enum/ItemGroup.cs @@ -1,20 +1,20 @@ -namespace Maple2.Model.Enum; - -public enum ItemGroup : byte { - // CharacterId=>Inventory, AccountId=>Storage - Default = 0, - - // CharacterId Specific - Gear = 1, - Outfit = 2, - Outfit2 = 3, - Badge = 4, - Medal = 5, - - SavedHair = 8, - - // AccountId specific - Furnishing = 10, - Home = 11, - Plot = 12, -} +namespace Maple2.Model.Enum; + +public enum ItemGroup : byte { + // CharacterId=>Inventory, AccountId=>Storage + Default = 0, + + // CharacterId Specific + Gear = 1, + Outfit = 2, + Outfit2 = 3, + Badge = 4, + Medal = 5, + + SavedHair = 8, + + // AccountId specific + Furnishing = 10, + Home = 11, + Plot = 12, +} diff --git a/Maple2.Model/Enum/ItemOptionMakeType.cs b/Maple2.Model/Enum/ItemOptionMakeType.cs index 6274e2c0d..c7e9d7e62 100644 --- a/Maple2.Model/Enum/ItemOptionMakeType.cs +++ b/Maple2.Model/Enum/ItemOptionMakeType.cs @@ -1,7 +1,7 @@ -namespace Maple2.Model.Enum; - -public enum ItemOptionMakeType { - Base = 0, // uses itemoptionvariation table - Range = 1, // uses itemoptionvariation_* tables - Lua = 2, // uses lua functions -} +namespace Maple2.Model.Enum; + +public enum ItemOptionMakeType { + Base = 0, // uses itemoptionvariation table + Range = 1, // uses itemoptionvariation_* tables + Lua = 2, // uses lua functions +} diff --git a/Maple2.Model/Enum/ItemTag.cs b/Maple2.Model/Enum/ItemTag.cs index 39a4e6afa..8e93079dc 100644 --- a/Maple2.Model/Enum/ItemTag.cs +++ b/Maple2.Model/Enum/ItemTag.cs @@ -1,449 +1,449 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Enum; - -// Please leave these names as-is to match item tags found in xml. -public enum ItemTag { - None = 0, - [Description("Channel Chat Voucher")] - FreeChannelChatCoupon = 1, - [Description("World Chat Voucher")] - FreeWorldChatCoupon = 2, - [Description("")] - FreeSuperChatCoupon = 3, - [Description("Revive Voucher")] - FreeReviveCoupon = 4, - [Description("Template Voucher")] - FreeDesignCoupon = 5, - [Description("Pet Name Change Voucher")] - FreePetNameChangeCoupon = 6, - [Description("Weekly Dungeon Ticket, Bonus Dungeon Reward Voucher, Dungeon Ticket")] - DungeonRewardTicketA = 7, - [Description("Treasure Dungeon Ticket")] - DungeonRewardTicketB = 8, - [Description("Id=20302765")] - DungeonRewardTicketC = 9, - [Description("Bonus Dungeon Reward Voucher")] - DungeonRewardTypeA = 10, - [Description("Fantastic Bonus Dungeon Reward Voucher")] - DungeonRewardTypeB = 11, - [Description("Superior Bonus Dungeon Reward Voucher")] - DungeonRewardTypeC = 12, - [Description("")] - DungeonRewardTypeD = 13, - [Description("Free Rotors Walkie-talkie")] - air_taxi = 14, - [Description("Rotors Walkie-talkie")] - air_taxi_advanced = 15, - [Description("Face Change Voucher")] - beauty_face = 16, - [Description("Hairstyle Voucher")] - beauty_hair = 17, - [Description("Gear Dye Voucher")] - beauty_itemcolor = 18, - [Description("Skin Tone Change Voucher")] - beauty_skin = 19, - [Description("Cosmetics Voucher")] - beauty_makeup = 20, - [Description("Special Hairstyle Voucher")] - beauty_hair_special = 21, - [Description("")] - beauty_hair_special_bonus = 22, - [Description("Red Bella Figurine Package, Black Bella Figurine Package, Yellow Mika Figurine Package, Blue Mika Figurine Package")] - Cashshop_Figure = 23, - [Description("Party Summon Scroll")] - party_call = 24, - [Description("Free Expedition Specialty Reset")] - ShadowPointReset = 25, - [Description("Lulu's Key")] - LulluKey = 26, - [Description("Mystery Box")] - LulluBox = 27, - [Description("")] - LulluKey01 = 28, - [Description("")] - LulluBox01 = 29, - [Description("")] - LulluKey02 = 30, - [Description("")] - LulluBox02 = 31, - [Description("Luxurious Style Key")] - LulluKey03 = 32, - [Description("Holiday Mystery Box")] - LulluBox03 = 33, - [Description("Golden Jewel Key")] - LulluKey04 = 34, - [Description("Mystery Style Box")] - LulluBox04 = 35, - [Description("")] - LulluKey05 = 36, - [Description("Mystery Jewel Box")] - LulluBox05 = 37, - [Description("")] - LulluKey06 = 38, - [Description("")] - LulluBox06 = 39, - [Description("")] - LulluKey07 = 40, - [Description("")] - LulluBox07 = 41, - [Description("")] - LulluKey08 = 42, - [Description("Style Crate")] - LulluBox08 = 43, - [Description("")] - LulluKey09 = 44, - [Description("")] - LulluBox09 = 45, - [Description("")] - LulluKey10 = 46, - [Description("")] - LulluBox10 = 47, - [Description("")] - LulluKey11 = 48, - [Description("")] - LulluBox11 = 49, - [Description("")] - LulluKey12 = 50, - [Description("")] - LulluBox12 = 51, - [Description("")] - LulluKey13 = 52, - [Description("")] - LulluBox13 = 53, - [Description("")] - LulluKey14 = 54, - [Description("")] - LulluBox14 = 55, - [Description("")] - LulluKey15 = 56, - [Description("")] - LulluBox15 = 57, - [Description("")] - LulluKey16 = 58, - [Description("")] - LulluBox16 = 59, - [Description("")] - LulluKey17 = 60, - [Description("")] - LulluBox17 = 61, - [Description("")] - LulluKey18 = 62, - [Description("")] - LulluBox18 = 63, - [Description("")] - LulluKey19 = 64, - [Description("")] - LulluBox19 = 65, - [Description("")] - LulluKey20 = 66, - [Description("")] - LulluBox20 = 67, - [Description("")] - LulluKey21 = 68, - [Description("")] - LulluBox21 = 69, - [Description("")] - LulluKey22 = 70, - [Description("")] - LulluBox22 = 71, - [Description("")] - LulluKey23 = 72, - [Description("")] - LulluBox23 = 73, - [Description("")] - LulluKey24 = 74, - [Description("")] - LulluBox24 = 75, - [Description("")] - LulluKey25 = 76, - [Description("")] - LulluBox25 = 77, - [Description("")] - LulluKey26 = 78, - [Description("")] - LulluBox26 = 79, - [Description("")] - LulluKey27 = 80, - [Description("")] - LulluBox27 = 81, - [Description("")] - LulluKey28 = 82, - [Description("")] - LulluBox28 = 83, - [Description("")] - LulluKey29 = 84, - [Description("")] - LulluBox29 = 85, - [Description("")] - LulluKey30 = 86, - [Description("")] - LulluBox30 = 87, - [Description("")] - PetBox = 88, - [Description("")] - DungeonLimitItem = 89, - [Description("")] - SkinGemDustA = 90, - [Description("")] - SkinGemDustB = 91, - [Description("Special Outfit Crystal")] - SkinCrystal = 92, - [Description("")] - SkinGemMaterial = 93, - [Description("")] - CharmStone = 94, - [Description("")] - ProtectStone = 95, - [Description("Metacell")] - MetaCell = 96, - [Description("Red Crystal")] - RedCrystal = 97, - [Description("Green Crystal")] - GreenCrystal = 98, - [Description("Blue Crystal")] - BlueCrystal = 99, - [Description("Crystal Fragment")] - CrystalPiece = 100, - [Description("Onyx Crystal")] - Onix = 101, - [Description("Chaos Onyx Crystal")] - ChaosOnix = 102, - [Description("")] - CrystalGemstone = 103, - [Description("")] - FishingLure = 104, - [Description("Weapon Attribute Lock Scroll")] - LockItemOptionWeapon = 105, - [Description("Armor Attribute Lock Scroll")] - LockItemOptionArmor = 106, - [Description("Accessory Attribute Lock Scroll")] - LockItemOptionAccessory = 107, - [Description("")] - LockItemOptionPet = 108, - [Description("OX Quiz Host Ticket")] - UGCEventOxOpen = 109, - [Description("Glamour Anvil")] - ItemExtraction = 110, - [Description("Pet Skin Crafting Scroll")] - PetExtraction = 111, - [Description("Dungeon Reward Booster")] - ExpenseRewardTicket = 112, - [Description("Instant Gathering Voucher")] - AutoMastery = 113, - [Description("Blue Gem Dust")] - DustBlue = 114, - [Description("Purple Gem Dust")] - DustPurple = 115, - [Description("Orange Gem Dust")] - DustOrange = 116, - [Description("White Gem Dust")] - DustWhite = 117, - [Description("Red Gem Dust")] - DustRed = 118, - [Description("Green Gem Dust")] - DustGreen = 119, - [Description("Cyan Gem Dust")] - DustCyan = 120, - [Description("Yellow Gem Dust")] - DustYellow = 121, - [Description("Skill Tab Voucher")] - SkillBookTreeAddTabCoupon = 122, - [Description("")] - EpicCharmStone = 123, - [Description("")] - EpicProtectStone = 124, - [Description("Rainbow Feed, Pumpkin Pie Feed, Ghost Fish")] - SlimeFood = 125, - [Description("* Fireworks")] - Firework = 126, - [Description("Tier 1 Wisdom Gemstone")] - GemstoneA01 = 127, - [Description("Tier 2 Wisdom Gemstone")] - GemstoneA02 = 128, - [Description("Tier 3 Wisdom Gemstone")] - GemstoneA03 = 129, - [Description("Tier 4 Wisdom Gemstone")] - GemstoneA04 = 130, - [Description("Tier 5 Wisdom Gemstone")] - GemstoneA05 = 131, - [Description("Tier 6 Wisdom Gemstone")] - GemstoneA06 = 132, - [Description("Tier 7 Wisdom Gemstone")] - GemstoneA07 = 133, - [Description("Tier 8 Wisdom Gemstone")] - GemstoneA08 = 134, - [Description("Tier 9 Wisdom Gemstone")] - GemstoneA09 = 135, - [Description("Tier 10 Wisdom Gemstone")] - GemstoneA10 = 136, - [Description("Tier 1 Luck Gemstone")] - GemstoneB01 = 137, - [Description("Tier 2 Luck Gemstone")] - GemstoneB02 = 138, - [Description("Tier 3 Luck Gemstone")] - GemstoneB03 = 139, - [Description("Tier 4 Luck Gemstone")] - GemstoneB04 = 140, - [Description("Tier 5 Luck Gemstone")] - GemstoneB05 = 141, - [Description("Tier 6 Luck Gemstone")] - GemstoneB06 = 142, - [Description("Tier 7 Luck Gemstone")] - GemstoneB07 = 143, - [Description("Tier 8 Luck Gemstone")] - GemstoneB08 = 144, - [Description("Tier 9 Luck Gemstone")] - GemstoneB09 = 145, - [Description("Tier 10 Luck Gemstone")] - GemstoneB10 = 146, - [Description("Tier 1 Destruction Gemstone")] - GemstoneC01 = 147, - [Description("Tier 2 Destruction Gemstone ")] - GemstoneC02 = 148, - [Description("Tier 3 Destruction Gemstone")] - GemstoneC03 = 149, - [Description("Tier 4 Destruction Gemstone")] - GemstoneC04 = 150, - [Description("Tier 5 Destruction Gemstone")] - GemstoneC05 = 151, - [Description("Tier 6 Destruction Gemstone")] - GemstoneC06 = 152, - [Description("Tier 7 Destruction Gemstone")] - GemstoneC07 = 153, - [Description("Tier 8 Destruction Gemstone")] - GemstoneC08 = 154, - [Description("Tier 9 Destruction Gemstone")] - GemstoneC09 = 155, - [Description("Tier 10 Destruction Gemstone")] - GemstoneC10 = 156, - [Description("Tier 1 Life Gemstone")] - GemstoneD01 = 157, - [Description("Tier 2 Life Gemstone ")] - GemstoneD02 = 158, - [Description("Tier 3 Life Gemstone")] - GemstoneD03 = 159, - [Description("Tier 4 Life Gemstone")] - GemstoneD04 = 160, - [Description("Tier 5 Life Gemstone")] - GemstoneD05 = 161, - [Description("Tier 6 Life Gemstone")] - GemstoneD06 = 162, - [Description("Tier 7 Life Gemstone")] - GemstoneD07 = 163, - [Description("Tier 8 Life Gemstone")] - GemstoneD08 = 164, - [Description("Tier 9 Life Gemstone")] - GemstoneD09 = 165, - [Description("Tier 10 Life Gemstone")] - GemstoneD10 = 166, - [Description("Tier 1 Power Gemstone")] - GemstoneE01 = 167, - [Description("Tier 2 Power Gemstone")] - GemstoneE02 = 168, - [Description("Tier 3 Power Gemstone")] - GemstoneE03 = 169, - [Description("Tier 4 Power Gemstone")] - GemstoneE04 = 170, - [Description("Tier 5 Power Gemstone")] - GemstoneE05 = 171, - [Description("Tier 6 Power Gemstone")] - GemstoneE06 = 172, - [Description("Tier 7 Power Gemstone")] - GemstoneE07 = 173, - [Description("Tier 8 Power Gemstone")] - GemstoneE08 = 174, - [Description("Tier 9 Power Gemstone")] - GemstoneE09 = 175, - [Description("Tier 10 Power Gemstone")] - GemstoneE10 = 176, - [Description("Tier 1 Dex Gemstone")] - GemstoneF01 = 177, - [Description("Tier 2 Dex Gemstone")] - GemstoneF02 = 178, - [Description("Tier 3 Dex Gemstone")] - GemstoneF03 = 179, - [Description("Tier 4 Dex Gemstone")] - GemstoneF04 = 180, - [Description("Tier 5 Dex Gemstone")] - GemstoneF05 = 181, - [Description("Tier 6 Dex Gemstone")] - GemstoneF06 = 182, - [Description("Tier 7 Dex Gemstone")] - GemstoneF07 = 183, - [Description("Tier 8 Dex Gemstone")] - GemstoneF08 = 184, - [Description("Tier 9 Dex Gemstone")] - GemstoneF09 = 185, - [Description("Tier 10 Dex Gemstone")] - GemstoneF10 = 186, - [Description("Tier 1 Accuracy Gemstone")] - GemstoneG01 = 187, - [Description("Tier 2 Accuracy Gemstone")] - GemstoneG02 = 188, - [Description("Tier 3 Accuracy Gemstone")] - GemstoneG03 = 189, - [Description("Tier 4 Accuracy Gemstone")] - GemstoneG04 = 190, - [Description("Tier 5 Accuracy Gemstone")] - GemstoneG05 = 191, - [Description("Tier 6 Accuracy Gemstone")] - GemstoneG06 = 192, - [Description("Tier 7 Accuracy Gemstone")] - GemstoneG07 = 193, - [Description("Tier 8 Accuracy Gemstone")] - GemstoneG08 = 194, - [Description("Tier 9 Accuracy Gemstone")] - GemstoneG09 = 195, - [Description("Tier 10 Accuracy Gemstone")] - GemstoneG10 = 196, - [Description("Tier 1 Offense Gemstone")] - GemstoneH01 = 197, - [Description("Tier 2 Offense Gemstone")] - GemstoneH02 = 198, - [Description("Tier 3 Offense Gemstone")] - GemstoneH03 = 199, - [Description("Tier 4 Offense Gemstone")] - GemstoneH04 = 200, - [Description("Tier 5 Offense Gemstone")] - GemstoneH05 = 201, - [Description("Tier 6 Offense Gemstone")] - GemstoneH06 = 202, - [Description("Tier 7 Offense Gemstone")] - GemstoneH07 = 203, - [Description("Tier 8 Offense Gemstone")] - GemstoneH08 = 204, - [Description("Tier 9 Offense Gemstone")] - GemstoneH09 = 205, - [Description("Tier 10 Offense Gemstone")] - GemstoneH10 = 206, - [Description("Toad's Toolkit")] - EnchantJockerItemNormal = 207, - [Description("Toad's Toolkit")] - EnchantJockerItemRare = 208, - [Description("Toad's Toolkit")] - EnchantJockerItemElite = 209, - [Description("Toad's Toolkit")] - EnchantJockerItemExcellent = 210, - [Description("Toad's Toolkit")] - EnchantJockerItemLegendary = 211, - [Description("Toad's Toolkit")] - EnchantJockerItemEpic = 212, - [Description("Daily Mission Insta-Completion Voucher")] - FameCompletionTicket = 213, - [Description("")] - PrismShard = 214, - [Description("")] - PrismStone = 215, - [Description("")] - WeddingHallCoupon_Grade1 = 216, - [Description("")] - WeddingHallCoupon_Grade2 = 217, - [Description("")] - WeddingHallCoupon_Grade3 = 218, - - // Not defined in client mapping. - PetEXP = 250, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Enum; + +// Please leave these names as-is to match item tags found in xml. +public enum ItemTag { + None = 0, + [Description("Channel Chat Voucher")] + FreeChannelChatCoupon = 1, + [Description("World Chat Voucher")] + FreeWorldChatCoupon = 2, + [Description("")] + FreeSuperChatCoupon = 3, + [Description("Revive Voucher")] + FreeReviveCoupon = 4, + [Description("Template Voucher")] + FreeDesignCoupon = 5, + [Description("Pet Name Change Voucher")] + FreePetNameChangeCoupon = 6, + [Description("Weekly Dungeon Ticket, Bonus Dungeon Reward Voucher, Dungeon Ticket")] + DungeonRewardTicketA = 7, + [Description("Treasure Dungeon Ticket")] + DungeonRewardTicketB = 8, + [Description("Id=20302765")] + DungeonRewardTicketC = 9, + [Description("Bonus Dungeon Reward Voucher")] + DungeonRewardTypeA = 10, + [Description("Fantastic Bonus Dungeon Reward Voucher")] + DungeonRewardTypeB = 11, + [Description("Superior Bonus Dungeon Reward Voucher")] + DungeonRewardTypeC = 12, + [Description("")] + DungeonRewardTypeD = 13, + [Description("Free Rotors Walkie-talkie")] + air_taxi = 14, + [Description("Rotors Walkie-talkie")] + air_taxi_advanced = 15, + [Description("Face Change Voucher")] + beauty_face = 16, + [Description("Hairstyle Voucher")] + beauty_hair = 17, + [Description("Gear Dye Voucher")] + beauty_itemcolor = 18, + [Description("Skin Tone Change Voucher")] + beauty_skin = 19, + [Description("Cosmetics Voucher")] + beauty_makeup = 20, + [Description("Special Hairstyle Voucher")] + beauty_hair_special = 21, + [Description("")] + beauty_hair_special_bonus = 22, + [Description("Red Bella Figurine Package, Black Bella Figurine Package, Yellow Mika Figurine Package, Blue Mika Figurine Package")] + Cashshop_Figure = 23, + [Description("Party Summon Scroll")] + party_call = 24, + [Description("Free Expedition Specialty Reset")] + ShadowPointReset = 25, + [Description("Lulu's Key")] + LulluKey = 26, + [Description("Mystery Box")] + LulluBox = 27, + [Description("")] + LulluKey01 = 28, + [Description("")] + LulluBox01 = 29, + [Description("")] + LulluKey02 = 30, + [Description("")] + LulluBox02 = 31, + [Description("Luxurious Style Key")] + LulluKey03 = 32, + [Description("Holiday Mystery Box")] + LulluBox03 = 33, + [Description("Golden Jewel Key")] + LulluKey04 = 34, + [Description("Mystery Style Box")] + LulluBox04 = 35, + [Description("")] + LulluKey05 = 36, + [Description("Mystery Jewel Box")] + LulluBox05 = 37, + [Description("")] + LulluKey06 = 38, + [Description("")] + LulluBox06 = 39, + [Description("")] + LulluKey07 = 40, + [Description("")] + LulluBox07 = 41, + [Description("")] + LulluKey08 = 42, + [Description("Style Crate")] + LulluBox08 = 43, + [Description("")] + LulluKey09 = 44, + [Description("")] + LulluBox09 = 45, + [Description("")] + LulluKey10 = 46, + [Description("")] + LulluBox10 = 47, + [Description("")] + LulluKey11 = 48, + [Description("")] + LulluBox11 = 49, + [Description("")] + LulluKey12 = 50, + [Description("")] + LulluBox12 = 51, + [Description("")] + LulluKey13 = 52, + [Description("")] + LulluBox13 = 53, + [Description("")] + LulluKey14 = 54, + [Description("")] + LulluBox14 = 55, + [Description("")] + LulluKey15 = 56, + [Description("")] + LulluBox15 = 57, + [Description("")] + LulluKey16 = 58, + [Description("")] + LulluBox16 = 59, + [Description("")] + LulluKey17 = 60, + [Description("")] + LulluBox17 = 61, + [Description("")] + LulluKey18 = 62, + [Description("")] + LulluBox18 = 63, + [Description("")] + LulluKey19 = 64, + [Description("")] + LulluBox19 = 65, + [Description("")] + LulluKey20 = 66, + [Description("")] + LulluBox20 = 67, + [Description("")] + LulluKey21 = 68, + [Description("")] + LulluBox21 = 69, + [Description("")] + LulluKey22 = 70, + [Description("")] + LulluBox22 = 71, + [Description("")] + LulluKey23 = 72, + [Description("")] + LulluBox23 = 73, + [Description("")] + LulluKey24 = 74, + [Description("")] + LulluBox24 = 75, + [Description("")] + LulluKey25 = 76, + [Description("")] + LulluBox25 = 77, + [Description("")] + LulluKey26 = 78, + [Description("")] + LulluBox26 = 79, + [Description("")] + LulluKey27 = 80, + [Description("")] + LulluBox27 = 81, + [Description("")] + LulluKey28 = 82, + [Description("")] + LulluBox28 = 83, + [Description("")] + LulluKey29 = 84, + [Description("")] + LulluBox29 = 85, + [Description("")] + LulluKey30 = 86, + [Description("")] + LulluBox30 = 87, + [Description("")] + PetBox = 88, + [Description("")] + DungeonLimitItem = 89, + [Description("")] + SkinGemDustA = 90, + [Description("")] + SkinGemDustB = 91, + [Description("Special Outfit Crystal")] + SkinCrystal = 92, + [Description("")] + SkinGemMaterial = 93, + [Description("")] + CharmStone = 94, + [Description("")] + ProtectStone = 95, + [Description("Metacell")] + MetaCell = 96, + [Description("Red Crystal")] + RedCrystal = 97, + [Description("Green Crystal")] + GreenCrystal = 98, + [Description("Blue Crystal")] + BlueCrystal = 99, + [Description("Crystal Fragment")] + CrystalPiece = 100, + [Description("Onyx Crystal")] + Onix = 101, + [Description("Chaos Onyx Crystal")] + ChaosOnix = 102, + [Description("")] + CrystalGemstone = 103, + [Description("")] + FishingLure = 104, + [Description("Weapon Attribute Lock Scroll")] + LockItemOptionWeapon = 105, + [Description("Armor Attribute Lock Scroll")] + LockItemOptionArmor = 106, + [Description("Accessory Attribute Lock Scroll")] + LockItemOptionAccessory = 107, + [Description("")] + LockItemOptionPet = 108, + [Description("OX Quiz Host Ticket")] + UGCEventOxOpen = 109, + [Description("Glamour Anvil")] + ItemExtraction = 110, + [Description("Pet Skin Crafting Scroll")] + PetExtraction = 111, + [Description("Dungeon Reward Booster")] + ExpenseRewardTicket = 112, + [Description("Instant Gathering Voucher")] + AutoMastery = 113, + [Description("Blue Gem Dust")] + DustBlue = 114, + [Description("Purple Gem Dust")] + DustPurple = 115, + [Description("Orange Gem Dust")] + DustOrange = 116, + [Description("White Gem Dust")] + DustWhite = 117, + [Description("Red Gem Dust")] + DustRed = 118, + [Description("Green Gem Dust")] + DustGreen = 119, + [Description("Cyan Gem Dust")] + DustCyan = 120, + [Description("Yellow Gem Dust")] + DustYellow = 121, + [Description("Skill Tab Voucher")] + SkillBookTreeAddTabCoupon = 122, + [Description("")] + EpicCharmStone = 123, + [Description("")] + EpicProtectStone = 124, + [Description("Rainbow Feed, Pumpkin Pie Feed, Ghost Fish")] + SlimeFood = 125, + [Description("* Fireworks")] + Firework = 126, + [Description("Tier 1 Wisdom Gemstone")] + GemstoneA01 = 127, + [Description("Tier 2 Wisdom Gemstone")] + GemstoneA02 = 128, + [Description("Tier 3 Wisdom Gemstone")] + GemstoneA03 = 129, + [Description("Tier 4 Wisdom Gemstone")] + GemstoneA04 = 130, + [Description("Tier 5 Wisdom Gemstone")] + GemstoneA05 = 131, + [Description("Tier 6 Wisdom Gemstone")] + GemstoneA06 = 132, + [Description("Tier 7 Wisdom Gemstone")] + GemstoneA07 = 133, + [Description("Tier 8 Wisdom Gemstone")] + GemstoneA08 = 134, + [Description("Tier 9 Wisdom Gemstone")] + GemstoneA09 = 135, + [Description("Tier 10 Wisdom Gemstone")] + GemstoneA10 = 136, + [Description("Tier 1 Luck Gemstone")] + GemstoneB01 = 137, + [Description("Tier 2 Luck Gemstone")] + GemstoneB02 = 138, + [Description("Tier 3 Luck Gemstone")] + GemstoneB03 = 139, + [Description("Tier 4 Luck Gemstone")] + GemstoneB04 = 140, + [Description("Tier 5 Luck Gemstone")] + GemstoneB05 = 141, + [Description("Tier 6 Luck Gemstone")] + GemstoneB06 = 142, + [Description("Tier 7 Luck Gemstone")] + GemstoneB07 = 143, + [Description("Tier 8 Luck Gemstone")] + GemstoneB08 = 144, + [Description("Tier 9 Luck Gemstone")] + GemstoneB09 = 145, + [Description("Tier 10 Luck Gemstone")] + GemstoneB10 = 146, + [Description("Tier 1 Destruction Gemstone")] + GemstoneC01 = 147, + [Description("Tier 2 Destruction Gemstone ")] + GemstoneC02 = 148, + [Description("Tier 3 Destruction Gemstone")] + GemstoneC03 = 149, + [Description("Tier 4 Destruction Gemstone")] + GemstoneC04 = 150, + [Description("Tier 5 Destruction Gemstone")] + GemstoneC05 = 151, + [Description("Tier 6 Destruction Gemstone")] + GemstoneC06 = 152, + [Description("Tier 7 Destruction Gemstone")] + GemstoneC07 = 153, + [Description("Tier 8 Destruction Gemstone")] + GemstoneC08 = 154, + [Description("Tier 9 Destruction Gemstone")] + GemstoneC09 = 155, + [Description("Tier 10 Destruction Gemstone")] + GemstoneC10 = 156, + [Description("Tier 1 Life Gemstone")] + GemstoneD01 = 157, + [Description("Tier 2 Life Gemstone ")] + GemstoneD02 = 158, + [Description("Tier 3 Life Gemstone")] + GemstoneD03 = 159, + [Description("Tier 4 Life Gemstone")] + GemstoneD04 = 160, + [Description("Tier 5 Life Gemstone")] + GemstoneD05 = 161, + [Description("Tier 6 Life Gemstone")] + GemstoneD06 = 162, + [Description("Tier 7 Life Gemstone")] + GemstoneD07 = 163, + [Description("Tier 8 Life Gemstone")] + GemstoneD08 = 164, + [Description("Tier 9 Life Gemstone")] + GemstoneD09 = 165, + [Description("Tier 10 Life Gemstone")] + GemstoneD10 = 166, + [Description("Tier 1 Power Gemstone")] + GemstoneE01 = 167, + [Description("Tier 2 Power Gemstone")] + GemstoneE02 = 168, + [Description("Tier 3 Power Gemstone")] + GemstoneE03 = 169, + [Description("Tier 4 Power Gemstone")] + GemstoneE04 = 170, + [Description("Tier 5 Power Gemstone")] + GemstoneE05 = 171, + [Description("Tier 6 Power Gemstone")] + GemstoneE06 = 172, + [Description("Tier 7 Power Gemstone")] + GemstoneE07 = 173, + [Description("Tier 8 Power Gemstone")] + GemstoneE08 = 174, + [Description("Tier 9 Power Gemstone")] + GemstoneE09 = 175, + [Description("Tier 10 Power Gemstone")] + GemstoneE10 = 176, + [Description("Tier 1 Dex Gemstone")] + GemstoneF01 = 177, + [Description("Tier 2 Dex Gemstone")] + GemstoneF02 = 178, + [Description("Tier 3 Dex Gemstone")] + GemstoneF03 = 179, + [Description("Tier 4 Dex Gemstone")] + GemstoneF04 = 180, + [Description("Tier 5 Dex Gemstone")] + GemstoneF05 = 181, + [Description("Tier 6 Dex Gemstone")] + GemstoneF06 = 182, + [Description("Tier 7 Dex Gemstone")] + GemstoneF07 = 183, + [Description("Tier 8 Dex Gemstone")] + GemstoneF08 = 184, + [Description("Tier 9 Dex Gemstone")] + GemstoneF09 = 185, + [Description("Tier 10 Dex Gemstone")] + GemstoneF10 = 186, + [Description("Tier 1 Accuracy Gemstone")] + GemstoneG01 = 187, + [Description("Tier 2 Accuracy Gemstone")] + GemstoneG02 = 188, + [Description("Tier 3 Accuracy Gemstone")] + GemstoneG03 = 189, + [Description("Tier 4 Accuracy Gemstone")] + GemstoneG04 = 190, + [Description("Tier 5 Accuracy Gemstone")] + GemstoneG05 = 191, + [Description("Tier 6 Accuracy Gemstone")] + GemstoneG06 = 192, + [Description("Tier 7 Accuracy Gemstone")] + GemstoneG07 = 193, + [Description("Tier 8 Accuracy Gemstone")] + GemstoneG08 = 194, + [Description("Tier 9 Accuracy Gemstone")] + GemstoneG09 = 195, + [Description("Tier 10 Accuracy Gemstone")] + GemstoneG10 = 196, + [Description("Tier 1 Offense Gemstone")] + GemstoneH01 = 197, + [Description("Tier 2 Offense Gemstone")] + GemstoneH02 = 198, + [Description("Tier 3 Offense Gemstone")] + GemstoneH03 = 199, + [Description("Tier 4 Offense Gemstone")] + GemstoneH04 = 200, + [Description("Tier 5 Offense Gemstone")] + GemstoneH05 = 201, + [Description("Tier 6 Offense Gemstone")] + GemstoneH06 = 202, + [Description("Tier 7 Offense Gemstone")] + GemstoneH07 = 203, + [Description("Tier 8 Offense Gemstone")] + GemstoneH08 = 204, + [Description("Tier 9 Offense Gemstone")] + GemstoneH09 = 205, + [Description("Tier 10 Offense Gemstone")] + GemstoneH10 = 206, + [Description("Toad's Toolkit")] + EnchantJockerItemNormal = 207, + [Description("Toad's Toolkit")] + EnchantJockerItemRare = 208, + [Description("Toad's Toolkit")] + EnchantJockerItemElite = 209, + [Description("Toad's Toolkit")] + EnchantJockerItemExcellent = 210, + [Description("Toad's Toolkit")] + EnchantJockerItemLegendary = 211, + [Description("Toad's Toolkit")] + EnchantJockerItemEpic = 212, + [Description("Daily Mission Insta-Completion Voucher")] + FameCompletionTicket = 213, + [Description("")] + PrismShard = 214, + [Description("")] + PrismStone = 215, + [Description("")] + WeddingHallCoupon_Grade1 = 216, + [Description("")] + WeddingHallCoupon_Grade2 = 217, + [Description("")] + WeddingHallCoupon_Grade3 = 218, + + // Not defined in client mapping. + PetEXP = 250, +} diff --git a/Maple2.Model/Enum/JobGroup.cs b/Maple2.Model/Enum/JobGroup.cs index 949c77604..1c2553572 100644 --- a/Maple2.Model/Enum/JobGroup.cs +++ b/Maple2.Model/Enum/JobGroup.cs @@ -1,47 +1,47 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo -namespace Maple2.Model.Enum; - -// table/jobgroup.xml -public enum JobCode : short { - None = 0, - Newbie = 1, - Knight = 10, - Berserker = 20, - Wizard = 30, - Priest = 40, - Archer = 50, - HeavyGunner = 60, - Thief = 70, - Assassin = 80, - RuneBlader = 90, - Striker = 100, - SoulBinder = 110, - //GameMaster = 999, -} - -public enum Job { - Newbie = 10, - Knight = 100, - KnightII = 101, - Berserker = 200, - BerserkerII = 201, - Wizard = 300, - WizardII = 301, - Priest = 400, - PriestII = 401, - Archer = 500, - ArcherII = 501, - HeavyGunner = 600, - HeavyGunnerII = 601, - Thief = 700, - ThiefII = 701, - Assassin = 800, - AssassinII = 801, - RuneBlader = 900, - RuneBladerII = 901, - Striker = 1000, - StrikerII = 1001, - SoulBinder = 1100, - SoulBinderII = 1101, - //GameMaster = 999, -} +// ReSharper disable InconsistentNaming, IdentifierTypo +namespace Maple2.Model.Enum; + +// table/jobgroup.xml +public enum JobCode : short { + None = 0, + Newbie = 1, + Knight = 10, + Berserker = 20, + Wizard = 30, + Priest = 40, + Archer = 50, + HeavyGunner = 60, + Thief = 70, + Assassin = 80, + RuneBlader = 90, + Striker = 100, + SoulBinder = 110, + //GameMaster = 999, +} + +public enum Job { + Newbie = 10, + Knight = 100, + KnightII = 101, + Berserker = 200, + BerserkerII = 201, + Wizard = 300, + WizardII = 301, + Priest = 400, + PriestII = 401, + Archer = 500, + ArcherII = 501, + HeavyGunner = 600, + HeavyGunnerII = 601, + Thief = 700, + ThiefII = 701, + Assassin = 800, + AssassinII = 801, + RuneBlader = 900, + RuneBladerII = 901, + Striker = 1000, + StrikerII = 1001, + SoulBinder = 1100, + SoulBinderII = 1101, + //GameMaster = 999, +} diff --git a/Maple2.Model/Enum/LapenshardSlot.cs b/Maple2.Model/Enum/LapenshardSlot.cs index 4ae8c1d33..718a2a6bd 100644 --- a/Maple2.Model/Enum/LapenshardSlot.cs +++ b/Maple2.Model/Enum/LapenshardSlot.cs @@ -1,10 +1,10 @@ -namespace Maple2.Model.Enum; - -public enum LapenshardSlot { - Red1 = 1, - Red2 = 2, - Blue1 = 3, - Blue2 = 4, - Green1 = 5, - Green2 = 6, -} +namespace Maple2.Model.Enum; + +public enum LapenshardSlot { + Red1 = 1, + Red2 = 2, + Blue1 = 3, + Blue2 = 4, + Green1 = 5, + Green2 = 6, +} diff --git a/Maple2.Model/Enum/LiftableState.cs b/Maple2.Model/Enum/LiftableState.cs index 857e464ae..4290b2d75 100644 --- a/Maple2.Model/Enum/LiftableState.cs +++ b/Maple2.Model/Enum/LiftableState.cs @@ -1,8 +1,8 @@ -namespace Maple2.Model.Enum; - -public enum LiftableState : byte { - Default = 0, - Removed = 1, - Disabled = 2, - Respawning = 3, -} +namespace Maple2.Model.Enum; + +public enum LiftableState : byte { + Default = 0, + Removed = 1, + Disabled = 2, + Respawning = 3, +} diff --git a/Maple2.Model/Enum/LiquidType.cs b/Maple2.Model/Enum/LiquidType.cs index a381cbfe2..d7defa858 100644 --- a/Maple2.Model/Enum/LiquidType.cs +++ b/Maple2.Model/Enum/LiquidType.cs @@ -1,15 +1,15 @@ -// ReSharper disable InconsistentNaming - -namespace Maple2.Model.Enum; - -public enum LiquidType { - none = 0, - water = 1, - seawater = 2, - lava = 3, - poison = 4, - oil = 5, - devilwater = 6, - emeraldwater = 7, - all = 8, -} +// ReSharper disable InconsistentNaming + +namespace Maple2.Model.Enum; + +public enum LiquidType { + none = 0, + water = 1, + seawater = 2, + lava = 3, + poison = 4, + oil = 5, + devilwater = 6, + emeraldwater = 7, + all = 8, +} diff --git a/Maple2.Model/Enum/Locale.cs b/Maple2.Model/Enum/Locale.cs index 922592721..1a84a1d83 100644 --- a/Maple2.Model/Enum/Locale.cs +++ b/Maple2.Model/Enum/Locale.cs @@ -1,21 +1,21 @@ -// ReSharper disable InconsistentNaming - -namespace Maple2.Model.Enum; - -public enum Locale : byte { - KR = 0, - JP = 1, - NA = 2, - CN = 3, -} - -public enum Language { - Korean = 0, - Japanese = 1, - English = 2, - Chinese = 3, - French = 4, - German = 5, - Portuguese = 6, - Spanish = 7, -} +// ReSharper disable InconsistentNaming + +namespace Maple2.Model.Enum; + +public enum Locale : byte { + KR = 0, + JP = 1, + NA = 2, + CN = 3, +} + +public enum Language { + Korean = 0, + Japanese = 1, + English = 2, + Chinese = 3, + French = 4, + German = 5, + Portuguese = 6, + Spanish = 7, +} diff --git a/Maple2.Model/Enum/Maid.cs b/Maple2.Model/Enum/Maid.cs index c92e504b2..c45ee1242 100644 --- a/Maple2.Model/Enum/Maid.cs +++ b/Maple2.Model/Enum/Maid.cs @@ -1,7 +1,7 @@ -namespace Maple2.Model.Enum; - -public enum MaidMood : byte { - Normal = 0, // (So-so) - Good = 1, - VeryGood = 2, -} +namespace Maple2.Model.Enum; + +public enum MaidMood : byte { + Normal = 0, // (So-so) + Good = 1, + VeryGood = 2, +} diff --git a/Maple2.Model/Enum/MailType.cs b/Maple2.Model/Enum/MailType.cs index 4ba197075..669c0ebdc 100644 --- a/Maple2.Model/Enum/MailType.cs +++ b/Maple2.Model/Enum/MailType.cs @@ -1,13 +1,13 @@ -namespace Maple2.Model.Enum; - -public enum MailType : byte { - Player = 1, - System = 101, - BlackMarketSale = 102, - BlackMarketFail = 103, - BlackMarketListingCancel = 104, - MesoMarket = 106, - WeddingSystem = 111, - WeddingInvite = 112, - Ad = 200, -} +namespace Maple2.Model.Enum; + +public enum MailType : byte { + Player = 1, + System = 101, + BlackMarketSale = 102, + BlackMarketFail = 103, + BlackMarketListingCancel = 104, + MesoMarket = 106, + WeddingSystem = 111, + WeddingInvite = 112, + Ad = 200, +} diff --git a/Maple2.Model/Enum/Map.cs b/Maple2.Model/Enum/Map.cs index 92cf78235..906b43c05 100644 --- a/Maple2.Model/Enum/Map.cs +++ b/Maple2.Model/Enum/Map.cs @@ -1,55 +1,55 @@ -namespace Maple2.Model.Enum; - -public enum MapRegion { - None = 0, - Tutorial = 1, // Includes Maple Island - LithHarbor = 2, - Tria = 3, - Kerning = 4, - Henesys = 5, - Perion = 6, - Ellinia = 7, - Taliskar = 8, - Ludibrium = 9, - Ludari = 11, - KarkarDesert = 12, - Nazkar = 13, - Calibre = 14, - Kritias = 15, - ShadowWorld = 100, -} - -public enum MapType { - None = 0, - Quest = 1, - Life = 2, // land plots and harvestable objects - Home = 3, - Telescope = 4, - Dungeon = 5, - PocketRealm = 7, - Event = 8, - Pvp = 9, - Alikar = 10, - Shadow = 11, - Shelter = 12, - Arcade = 13, - Event2 = 14, - SurvivalSolo = 15, - SurvivalTeam = 16, - FieldWar = 17, -} - -public enum Continent { - MapleIsland = 101, - VictoriaIsland = 102, - KarkarIsland = 103, - Calibre = 104, - Kritias = 105, - ShadowWorld = 202, -} - -public enum AutoReviveType { - None = 0, - Trigger = 1, - Countdown = 2, // Only map 65000003 - Treasure Island. Uses autoRevivalTime -} +namespace Maple2.Model.Enum; + +public enum MapRegion { + None = 0, + Tutorial = 1, // Includes Maple Island + LithHarbor = 2, + Tria = 3, + Kerning = 4, + Henesys = 5, + Perion = 6, + Ellinia = 7, + Taliskar = 8, + Ludibrium = 9, + Ludari = 11, + KarkarDesert = 12, + Nazkar = 13, + Calibre = 14, + Kritias = 15, + ShadowWorld = 100, +} + +public enum MapType { + None = 0, + Quest = 1, + Life = 2, // land plots and harvestable objects + Home = 3, + Telescope = 4, + Dungeon = 5, + PocketRealm = 7, + Event = 8, + Pvp = 9, + Alikar = 10, + Shadow = 11, + Shelter = 12, + Arcade = 13, + Event2 = 14, + SurvivalSolo = 15, + SurvivalTeam = 16, + FieldWar = 17, +} + +public enum Continent { + MapleIsland = 101, + VictoriaIsland = 102, + KarkarIsland = 103, + Calibre = 104, + Kritias = 105, + ShadowWorld = 202, +} + +public enum AutoReviveType { + None = 0, + Trigger = 1, + Countdown = 2, // Only map 65000003 - Treasure Island. Uses autoRevivalTime +} diff --git a/Maple2.Model/Enum/MapAttribute.cs b/Maple2.Model/Enum/MapAttribute.cs index f22ecf2f3..7bbc01d05 100644 --- a/Maple2.Model/Enum/MapAttribute.cs +++ b/Maple2.Model/Enum/MapAttribute.cs @@ -1,16 +1,16 @@ -namespace Maple2.Model.Enum; - -public enum MapAttribute { - none, - glass, - grass, - ground, - metal, - oil, - plastic, - rock, - sand, - snow, - water, - wood, -} +namespace Maple2.Model.Enum; + +public enum MapAttribute { + none, + glass, + grass, + ground, + metal, + oil, + plastic, + rock, + sand, + snow, + water, + wood, +} diff --git a/Maple2.Model/Enum/MasteryType.cs b/Maple2.Model/Enum/MasteryType.cs index 9ef50cb4a..21b74966e 100644 --- a/Maple2.Model/Enum/MasteryType.cs +++ b/Maple2.Model/Enum/MasteryType.cs @@ -1,16 +1,16 @@ -namespace Maple2.Model.Enum; - -public enum MasteryType : byte { - Unknown = 0, - Fishing = 1, - Music = 2, - Mining = 3, - Gathering = 4, - Breeding = 5, - Farming = 6, - Blacksmithing = 7, - Engraving = 8, - Alchemist = 9, - Cooking = 10, - PetTaming = 11, -} +namespace Maple2.Model.Enum; + +public enum MasteryType : byte { + Unknown = 0, + Fishing = 1, + Music = 2, + Mining = 3, + Gathering = 4, + Breeding = 5, + Farming = 6, + Blacksmithing = 7, + Engraving = 8, + Alchemist = 9, + Cooking = 10, + PetTaming = 11, +} diff --git a/Maple2.Model/Enum/MedalType.cs b/Maple2.Model/Enum/MedalType.cs index 11af89582..8fa27b98a 100644 --- a/Maple2.Model/Enum/MedalType.cs +++ b/Maple2.Model/Enum/MedalType.cs @@ -1,7 +1,7 @@ -namespace Maple2.Model.Enum; - -public enum MedalType : byte { - Tail = 0, - Riding = 1, - Gliding = 2, -} +namespace Maple2.Model.Enum; + +public enum MedalType : byte { + Tail = 0, + Riding = 1, + Gliding = 2, +} diff --git a/Maple2.Model/Enum/Mentoring.cs b/Maple2.Model/Enum/Mentoring.cs index 88ef86c58..89e8d37f3 100644 --- a/Maple2.Model/Enum/Mentoring.cs +++ b/Maple2.Model/Enum/Mentoring.cs @@ -1,13 +1,13 @@ -// ReSharper disable InconsistentNaming - -namespace Maple2.Model.Enum; - -public enum MentorRole : byte { - None = 0, - MenteeCandidate = 1, - Mentee = 2, - UnregisteredMentee = 3, - MentorCandidate = 4, - Mentor = 5, - UnregisteredMentor = 6, -} +// ReSharper disable InconsistentNaming + +namespace Maple2.Model.Enum; + +public enum MentorRole : byte { + None = 0, + MenteeCandidate = 1, + Mentee = 2, + UnregisteredMentee = 3, + MentorCandidate = 4, + Mentor = 5, + UnregisteredMentor = 6, +} diff --git a/Maple2.Model/Enum/MeretMarket.cs b/Maple2.Model/Enum/MeretMarket.cs index 494dff62e..0513d70ec 100644 --- a/Maple2.Model/Enum/MeretMarket.cs +++ b/Maple2.Model/Enum/MeretMarket.cs @@ -1,71 +1,71 @@ -namespace Maple2.Model.Enum; - -public enum MeretMarketSection { - All = 0, - Premium = 100000, - RedMeret = 100001, - Ugc = 110000, -} - -public enum MeretMarketSort : byte { - None = 0, - MostPopularUgc = 1, - PriceLowest = 2, - PriceHighest = 3, - MostRecent = 4, - MostPopularPremium = 5, - TopSeller = 6, -} - -public enum MeretMarketItemSaleTag : byte { - None = 0, - New = 1, - Hot = 2, - Event = 3, - Sale = 4, - Special = 5, -} - -public enum MeretMarketCurrencyType : byte { - Meso = 0, - Meret = 1, - RedMeret = 2, -} - -public enum MeretMarketCategory { - None = 0, - Promo = 10, - Functional = 40300, - Lifestyle = 40600, -} - -public enum MeretMarketBannerTag { - None = 0, - PinkGift = 1, - BlueGift = 2, -} - -[Flags] -public enum GenderFilterFlag : byte { - None = 0, - Male = 1, - Female = 2, - All = Male | Female, -} - -[Flags] -public enum JobFilterFlag { - None = 0, - Newbie = 1, - Knight = 2, - Berserker = 4, - Wizard = 8, - Priest = 16, - Archer = 32, - HeavyGunner = 64, - Thief = 128, - Assassin = 256, - RuneBlader = 512, - Striker = 1024, - SoulBinder = 2048, -} +namespace Maple2.Model.Enum; + +public enum MeretMarketSection { + All = 0, + Premium = 100000, + RedMeret = 100001, + Ugc = 110000, +} + +public enum MeretMarketSort : byte { + None = 0, + MostPopularUgc = 1, + PriceLowest = 2, + PriceHighest = 3, + MostRecent = 4, + MostPopularPremium = 5, + TopSeller = 6, +} + +public enum MeretMarketItemSaleTag : byte { + None = 0, + New = 1, + Hot = 2, + Event = 3, + Sale = 4, + Special = 5, +} + +public enum MeretMarketCurrencyType : byte { + Meso = 0, + Meret = 1, + RedMeret = 2, +} + +public enum MeretMarketCategory { + None = 0, + Promo = 10, + Functional = 40300, + Lifestyle = 40600, +} + +public enum MeretMarketBannerTag { + None = 0, + PinkGift = 1, + BlueGift = 2, +} + +[Flags] +public enum GenderFilterFlag : byte { + None = 0, + Male = 1, + Female = 2, + All = Male | Female, +} + +[Flags] +public enum JobFilterFlag { + None = 0, + Newbie = 1, + Knight = 2, + Berserker = 4, + Wizard = 8, + Priest = 16, + Archer = 32, + HeavyGunner = 64, + Thief = 128, + Assassin = 256, + RuneBlader = 512, + Striker = 1024, + SoulBinder = 2048, +} diff --git a/Maple2.Model/Enum/MigrationType.cs b/Maple2.Model/Enum/MigrationType.cs index 31150d03c..6877b9cdd 100644 --- a/Maple2.Model/Enum/MigrationType.cs +++ b/Maple2.Model/Enum/MigrationType.cs @@ -1,8 +1,8 @@ -namespace Maple2.Model.Enum; - -public enum MigrationType { - Normal = 0, - DecorPlanner = 1, - BlueprintDesigner = 3, - Dungeon = 4, -} +namespace Maple2.Model.Enum; + +public enum MigrationType { + Normal = 0, + DecorPlanner = 1, + BlueprintDesigner = 3, + Dungeon = 4, +} diff --git a/Maple2.Model/Enum/NpcAi.cs b/Maple2.Model/Enum/NpcAi.cs index 7948e92a4..62ac4c46d 100644 --- a/Maple2.Model/Enum/NpcAi.cs +++ b/Maple2.Model/Enum/NpcAi.cs @@ -1,65 +1,65 @@ -namespace Maple2.Model.Enum; - -public enum AiConditionTargetState { - GrabTarget, - HoldMe, -} - -public enum AiConditionOp { - Equal, - Greater, - Less, - GreaterEqual, - LessEqual, -} - -public enum NodeSummonOption { - None, - MasterHp, - HitDamage, - LinkHp, -} - -public enum NodeSummonMaster { - Master, - Slave, - None, -} - -public enum NodeAiTarget { - DefaultTarget, - Hostile, - Friendly, -} - -public enum NodeTargetType { - Rand, - Near, - Far, - Mid, - NearAssociated, - RankAssociated, - HasAdditional, - RandAssociated, - GrabbedUser, - Random = Rand, -} - -public enum NodeJumpType { - JumpA = 1, - JumpB = 2, -} - -public enum NodeRideType { - Slave, -} - -public enum NodeBuffType { - Add, - Remove, -} - -public enum NodePopupType : byte { - Talk, - CutIn, -} +namespace Maple2.Model.Enum; + +public enum AiConditionTargetState { + GrabTarget, + HoldMe, +} + +public enum AiConditionOp { + Equal, + Greater, + Less, + GreaterEqual, + LessEqual, +} + +public enum NodeSummonOption { + None, + MasterHp, + HitDamage, + LinkHp, +} + +public enum NodeSummonMaster { + Master, + Slave, + None, +} + +public enum NodeAiTarget { + DefaultTarget, + Hostile, + Friendly, +} + +public enum NodeTargetType { + Rand, + Near, + Far, + Mid, + NearAssociated, + RankAssociated, + HasAdditional, + RandAssociated, + GrabbedUser, + Random = Rand, +} + +public enum NodeJumpType { + JumpA = 1, + JumpB = 2, +} + +public enum NodeRideType { + Slave, +} + +public enum NodeBuffType { + Add, + Remove, +} + +public enum NodePopupType : byte { + Talk, + CutIn, +} diff --git a/Maple2.Model/Enum/NpcTalk.cs b/Maple2.Model/Enum/NpcTalk.cs index 7b7d0213e..6117625ec 100644 --- a/Maple2.Model/Enum/NpcTalk.cs +++ b/Maple2.Model/Enum/NpcTalk.cs @@ -1,131 +1,131 @@ -namespace Maple2.Model.Enum; - -[Flags] -public enum NpcTalkType : byte { - None = 0, - /// - /// Simple NpcTalk without CinematicComponent, used for UIDialogs. - /// sub_649B00(uiTalkMgr, npcId, 1) - /// - Dialog = 1, - Talk = 2, - Quest = 4, - /// - /// Similar to '1': - /// sub_649B00(uiTalkMgr, npcId, 0) - /// - Select = 8, - Component = 16, - /// - /// Seems to affect 'SelectableTalk' only - /// - Flag6 = 32, -} - -public enum NpcTalkButton { - None = 0, - /// - /// No options - /// - Empty = 1, - /// - /// s_itemenchant_cinematic_btn Quit:$key:1$ - /// - Stop = 2, - /// - /// s_quest_talk_end Close\n$key:57$ - /// - Close = 3, - /// - /// s_quest_talk_progress Next\n$key:57$|Close\n$key:1$ - /// - Next = 4, - /// - /// Used for Select script - /// - SelectableTalk = 5, - /// - /// s_quest_talk_accept Accept\n$key:57$|Decline\n$key:1$ - /// - QuestAccept = 6, - /// - /// s_quest_talk_complete Complete\n$key:57$|Close\n$key:1$ - /// - QuestComplete = 7, - /// - /// s_quest_talk_end Close\n$key:57$ - /// - QuestProgress = 8, - /// - /// s_quest_talk_progress OR s_quest_talk_end - /// - SelectableDistractor = 9, - /// - /// s_quest_talk_progress OR s_quest_talk_end - /// - SelectableBeauty = 10, - /// - /// s_changejob_accept Perform Job Advancement\n($key:57$)|Nevermind\n($key:1$) - /// - ChangeJob = 11, - /// - /// s_quest_talk_accept Accept\n$key:57$|Decline\n$key:1$ - /// - UgcSign = 12, - /// - /// s_resolve_panelty_accept Get Treatment\n$key:57$|Decline\n$key:1$ - /// - PenaltyResolve = 13, - /// - /// s_take_boat_accept Go\n$key:57$|Stay\n$key:1$ - /// - TakeBoat = 14, - /// - /// s_sell_ugc_map_accept Confirm\n$key:57$|Cancel\n$key:1$ - /// - SellUgcMap = 15, - /// - /// s_roulette_accept Spin\n$key:57$ - /// - Roulette = 16, - /// - /// s_quest_talk_end Close\n$key:57$ - /// s_roulette_talk_skip Skip\n$key:57$ - /// - RouletteSkip = 17, - /// - /// s_resolve_panelty_accept Get Treatment\n$key:57$|Decline\n$key:1$ - /// - HomeDoctor = 18, - /// - /// s_quest_talk_progress OR s_quest_talk_end - /// - CustomSelectableDistractor = 19, -} - -public enum NpcTalkAction : byte { - Unknown1 = 1, - MovePlayer = 3, - OpenDialog = 4, - RewardItem = 5, - RewardExp = 6, - RewardMeso = 7, - AddOption = 8, - Unknown9 = 9, - Cutscene = 10, -} - -public enum ScriptEventType : short { - EnchantSelect = 1, - EnchantFail = 2, - EnchantComplete = 3, - PeachySelect = 31, - RerollItemSelect = 100, - RerollFail = 101, - RerollOptionSelect = 102, - RerollComplete = 103, - EmpowerSelect = 202, - EmpowerTry = 203, - EmpowerResult = 204, - -} +namespace Maple2.Model.Enum; + +[Flags] +public enum NpcTalkType : byte { + None = 0, + /// + /// Simple NpcTalk without CinematicComponent, used for UIDialogs. + /// sub_649B00(uiTalkMgr, npcId, 1) + /// + Dialog = 1, + Talk = 2, + Quest = 4, + /// + /// Similar to '1': + /// sub_649B00(uiTalkMgr, npcId, 0) + /// + Select = 8, + Component = 16, + /// + /// Seems to affect 'SelectableTalk' only + /// + Flag6 = 32, +} + +public enum NpcTalkButton { + None = 0, + /// + /// No options + /// + Empty = 1, + /// + /// s_itemenchant_cinematic_btn Quit:$key:1$ + /// + Stop = 2, + /// + /// s_quest_talk_end Close\n$key:57$ + /// + Close = 3, + /// + /// s_quest_talk_progress Next\n$key:57$|Close\n$key:1$ + /// + Next = 4, + /// + /// Used for Select script + /// + SelectableTalk = 5, + /// + /// s_quest_talk_accept Accept\n$key:57$|Decline\n$key:1$ + /// + QuestAccept = 6, + /// + /// s_quest_talk_complete Complete\n$key:57$|Close\n$key:1$ + /// + QuestComplete = 7, + /// + /// s_quest_talk_end Close\n$key:57$ + /// + QuestProgress = 8, + /// + /// s_quest_talk_progress OR s_quest_talk_end + /// + SelectableDistractor = 9, + /// + /// s_quest_talk_progress OR s_quest_talk_end + /// + SelectableBeauty = 10, + /// + /// s_changejob_accept Perform Job Advancement\n($key:57$)|Nevermind\n($key:1$) + /// + ChangeJob = 11, + /// + /// s_quest_talk_accept Accept\n$key:57$|Decline\n$key:1$ + /// + UgcSign = 12, + /// + /// s_resolve_panelty_accept Get Treatment\n$key:57$|Decline\n$key:1$ + /// + PenaltyResolve = 13, + /// + /// s_take_boat_accept Go\n$key:57$|Stay\n$key:1$ + /// + TakeBoat = 14, + /// + /// s_sell_ugc_map_accept Confirm\n$key:57$|Cancel\n$key:1$ + /// + SellUgcMap = 15, + /// + /// s_roulette_accept Spin\n$key:57$ + /// + Roulette = 16, + /// + /// s_quest_talk_end Close\n$key:57$ + /// s_roulette_talk_skip Skip\n$key:57$ + /// + RouletteSkip = 17, + /// + /// s_resolve_panelty_accept Get Treatment\n$key:57$|Decline\n$key:1$ + /// + HomeDoctor = 18, + /// + /// s_quest_talk_progress OR s_quest_talk_end + /// + CustomSelectableDistractor = 19, +} + +public enum NpcTalkAction : byte { + Unknown1 = 1, + MovePlayer = 3, + OpenDialog = 4, + RewardItem = 5, + RewardExp = 6, + RewardMeso = 7, + AddOption = 8, + Unknown9 = 9, + Cutscene = 10, +} + +public enum ScriptEventType : short { + EnchantSelect = 1, + EnchantFail = 2, + EnchantComplete = 3, + PeachySelect = 31, + RerollItemSelect = 100, + RerollFail = 101, + RerollOptionSelect = 102, + RerollComplete = 103, + EmpowerSelect = 202, + EmpowerTry = 203, + EmpowerResult = 204, + +} diff --git a/Maple2.Model/Enum/NxShapeType.cs b/Maple2.Model/Enum/NxShapeType.cs index db10e2a36..b8722bc2f 100644 --- a/Maple2.Model/Enum/NxShapeType.cs +++ b/Maple2.Model/Enum/NxShapeType.cs @@ -1,14 +1,14 @@ -namespace Maple2.Model.Enum; - -public enum NxShapeType : uint { - Plane, - Sphere, - Box, - Capsule, - Wheel, - Convex, - Mesh, - HeightField, - RawMesh, - Compound, -} +namespace Maple2.Model.Enum; + +public enum NxShapeType : uint { + Plane, + Sphere, + Box, + Capsule, + Wheel, + Convex, + Mesh, + HeightField, + RawMesh, + Compound, +} diff --git a/Maple2.Model/Enum/Party.cs b/Maple2.Model/Enum/Party.cs index cdbd03313..cb1e2563e 100644 --- a/Maple2.Model/Enum/Party.cs +++ b/Maple2.Model/Enum/Party.cs @@ -1,30 +1,30 @@ -using System.ComponentModel; - -namespace Maple2.Model.Enum; - -public enum PartyInviteResponse : byte { - Accept = 1, - RejectInvite = 9, - RejectTimeout = 12, -} - -public enum PartyVoteType : byte { - Kick = 1, - ReadyCheck = 2, -} - -public enum PartyMessage { - [Description("The voting period has ended.")] - s_party_vote_expired, - [Description("The party leader has reset the dungeon.")] - s_field_enteracne_party_notify_reset_dungeon, - [Description("The vote to kick failed.")] - s_party_vote_rejected_kick_user, -} - -public enum PartySearchSort : byte { - MostMembers = 12, - LeastMembers = 13, - Newest = 21, - Oldest = 22, -} +using System.ComponentModel; + +namespace Maple2.Model.Enum; + +public enum PartyInviteResponse : byte { + Accept = 1, + RejectInvite = 9, + RejectTimeout = 12, +} + +public enum PartyVoteType : byte { + Kick = 1, + ReadyCheck = 2, +} + +public enum PartyMessage { + [Description("The voting period has ended.")] + s_party_vote_expired, + [Description("The party leader has reset the dungeon.")] + s_field_enteracne_party_notify_reset_dungeon, + [Description("The vote to kick failed.")] + s_party_vote_rejected_kick_user, +} + +public enum PartySearchSort : byte { + MostMembers = 12, + LeastMembers = 13, + Newest = 21, + Oldest = 22, +} diff --git a/Maple2.Model/Enum/PlayerObjectFlag.cs b/Maple2.Model/Enum/PlayerObjectFlag.cs index 8ef4d2a3a..7c23916f9 100644 --- a/Maple2.Model/Enum/PlayerObjectFlag.cs +++ b/Maple2.Model/Enum/PlayerObjectFlag.cs @@ -1,14 +1,14 @@ -namespace Maple2.Model.Enum; - -[Flags] -public enum PlayerObjectFlag : byte { - None = 0, - Dead = 1, - Position = 2, - Level = 4, - Job = 8, - Motto = 16, - GearScore = 32, - State = 64, - All = Dead | Position | Level | Job | Motto | GearScore | State, -} +namespace Maple2.Model.Enum; + +[Flags] +public enum PlayerObjectFlag : byte { + None = 0, + Dead = 1, + Position = 2, + Level = 4, + Job = 8, + Motto = 16, + GearScore = 32, + State = 64, + All = Dead | Position | Level | Job | Motto | GearScore | State, +} diff --git a/Maple2.Model/Enum/PlotState.cs b/Maple2.Model/Enum/PlotState.cs index bc0beca5c..4094160f9 100644 --- a/Maple2.Model/Enum/PlotState.cs +++ b/Maple2.Model/Enum/PlotState.cs @@ -1,7 +1,7 @@ -namespace Maple2.Model.Enum; - -public enum PlotState : byte { - Open = 0, - Taken = 1, - Pending = 4, -} +namespace Maple2.Model.Enum; + +public enum PlotState : byte { + Open = 0, + Taken = 1, + Pending = 4, +} diff --git a/Maple2.Model/Enum/Portal.cs b/Maple2.Model/Enum/Portal.cs index 76293e914..6c25f8ba7 100644 --- a/Maple2.Model/Enum/Portal.cs +++ b/Maple2.Model/Enum/Portal.cs @@ -1,19 +1,19 @@ -namespace Maple2.Model.Enum; - -public enum PortalType : byte { - Field = 0, - DungeonReturnToLobby = 1, - Event = 3, // Pocket Realms & Player Hosted Mini games - FieldToHome = 5, - Quest = 6, - Boss = 8, - DungeonEnter = 9, - InHome = 11, - LeaveDungeon = 13, - ContentsGuide = 15, -} - -public enum PortalActionType { - Interact = 0, - Touch = 1, -} +namespace Maple2.Model.Enum; + +public enum PortalType : byte { + Field = 0, + DungeonReturnToLobby = 1, + Event = 3, // Pocket Realms & Player Hosted Mini games + FieldToHome = 5, + Quest = 6, + Boss = 8, + DungeonEnter = 9, + InHome = 11, + LeaveDungeon = 13, + ContentsGuide = 15, +} + +public enum PortalActionType { + Interact = 0, + Touch = 1, +} diff --git a/Maple2.Model/Enum/Prestige.cs b/Maple2.Model/Enum/Prestige.cs index 38fb27c1f..21ff23ac1 100644 --- a/Maple2.Model/Enum/Prestige.cs +++ b/Maple2.Model/Enum/Prestige.cs @@ -1,7 +1,7 @@ -namespace Maple2.Model.Enum; - -public enum PrestigeAwardType { - none, - item, - statPoint, -} +namespace Maple2.Model.Enum; + +public enum PrestigeAwardType { + none, + item, + statPoint, +} diff --git a/Maple2.Model/Enum/Quest.cs b/Maple2.Model/Enum/Quest.cs index 6ebdd277c..b882e2325 100644 --- a/Maple2.Model/Enum/Quest.cs +++ b/Maple2.Model/Enum/Quest.cs @@ -1,44 +1,44 @@ -namespace Maple2.Model.Enum; - -public enum QuestType { - EpicQuest = 0, - WorldQuest = 1, - EventQuest = 2, - DailyMission = 3, // Navigator - FieldMission = 4, // Exploration - EventMission = 5, - GuildQuest = 6, - MentoringMission = 7, - FieldQuest = 8, - AllianceQuest = 9, - WeddingMission = 10, -} - -public enum QuestState { - None = 0, - Started = 1, - Completed = 2, -} - -public enum QuestRemoteType { - None = 0, - Cinematic = 1, - Popup = 2, - System = 3, -} - -public enum QuestDispatchType { - None, - MonologueAccept, - MonologueComplete, - DirectAccept, - DirectComplete, -} - -public enum QuestEventMissionType { - none, - gallery, - levelup_package, - stamp, - timerun, -} +namespace Maple2.Model.Enum; + +public enum QuestType { + EpicQuest = 0, + WorldQuest = 1, + EventQuest = 2, + DailyMission = 3, // Navigator + FieldMission = 4, // Exploration + EventMission = 5, + GuildQuest = 6, + MentoringMission = 7, + FieldQuest = 8, + AllianceQuest = 9, + WeddingMission = 10, +} + +public enum QuestState { + None = 0, + Started = 1, + Completed = 2, +} + +public enum QuestRemoteType { + None = 0, + Cinematic = 1, + Popup = 2, + System = 3, +} + +public enum QuestDispatchType { + None, + MonologueAccept, + MonologueComplete, + DirectAccept, + DirectComplete, +} + +public enum QuestEventMissionType { + none, + gallery, + levelup_package, + stamp, + timerun, +} diff --git a/Maple2.Model/Enum/Reputation.cs b/Maple2.Model/Enum/Reputation.cs index ffd29dcc7..fa94e71da 100644 --- a/Maple2.Model/Enum/Reputation.cs +++ b/Maple2.Model/Enum/Reputation.cs @@ -1,14 +1,14 @@ -namespace Maple2.Model.Enum; - -public enum ReputationType : short { - None = 0, - GreenHood = 1, - Lumiknight = 2, - DarkWind = 3, - RoyalGuard = 4, - MapleAlliance = 5, - KritiasLumiknight = 6, // may be wrong - KritiasMapleAlliance = 7, // may be wrong - KritiasGreenHood = 8, - Humanitas = 9, -} +namespace Maple2.Model.Enum; + +public enum ReputationType : short { + None = 0, + GreenHood = 1, + Lumiknight = 2, + DarkWind = 3, + RoyalGuard = 4, + MapleAlliance = 5, + KritiasLumiknight = 6, // may be wrong + KritiasMapleAlliance = 7, // may be wrong + KritiasGreenHood = 8, + Humanitas = 9, +} diff --git a/Maple2.Model/Enum/ResetType.cs b/Maple2.Model/Enum/ResetType.cs index 16a3659c5..959384475 100644 --- a/Maple2.Model/Enum/ResetType.cs +++ b/Maple2.Model/Enum/ResetType.cs @@ -1,9 +1,9 @@ -namespace Maple2.Model.Enum; - -public enum ResetType : byte { - Default = 0, - Day = 1, - Week = 2, - Month = 3, - Unlimited = 99, -} +namespace Maple2.Model.Enum; + +public enum ResetType : byte { + Default = 0, + Day = 1, + Week = 2, + Month = 3, + Unlimited = 99, +} diff --git a/Maple2.Model/Enum/RideType.cs b/Maple2.Model/Enum/RideType.cs index feba9f414..3307ab084 100644 --- a/Maple2.Model/Enum/RideType.cs +++ b/Maple2.Model/Enum/RideType.cs @@ -1,38 +1,38 @@ -namespace Maple2.Model.Enum; - -public enum RideOnType : byte { - None = 0, - Default = 1, - Battle = 2, - Object = 3, -} - -public enum RideOffType : byte { - Default = 0, - UseSkill = 1, - Interact = 2, - Taxi = 3, - CashCall = 4, - BeautyShop = 5, - TakeLr = 6, - Hold = 7, - Recall = 8, - SummonPetOn = 9, - SummonPetTransfer = 10, - HomeConvenient = 11, - DisableField = 12, - Dead = 13, - AdditionalEffect = 14, - RidingUi = 15, - Homemade = 16, - AutoInteraction = 17, - AutoClimb = 18, - CoupleEmotion = 19, - React = 20, - UseFunctionItem = 21, - Nurturing = 22, - Groggy = 23, - UnRideSkill = 24, - UseGlideItem = 25, - HideAndSeek = 26, -} +namespace Maple2.Model.Enum; + +public enum RideOnType : byte { + None = 0, + Default = 1, + Battle = 2, + Object = 3, +} + +public enum RideOffType : byte { + Default = 0, + UseSkill = 1, + Interact = 2, + Taxi = 3, + CashCall = 4, + BeautyShop = 5, + TakeLr = 6, + Hold = 7, + Recall = 8, + SummonPetOn = 9, + SummonPetTransfer = 10, + HomeConvenient = 11, + DisableField = 12, + Dead = 13, + AdditionalEffect = 14, + RidingUi = 15, + Homemade = 16, + AutoInteraction = 17, + AutoClimb = 18, + CoupleEmotion = 19, + React = 20, + UseFunctionItem = 21, + Nurturing = 22, + Groggy = 23, + UnRideSkill = 24, + UseGlideItem = 25, + HideAndSeek = 26, +} diff --git a/Maple2.Model/Enum/RoomTimerType.cs b/Maple2.Model/Enum/RoomTimerType.cs index 72ae2891e..31f0b1359 100644 --- a/Maple2.Model/Enum/RoomTimerType.cs +++ b/Maple2.Model/Enum/RoomTimerType.cs @@ -1,6 +1,6 @@ -namespace Maple2.Model.Enum; - -public enum RoomTimerType : byte { - Gauge, - Clock, -} +namespace Maple2.Model.Enum; + +public enum RoomTimerType : byte { + Gauge, + Clock, +} diff --git a/Maple2.Model/Enum/ScriptType.cs b/Maple2.Model/Enum/ScriptType.cs index db1629e2a..cfa1c97a1 100644 --- a/Maple2.Model/Enum/ScriptType.cs +++ b/Maple2.Model/Enum/ScriptType.cs @@ -1,14 +1,14 @@ -namespace Maple2.Model.Enum; - -public enum ScriptType { - Npc = 1, - Quest = 2, -} - -public enum ScriptStateType { - Script = 0, - Job = 1, - Select = 2, - Monologue = 3, - Quest = 4, -} +namespace Maple2.Model.Enum; + +public enum ScriptType { + Npc = 1, + Quest = 2, +} + +public enum ScriptStateType { + Script = 0, + Job = 1, + Select = 2, + Monologue = 3, + Quest = 4, +} diff --git a/Maple2.Model/Enum/SessionState.cs b/Maple2.Model/Enum/SessionState.cs index cf1d7a256..0910fcb30 100644 --- a/Maple2.Model/Enum/SessionState.cs +++ b/Maple2.Model/Enum/SessionState.cs @@ -1,8 +1,8 @@ -namespace Maple2.Model.Enum; - -public enum SessionState { - Disconnected = 0, - ChangeMap = 1, // Moving between maps - ChangeChannel = 2, // Changing channels - Connected = 3, -} +namespace Maple2.Model.Enum; + +public enum SessionState { + Disconnected = 0, + ChangeMap = 1, // Moving between maps + ChangeChannel = 2, // Changing channels + Connected = 3, +} diff --git a/Maple2.Model/Enum/Shop.cs b/Maple2.Model/Enum/Shop.cs index 3ed794fb6..e8f0e4abe 100644 --- a/Maple2.Model/Enum/Shop.cs +++ b/Maple2.Model/Enum/Shop.cs @@ -1,45 +1,45 @@ -namespace Maple2.Model.Enum; - -public enum ShopCurrencyType : byte { - Meso = 0, - Item = 1, - ValorToken = 2, - Treva = 3, - Meret = 4, - Rue = 5, - HaviFruit = 6, - GuildCoin = 7, - ReverseCoin = 8, - EventMeret = 9, - GameMeret = 10, // RedMeret - MentorToken = 11, - MenteeToken = 12, - StarPoint = 13, - MesoToken = 14, -} - -public enum ShopFrameType : byte { - Default = 0, - Unknown = 1, - Star = 2, - StyleCrate = 3, - Capsule = 4, -} - -public enum ShopItemLabel : byte { - None = 0, - New = 1, - Event = 2, - HalfPrice = 3, - Special = 4, -} - -public enum ShopBuyDay : byte { - Sunday = 1, - Monday = 2, - Tuesday = 3, - Wednesday = 4, - Thursday = 5, - Friday = 6, - Saturday = 7, -} +namespace Maple2.Model.Enum; + +public enum ShopCurrencyType : byte { + Meso = 0, + Item = 1, + ValorToken = 2, + Treva = 3, + Meret = 4, + Rue = 5, + HaviFruit = 6, + GuildCoin = 7, + ReverseCoin = 8, + EventMeret = 9, + GameMeret = 10, // RedMeret + MentorToken = 11, + MenteeToken = 12, + StarPoint = 13, + MesoToken = 14, +} + +public enum ShopFrameType : byte { + Default = 0, + Unknown = 1, + Star = 2, + StyleCrate = 3, + Capsule = 4, +} + +public enum ShopItemLabel : byte { + None = 0, + New = 1, + Event = 2, + HalfPrice = 3, + Special = 4, +} + +public enum ShopBuyDay : byte { + Sunday = 1, + Monday = 2, + Tuesday = 3, + Wednesday = 4, + Thursday = 5, + Friday = 6, + Saturday = 7, +} diff --git a/Maple2.Model/Enum/Skill.cs b/Maple2.Model/Enum/Skill.cs index 9901e5d0c..1d1af5f5e 100644 --- a/Maple2.Model/Enum/Skill.cs +++ b/Maple2.Model/Enum/Skill.cs @@ -1,218 +1,218 @@ -using System.ComponentModel; - -namespace Maple2.Model.Enum; - -public enum SkillType : byte { - [Description("s_word_skill_active: Active")] - Active = 0, - [Description("s_word_skill_passive: Passive")] - Passive = 1, - [Description("s_word_skill_move: Action")] - Action = 2, // Literally only 11000054 - [Description("")] - Consumable = 3, -} - -public enum SkillSubType : byte { - Type0 = 0, - Type1 = 1, - Type2 = 2, - Type3 = 3, - Type4 = 4, - Type5 = 5, - Type6 = 6, - Type7 = 7, - Type8 = 8, - Type9 = 9, -} - -public enum RangeType : byte { - [Description("")] - None = 0, - [Description("s_word_nearrange: Close Range")] - Melee = 1, - [Description("s_word_longrange: Long Range")] - Range = 2, - [Description("Invalid?")] - Unknown = 3, // 10200282,10200283,35001301 -} - -public enum AttackType : byte { - None = 0, - [Description("s_word_element_physics: Physical")] - Physical = 1, - [Description("s_word_magic: Magic")] - Magic = 2, - All = 3, -} - -public enum Element : byte { - None = 0, - Fire = 1, - Ice = 2, - Electric = 3, - Holy = 4, - Dark = 5, - Poison = 6, - Physical = 7, -} - -public enum SkillGroupType : byte { - None = 0, - Default = 1, - Lapenshard = 2, -} - -public enum SkillRank : byte { - Basic = 0, - Awakening = 1, - Both = 2, -} - -public enum SkillRegion { - None = 0, - Box = 1, - Cylinder = 2, - Frustum = 3, - HoleCylinder = 4, -} - -public enum SkillTargetType { - None = 0, - Owner = 1, - Target = 2, - Caster = 3, - PetOwner = 4, - Attacker = 5, - RegionBuff = 6, - RegionDebuff = 7, - RegionPet = 8, -} - -public enum ApplyTargetType { - None = 0, - Hostile = 1, - Friendly = 2, - Player1 = 3, // Unknown, - RegionBuff = 5, // Includes speed pads in Crazy Runners - RegionBuff2 = 6, // Includes Healing spots - Player4 = 7, // Unknown, Debuff (Archeon's ice bombs) - HungryMobs = 8, -} - -public enum SkillTarget { - Self = 0, - Owner = 1, - Target = 2, -} - -public enum SkillOwner { - Self = 0, // ? - Caster = 1, - RegionOwner = 5, -} - -// 1: -// 2: If you're attacked within 18 sec, grants Counterattack Chance, increasing damage by 3%. -// 4: If you're attacked within 18 sec, grants Counterattack Chance, increasing damage by 3%. -// 5: Casting Flame Tornado temporarily grants Flame Imp. -// 6: The eagle's majesty inspires. Restores 1 spirit every sec. The eagle also attacks on hit. -// 7: Blocking an attack has a 40% chance to grant Counterattack Chance, increasing all damage by 3% for 18 sec. -// 10: At 5 stacks, activates ultimate curse. -// 11: Greatly increases movement speed for 3 sec when investigating nearby objects. -// 13: Can be used while Meridian Flow III is active.\n\nDeals 1668% damage and removes Meridian Flow III. -// 14: 10700021, Empowered while Cunning is active. -// Grants a Deflector Shield that absorbs damage equal to 30% of your max health. -// 16: 10200261, Triggers Death Rattle when one of the following conditions is met: (Conditions: 4, 6, 16) -// - You use a skill that consumes Dark Aura while it is at 10 stacks (16) -// - You are surrounded by 8 or more enemies (6) -// - Your health is reduced to 10% or below. (4) -// 17: EventEffectId -// [Bonus Effects]\nConsume 1 Awakened Mantra Core to instantly charge to 5.\nUse Vision Torrent to turn this skill into Vision Spirit Burn.\nSoul Exhaustion prevents the use of Spirit Burn for 10 sec.\nVision Amplification increases magic damage by 12% when Vision Torrent is active. -// -// -// 18: 90051156, Increases movement speed for 10 sec after Mining, Foraging, Ranching, or Farming. -// 19: 61100139, Defense +2% for 10 sec when owner's attack misses -// 20: EventSkillId -// 102: 10300271, Gains a stack when Frost is inflicted. -// 103: 11000266, Deals another hit for 500% damage after hitting a target all 9 times. - -// 1: -// 2: -// 4: Player Attacked -// 5: Skill Casted? (See: InvokeEffectProperty) -// 6: Enemies Nearby -// 7: Blocked Attack -// 10: Buff Stacks -// 11: Investigating Objects -// 13: -// 14: -// 16: -// 17: -// 18: Life Skills (Mining, Foraging, Ranching, or Farming) -// 19: Attack Misses -// 20: -// 102: Frost Inflicted (10300271) -// 103: Target Hit All 9x (11000266) -// ----- -// All: 1,2,4,5,6,7,10,11,13,14,16,17,18,19,20,102,103 -// IgnoreOwner => 1,2,4,5,6,7,10,16,17,18 -// SkillIds => 4,6,7,14,20 -// BuffIds => 16,17,102 -public enum EventConditionType { - Activate = 0, // always 0 in skills - Tick = 0, - OnEvade = 1, // owner - OnBlock = 2, // owner - OnAttacked = 4, // owner, target - OnOwnerAttackCrit = 5, // owner - OnOwnerAttackHit = 6, // owner - OnSkillCasted = 7, // owner, caster - - OnBuffStacksReached = 10, // owner, caster - OnInvestigate = 11, // owner. not fired in homes - OnDeath = 13, // owner - OnSkillCastEnd = 14, // owner. unsure - OnEffectApplied = 16, // owner - OnEffectRemoved = 17, // owner - OnLifeSkillGather = 18, // owner - OnAttackMiss = 19, // owner, - OnEmote = 20, // owner - UnknownWizardEvent = 102, - UnknownStrikerEvent = 103, // owner -} - -public enum CompulsionType { - None = 0, - Hit = 1, - Critical = 2, - Interrupt = 3, // unconfirmed -} - -public enum TargetType { - Self = 0, // used on skill attacks whos sole purpose is deploying a region skill/self buff - Hostile = 1, - Friendly = 2, - Player1 = 3, // Unknown, - Player2 = 5, // Unknown, - Player3 = 6, // Unknown, Recovery - Player4 = 7, // Unknown, Debuff (Archeon's ice bombs) - - HungryMobs = 8, -} - -public enum BounceType { - None = 0, - Range = 1, // within range - Chain = 2, // Bounce continues as long as another entity is in range of the last bounce - Pierce = 3, // Linear - Boomerang = 4, // Shield Toss, Shadow Cutter - Unknown5 = 5, -} - -[Flags] -public enum SuperArmor { - None = 0, - StunImmunity = 1, - KnockbackImmunity = 2, -} +using System.ComponentModel; + +namespace Maple2.Model.Enum; + +public enum SkillType : byte { + [Description("s_word_skill_active: Active")] + Active = 0, + [Description("s_word_skill_passive: Passive")] + Passive = 1, + [Description("s_word_skill_move: Action")] + Action = 2, // Literally only 11000054 + [Description("")] + Consumable = 3, +} + +public enum SkillSubType : byte { + Type0 = 0, + Type1 = 1, + Type2 = 2, + Type3 = 3, + Type4 = 4, + Type5 = 5, + Type6 = 6, + Type7 = 7, + Type8 = 8, + Type9 = 9, +} + +public enum RangeType : byte { + [Description("")] + None = 0, + [Description("s_word_nearrange: Close Range")] + Melee = 1, + [Description("s_word_longrange: Long Range")] + Range = 2, + [Description("Invalid?")] + Unknown = 3, // 10200282,10200283,35001301 +} + +public enum AttackType : byte { + None = 0, + [Description("s_word_element_physics: Physical")] + Physical = 1, + [Description("s_word_magic: Magic")] + Magic = 2, + All = 3, +} + +public enum Element : byte { + None = 0, + Fire = 1, + Ice = 2, + Electric = 3, + Holy = 4, + Dark = 5, + Poison = 6, + Physical = 7, +} + +public enum SkillGroupType : byte { + None = 0, + Default = 1, + Lapenshard = 2, +} + +public enum SkillRank : byte { + Basic = 0, + Awakening = 1, + Both = 2, +} + +public enum SkillRegion { + None = 0, + Box = 1, + Cylinder = 2, + Frustum = 3, + HoleCylinder = 4, +} + +public enum SkillTargetType { + None = 0, + Owner = 1, + Target = 2, + Caster = 3, + PetOwner = 4, + Attacker = 5, + RegionBuff = 6, + RegionDebuff = 7, + RegionPet = 8, +} + +public enum ApplyTargetType { + None = 0, + Hostile = 1, + Friendly = 2, + Player1 = 3, // Unknown, + RegionBuff = 5, // Includes speed pads in Crazy Runners + RegionBuff2 = 6, // Includes Healing spots + Player4 = 7, // Unknown, Debuff (Archeon's ice bombs) + HungryMobs = 8, +} + +public enum SkillTarget { + Self = 0, + Owner = 1, + Target = 2, +} + +public enum SkillOwner { + Self = 0, // ? + Caster = 1, + RegionOwner = 5, +} + +// 1: +// 2: If you're attacked within 18 sec, grants Counterattack Chance, increasing damage by 3%. +// 4: If you're attacked within 18 sec, grants Counterattack Chance, increasing damage by 3%. +// 5: Casting Flame Tornado temporarily grants Flame Imp. +// 6: The eagle's majesty inspires. Restores 1 spirit every sec. The eagle also attacks on hit. +// 7: Blocking an attack has a 40% chance to grant Counterattack Chance, increasing all damage by 3% for 18 sec. +// 10: At 5 stacks, activates ultimate curse. +// 11: Greatly increases movement speed for 3 sec when investigating nearby objects. +// 13: Can be used while Meridian Flow III is active.\n\nDeals 1668% damage and removes Meridian Flow III. +// 14: 10700021, Empowered while Cunning is active. +// Grants a Deflector Shield that absorbs damage equal to 30% of your max health. +// 16: 10200261, Triggers Death Rattle when one of the following conditions is met: (Conditions: 4, 6, 16) +// - You use a skill that consumes Dark Aura while it is at 10 stacks (16) +// - You are surrounded by 8 or more enemies (6) +// - Your health is reduced to 10% or below. (4) +// 17: EventEffectId +// [Bonus Effects]\nConsume 1 Awakened Mantra Core to instantly charge to 5.\nUse Vision Torrent to turn this skill into Vision Spirit Burn.\nSoul Exhaustion prevents the use of Spirit Burn for 10 sec.\nVision Amplification increases magic damage by 12% when Vision Torrent is active. +// +// +// 18: 90051156, Increases movement speed for 10 sec after Mining, Foraging, Ranching, or Farming. +// 19: 61100139, Defense +2% for 10 sec when owner's attack misses +// 20: EventSkillId +// 102: 10300271, Gains a stack when Frost is inflicted. +// 103: 11000266, Deals another hit for 500% damage after hitting a target all 9 times. + +// 1: +// 2: +// 4: Player Attacked +// 5: Skill Casted? (See: InvokeEffectProperty) +// 6: Enemies Nearby +// 7: Blocked Attack +// 10: Buff Stacks +// 11: Investigating Objects +// 13: +// 14: +// 16: +// 17: +// 18: Life Skills (Mining, Foraging, Ranching, or Farming) +// 19: Attack Misses +// 20: +// 102: Frost Inflicted (10300271) +// 103: Target Hit All 9x (11000266) +// ----- +// All: 1,2,4,5,6,7,10,11,13,14,16,17,18,19,20,102,103 +// IgnoreOwner => 1,2,4,5,6,7,10,16,17,18 +// SkillIds => 4,6,7,14,20 +// BuffIds => 16,17,102 +public enum EventConditionType { + Activate = 0, // always 0 in skills + Tick = 0, + OnEvade = 1, // owner + OnBlock = 2, // owner + OnAttacked = 4, // owner, target + OnOwnerAttackCrit = 5, // owner + OnOwnerAttackHit = 6, // owner + OnSkillCasted = 7, // owner, caster + + OnBuffStacksReached = 10, // owner, caster + OnInvestigate = 11, // owner. not fired in homes + OnDeath = 13, // owner + OnSkillCastEnd = 14, // owner. unsure + OnEffectApplied = 16, // owner + OnEffectRemoved = 17, // owner + OnLifeSkillGather = 18, // owner + OnAttackMiss = 19, // owner, + OnEmote = 20, // owner + UnknownWizardEvent = 102, + UnknownStrikerEvent = 103, // owner +} + +public enum CompulsionType { + None = 0, + Hit = 1, + Critical = 2, + Interrupt = 3, // unconfirmed +} + +public enum TargetType { + Self = 0, // used on skill attacks whos sole purpose is deploying a region skill/self buff + Hostile = 1, + Friendly = 2, + Player1 = 3, // Unknown, + Player2 = 5, // Unknown, + Player3 = 6, // Unknown, Recovery + Player4 = 7, // Unknown, Debuff (Archeon's ice bombs) + + HungryMobs = 8, +} + +public enum BounceType { + None = 0, + Range = 1, // within range + Chain = 2, // Bounce continues as long as another entity is in range of the last bounce + Pierce = 3, // Linear + Boomerang = 4, // Shield Toss, Shadow Cutter + Unknown5 = 5, +} + +[Flags] +public enum SuperArmor { + None = 0, + StunImmunity = 1, + KnockbackImmunity = 2, +} diff --git a/Maple2.Model/Enum/SkillPointSource.cs b/Maple2.Model/Enum/SkillPointSource.cs index 2ffff4358..7cc1ff3da 100644 --- a/Maple2.Model/Enum/SkillPointSource.cs +++ b/Maple2.Model/Enum/SkillPointSource.cs @@ -1,7 +1,7 @@ -namespace Maple2.Model.Enum; - -public enum SkillPointSource { - Trophy = 1, - Chapter = 2, - Unknown = 3, -} +namespace Maple2.Model.Enum; + +public enum SkillPointSource { + Trophy = 1, + Chapter = 2, + Unknown = 3, +} diff --git a/Maple2.Model/Enum/SmartPushType.cs b/Maple2.Model/Enum/SmartPushType.cs index 3c453af97..2316e31b7 100644 --- a/Maple2.Model/Enum/SmartPushType.cs +++ b/Maple2.Model/Enum/SmartPushType.cs @@ -1,7 +1,7 @@ -namespace Maple2.Model.Enum; - -public enum SmartPushType { - none, - additionalEffect, - autoInteraction, -} +namespace Maple2.Model.Enum; + +public enum SmartPushType { + none, + additionalEffect, + autoInteraction, +} diff --git a/Maple2.Model/Enum/SpecialAttribute.cs b/Maple2.Model/Enum/SpecialAttribute.cs index 402e528a0..81622d765 100644 --- a/Maple2.Model/Enum/SpecialAttribute.cs +++ b/Maple2.Model/Enum/SpecialAttribute.cs @@ -1,365 +1,365 @@ -using System.ComponentModel; - -namespace Maple2.Model.Enum; - -public enum SpecialAttribute : byte { - None = 0, - [Description("s_item_opt_sa_improve_acquire_exp: Experience from Enemies")] - Experience = 1, - [Description("s_item_opt_sa_improve_acquire_meso: Mesos from Monsters")] - Meso = 2, - [Description("s_item_opt_sa_improve_speed_swim: Swim Speed")] - SwimSpeed = 3, - [Description("s_item_opt_sa_improve_speed_dash: Dash Distance")] - DashDistance = 4, - [Description("s_item_opt_sa_improve_acquire_potion: Tonic Drop Rate")] - TonicDropRate = 5, - [Description("s_item_opt_sa_improve_acquire_equipment: Gear Drop Rate")] - GearDropRate = 6, - [Description("s_item_opt_sa_improve_damage_final: Total Damage Bonus")] - TotalDamage = 7, - [Description("s_item_opt_sa_improve_damage_critical: Critical Damage")] - CriticalDamage = 8, - [Description("s_item_opt_sa_improve_damage_normalNpc: Common Monster Damage")] - NormalNpcDamage = 9, - [Description("s_item_opt_sa_improve_damage_leaderNpc: Leader Monster Damage")] - LeaderNpcDamage = 10, - [Description("s_item_opt_sa_improve_damage_namedNpc: Elite Monster Damage")] - EliteNpcDamage = 11, - [Description("s_item_opt_sa_improve_damage_bossNpc: Bonus Boss Damage")] - BossNpcDamage = 12, - [Description("s_item_opt_sa_improve_recovery_hp_dokill: Heal from Beating Enemies")] - HpOnKill = 13, - [Description("s_item_opt_sa_improve_recovery_sp_dokill: Spirit from Beating Enemies")] - SpiritOnKill = 14, - [Description("s_item_opt_sa_improve_recovery_ep_dokill: Stamina from Beating Enemies")] - StaminaOnKill = 15, - [Description("s_item_opt_sa_improve_recovery_regen_doheal: Recovery Bonus")] - RecoveryBonus = 16, - [Description("s_item_opt_sa_improve_recovery_regen_receiveheal: Bonus Recovery from Allies")] - BonusRecoveryFromAlly = 17, - [Description("s_item_opt_sa_improve_elements_ice: Ice Damage Bonus")] - IceDamage = 18, - [Description("s_item_opt_sa_improve_elements_fire: Fire Damage Bonus")] - FireDamage = 19, - [Description("s_item_opt_sa_improve_elements_dark: Dark Damage Bonus")] - DarkDamage = 20, - [Description("s_item_opt_sa_improve_elements_light: Holy Damage Bonus")] - HolyDamage = 21, - [Description("s_item_opt_sa_improve_elements_poison: Poison Damage Bonus")] - PoisonDamage = 22, - [Description("s_item_opt_sa_improve_elements_thunder: Electric Damage Bonus")] - ElectricDamage = 23, - [Description("s_item_opt_sa_improve_damage_nearrange: Melee Damage Bonus")] - MeleeDamage = 24, - [Description("s_item_opt_sa_improve_damage_longrange: Ranged Damage Bonus")] - RangedDamage = 25, - [Description("s_item_opt_sa_improve_piercing_par: Physical Piercing")] - PhysicalPiercing = 26, - [Description("s_item_opt_sa_improve_piercing_mar: Magic Piercing")] - MagicalPiercing = 27, - [Description("s_item_opt_sa_reduce_elements_ice: Ice Damage Reduction")] - ReduceIceDamage = 28, - [Description("s_item_opt_sa_reduce_elements_fire: Fire Damage Reduction")] - ReduceFireDamage = 29, - [Description("s_item_opt_sa_reduce_elements_dark: Dark Damage Reduction")] - ReduceDarkDamage = 30, - [Description("s_item_opt_sa_reduce_elements_light: Holy Damage Reduction")] - ReduceHolyDamage = 31, - [Description("s_item_opt_sa_reduce_elements_poison: Poison Damage Reduction")] - ReducePoisonDamage = 32, - [Description("s_item_opt_sa_reduce_elements_thunder: Electric Damage Reduction")] - ReduceElectricDamage = 33, - [Description("s_item_opt_sa_reduce_time_stun: Stun Duration Reduction")] - ReduceStun = 34, - [Description("s_item_opt_sa_reduce_time_cooldown: Skill Cooldown Reduction")] - ReduceCooldown = 35, - [Description("s_item_opt_sa_reduce_time_condition: Debuff Duration Reduction")] - ReduceDebuff = 36, - [Description("s_item_opt_sa_reduce_damage_nearrange: Melee Reduction")] - ReduceMeleeDamage = 37, - [Description("s_item_opt_sa_reduce_damage_longrange: Ranged Damage Reduction")] - ReduceRangedDamage = 38, - [Description("s_item_opt_sa_reduce_distance_knockBack: Knockback Distance Reduction")] - ReduceKnockBack = 39, - [Description("s_item_opt_sa_probability_stun_nearrange: Melee Attacks Stun")] - MeleeStun = 40, - [Description("s_item_opt_sa_probability_stun_longrange: Ranged Attacks Stun")] - RangedStun = 41, - [Description("s_item_opt_sa_probability_knockback_nearrange: Melee Attacks Gain Knockback")] - MeleeKnockBack = 42, - [Description("s_item_opt_sa_probability_knockback_longrange: Ranged Attacks Gain Knockback")] - RangedKnockBack = 43, - [Description("s_item_opt_sa_probability_cannotmove_nearrange: Melee Attacks Immobilize Target")] - MeleeImmobilize = 44, - [Description("s_item_opt_sa_probability_cannotmove_longrange: Ranged Attacks Immobilize Target")] - RangedImmobilize = 45, - [Description("s_item_opt_sa_probability_splashdamage_nearrange: Melee Attacks Damage in Area")] - MeleeSplashDamage = 46, - [Description("s_item_opt_sa_probability_splashdamage_longrange: Ranged Attacks Damage in Area")] - RangedSplashDamage = 47, - [Description("s_item_opt_sa_improve_npckill_dropitem_incrate: Enemy Item Drop Chance")] - DropRate = 48, - [Description("s_item_opt_sa_improve_acquire_questreward_exp: Experience from Quests")] - QuestExp = 49, - [Description("s_item_opt_sa_improve_acquire_questreward_meso: Mesos from Quests")] - QuestMeso = 50, - [Description("")] - InvokeEffect1 = 51, - [Description("")] - InvokeEffect2 = 52, - [Description("")] - InvokeEffect3 = 53, - [Description("s_item_opt_sa_improve_damage_pvp: PvP Damage")] - PvpDamage = 54, - [Description("s_item_opt_sa_reduce_damage_pvp: PvP Defense")] - ReducePvpDamage = 55, - [Description("s_item_opt_sa_improve_guild_exp: Guild Experience")] - GuildExp = 56, - [Description("s_item_opt_sa_improve_guild_coin: Guild Coins")] - GuildCoin = 57, - [Description("s_item_opt_sa_improve_massive_event_expball: Experience Orb Drop Rate")] - MassiveEventExpBall = 58, - [Description("s_item_opt_sa_improve_acquire_fishing_exp: Experience from Fishing")] - FishingExp = 59, - [Description("s_item_opt_sa_improve_acquire_arcade_exp: Arcade Clear Experience")] - ArcadeExp = 60, - [Description("s_item_opt_sa_improve_acquire_playinstrument_exp: Experience from Performance")] - PlayInstrumentExp = 61, - [Description("s_item_opt_sa_improve_maid_mood: Assistant Experience")] - MaidExp = 62, - [Description("s_item_opt_sa_reduce_maid_recipe: Assistant Craft Material Discount")] - ReduceMaidRecipe = 63, - [Description("s_item_opt_sa_reduce_meso_trade_fee: Meso Handling Fee Discount")] - ReduceMesoTradeFee = 64, - [Description("s_item_opt_sa_reduce_enchant_material_fee: Enchant Material Discount")] - ReduceEnchantMaterialFee = 65, - [Description("s_item_opt_sa_reduce_merat_revival_fee: Meret Revive Discount")] - ReduceMeretRevivalFee = 66, - [Description("s_item_opt_sa_improve_mining_reward_item: Mining Quantity Earned")] - MiningRewardItem = 67, - [Description("s_item_opt_sa_improve_breeding_reward_item: Ranching Quantity Earned")] - BreedingRewardItem = 68, - [Description("s_item_opt_sa_improve_blacksmithing_reward_mastery: Smithing Experience Earned")] - SmithingRewardMastery = 69, - [Description("s_item_opt_sa_improve_engraving_reward_mastery: Handicraft Mastery Earned")] - EngravingRewardMastery = 70, - [Description("s_item_opt_sa_improve_gathering_reward_item: Foraging Quantity Earned")] - GatheringRewardItem = 71, - [Description("s_item_opt_sa_improve_farming_reward_item: Farming Quantity Earned")] - FarmingRewardItem = 72, - [Description("s_item_opt_sa_improve_alchemist_reward_mastery: Alchemy Mastery Earned")] - AlchemistRewardMastery = 73, - [Description("s_item_opt_sa_improve_cooking_reward_mastery: Cooking Mastery Earned")] - CookingRewardMastery = 74, - [Description("s_item_opt_sa_improve_acquire_gathering_exp: Experience from Foraging")] - AcquireGatheringExp = 75, - [Description("s_item_opt_sa_improve_acquire_manufacturing_exp: Experience from Crafting")] - AcquireManufacturingExp = 76, - [Description("s_item_opt_sa_skill_levelup_tier_1: Lv. 1 (1) Acquired Skill Level")] - SkillLevelUpTier1 = 77, - [Description("s_item_opt_sa_skill_levelup_tier_2: Lv. 1 (2) Acquired Skill Level")] - SkillLevelUpTier2 = 78, - [Description("s_item_opt_sa_skill_levelup_tier_3: Lv. 10 Acquired Skill Level")] - SkillLevelUpTier3 = 79, - [Description("s_item_opt_sa_skill_levelup_tier_4: Lv. 13 Acquired Skill Level")] - SkillLevelUpTier4 = 80, - [Description("s_item_opt_sa_skill_levelup_tier_5: Lv. 16 Acquired Skill Level")] - SkillLevelUpTier5 = 81, - [Description("s_item_opt_sa_skill_levelup_tier_6: Lv. 19 Acquired Skill Level")] - SkillLevelUpTier6 = 82, - [Description("s_item_opt_sa_skill_levelup_tier_7: Lv. 22 Acquired Skill Level")] - SkillLevelUpTier7 = 83, - [Description("s_item_opt_sa_skill_levelup_tier_8: Lv. 25 Acquired Skill Level")] - SkillLevelUpTier8 = 84, - [Description("s_item_opt_sa_skill_levelup_tier_9: Lv. 28 Acquired Skill Level")] - SkillLevelUpTier9 = 85, - [Description("s_item_opt_sa_skill_levelup_tier_10: Lv. 31 Acquired Skill Level")] - SkillLevelUpTier10 = 86, - [Description("s_item_opt_sa_skill_levelup_tier_11: Lv. 34 Acquired Skill Level")] - SkillLevelUpTier11 = 87, - [Description("s_item_opt_sa_skill_levelup_tier_12: Lv. 37 Acquired Skill Level")] - SkillLevelUpTier12 = 88, - [Description("s_item_opt_sa_skill_levelup_tier_13: Lv. 40 Acquired Skill Level")] - SkillLevelUpTier13 = 89, - [Description("s_item_opt_sa_skill_levelup_tier_14: Lv. 43 Acquired Skill Level")] - SkillLevelUpTier14 = 90, - [Description("s_item_opt_sa_improve_massive_ox_exp: OX Quiz Experience")] - MassiveOxExp = 91, - [Description("s_item_opt_sa_improve_massive_trapmaster_exp: Trap Master Experience")] - MassiveTrapMasterExp = 92, - [Description("s_item_opt_sa_improve_massive_finalsurvival_exp: Sole Survivor Experience")] - MassiveFinalSurvivalExp = 93, - [Description("s_item_opt_sa_improve_massive_crazyrunner_exp: Crazy Runners Experience")] - MassiveCrazyRunnerExp = 94, - [Description("s_item_opt_sa_improve_massive_escape_exp: Ludibrium Escape Experience")] - MassiveEscapeExp = 95, - [Description("s_item_opt_sa_improve_massive_springbeach_exp: Spring Beach Experience")] - MassiveSpringBeachExp = 96, - [Description("s_item_opt_sa_improve_massive_dancedance_exp: Dance Dance Stop Experience")] - MassiveDanceDanceExp = 97, - [Description("s_item_opt_sa_improve_massive_ox_msp: OX Quiz Movement Speed")] - MassiveOxSpeed = 98, - [Description("s_item_opt_sa_improve_massive_trapmaster_msp: Trap Master Movement Speed")] - MassiveTrapMasterSpeed = 99, - [Description("s_item_opt_sa_improve_massive_finalsurvival_msp: Sole Survivor Movement Speed")] - MassiveFinalSurvivalSpeed = 100, - [Description("s_item_opt_sa_improve_massive_crazyrunner_msp: Crazy Runners Movement Speed")] - MassiveCrazyRunnerSpeed = 101, - [Description("s_item_opt_sa_improve_massive_escape_msp: Ludibrium Escape Movement Speed")] - MassiveEscapeSpeed = 102, - [Description("s_item_opt_sa_improve_massive_springbeach_msp: Spring Beach Movement Speed")] - MassiveSpringBeachSpeed = 103, - [Description("s_item_opt_sa_improve_massive_dancedance_msp: Dance Dance Stop Movement Speed")] - MassiveDanceDanceSpeed = 104, - [Description("s_item_opt_sa_npc_hit_reward_sp_ball: Chance to Generate Spirit Orbs on Attack")] - NpcHitRewardSpBall = 105, - [Description("s_item_opt_sa_npc_hit_reward_ep_ball: Chance to Generate Stamina Orbs on Attack")] - NpcHitRewardEpBall = 106, - [Description("s_item_opt_sa_improve_honor_token: Valor Tokens")] - HonorToken = 107, - [Description("s_item_opt_sa_improve_pvp_exp: PvP Experience")] - PvpExp = 108, - [Description("s_item_opt_sa_improve_darkstream_damage: Dark Descent Damage Bonus")] - DarkStreamDamage = 109, - [Description("s_item_opt_sa_reduce_darkstream_recive_damage: Decreases Damage in the Dark Descent")] - ReduceDarkStreamReceiveDamage = 110, - [Description("s_item_opt_sa_improve_darkstream_evp: Dark Descent Evasion")] - DarkStreamEvp = 111, - [Description("s_item_opt_sa_fishing_double_mastery: Chance for 2x Fishing Mastery")] - FishingDoubleMastery = 112, - [Description("s_item_opt_sa_playinstrument_double_mastery: Chance for 2x Performance Mastery")] - PlayInstrumentDoubleMastery = 113, - [Description("s_item_opt_sa_complete_fieldmission_msp: Movement Speed in Explored Areas")] - CompleteFieldMissionSpeed = 114, - [Description("s_item_opt_sa_improve_glide_vertical_velocity: Air Mount Ascent Speed")] - GlideVerticalVelocity = 115, - [Description("s_item_opt_sa_additionaleffect_95000018: Being fixed")] - AdditionalEffect95000018 = 116, - [Description("s_item_opt_sa_additionaleffect_95000012: Enemy Defense on Hit")] - AdditionalEffect95000012 = 117, - [Description("s_item_opt_sa_additionaleffect_95000014: Enemy Attack on Hit")] - AdditionalEffect95000014 = 118, - [Description("s_item_opt_sa_additionaleffect_95000020: Total Damage when Enemy within 5m")] - AdditionalEffect95000020 = 119, - [Description("s_item_opt_sa_additionaleffect_95000021: Total Damage when 3 Enemies within 5m")] - AdditionalEffect95000021 = 120, - [Description("s_item_opt_sa_additionaleffect_95000022: Total Damage when Spirit Is 80 or More")] - AdditionalEffect95000022 = 121, - [Description("s_item_opt_sa_additionaleffect_95000023: Total Damage when Stamina Full")] - AdditionalEffect95000023 = 122, - [Description("s_item_opt_sa_additionaleffect_95000024: Total Damage when Herb Effects Active")] - AdditionalEffect95000024 = 123, - [Description("s_item_opt_sa_additionaleffect_95000025: World Boss Damage")] - AdditionalEffect95000025 = 124, - [Description("s_item_opt_sa_additionaleffect_95000026: 95000026")] - AdditionalEffect95000026 = 125, - [Description("s_item_opt_sa_additionaleffect_95000027: 95000027")] - AdditionalEffect95000027 = 126, - [Description("s_item_opt_sa_additionaleffect_95000028: 95000028")] - AdditionalEffect95000028 = 127, - [Description("s_item_opt_sa_additionaleffect_95000029: 95000029")] - AdditionalEffect95000029 = 128, - [Description("s_item_opt_sa_reduce_recovery_ep_inv: Stamina Recovery Speed")] - ReduceRecoveryEpInv = 129, - [Description("s_item_opt_sa_improve_stat_wap_u: Maximum Weapon Attack")] - MaxWeaponAttack = 130, - [Description("s_item_opt_sa_mining_double_reward: Chance for 2x Mining Production")] - MiningDoubleReward = 131, - [Description("s_item_opt_sa_breeding_double_reward: Chance for 2x Ranching Production")] - BreedingDoubleReward = 132, - [Description("s_item_opt_sa_gathering_double_reward: Chance for 2x Foraging Production")] - GatheringDoubleReward = 133, - [Description("s_item_opt_sa_farming_double_reward: Chance for 2x Farming Production")] - FarmingDoubleReward = 134, - [Description("s_item_opt_sa_blacksmithing_double_reward: Chance for 2x Smithing Production")] - SmithingDoubleReward = 135, - [Description("s_item_opt_sa_engraving_double_reward: Chance for 2x Handicraft Production")] - EngravingDoubleReward = 136, - [Description("s_item_opt_sa_alchemist_double_reward: Chance for 2x Alchemy Production")] - AlchemistDoubleReward = 137, - [Description("s_item_opt_sa_cooking_double_reward: Chance for 2x Cooking Production")] - CookingDoubleReward = 138, - [Description("s_item_opt_sa_mining_double_mastery: Chance for 2x Mining Mastery")] - MiningDoubleMastery = 139, - [Description("s_item_opt_sa_breeding_double_mastery: Chance for 2x Ranching Mastery")] - BreedingDoubleMastery = 140, - [Description("s_item_opt_sa_gathering_double_mastery: Chance for 2x Foraging Mastery")] - GatheringDoubleMastery = 141, - [Description("s_item_opt_sa_farming_double_mastery: Chance for 2x Farming Mastery")] - FarmingDoubleMastery = 142, - [Description("s_item_opt_sa_blacksmithing_double_mastery: Chance for 2x Smithing Mastery")] - SmithingDoubleMastery = 143, - [Description("s_item_opt_sa_engraving_double_mastery: Chance for 2x Handicraft Mastery")] - EngravingDoubleMastery = 144, - [Description("s_item_opt_sa_alchemist_double_mastery: Chance for 2x Alchemy Mastery")] - AlchemistDoubleMastery = 145, - [Description("s_item_opt_sa_cooking_double_mastery: Chance for 2x Cooking Mastery")] - CookingDoubleMastery = 146, - [Description("s_item_opt_sa_improve_chaosraid_wap: Weapon Attack in Chaos Raids")] - ChaosRaidAttack = 147, - [Description("s_item_opt_sa_improve_chaosraid_asp: Attack Speed in Chaos Raids")] - ChaosRaidAttackSpeed = 148, - [Description("s_item_opt_sa_improve_chaosraid_atp: Accuracy in Chaos Raids")] - ChaosRaidAccuracy = 149, - [Description("s_item_opt_sa_improve_chaosraid_hp: Health in Chaos Raids")] - ChaosRaidHp = 150, - [Description("s_item_opt_sa_improve_recovery_ball: Stamina and Spirit from Orbs")] - RecoveryBall = 151, - [Description("s_item_opt_sa_improve_fieldboss_kill_exp: World Boss Experience")] - FieldBossExp = 152, - [Description("s_item_opt_sa_improve_fieldboss_kill_drop: World Boss Item Drop Rate")] - FieldBossDropRate = 153, - [Description("s_item_opt_sa_reduce_fieldboss_recive_damage: Reduced Damage from World Bosses")] - ReduceFieldBossReceiveDamage = 154, - [Description("s_item_opt_sa_additionaleffect_95000016: 95000016")] - AdditionalEffect95000016 = 155, - [Description("s_item_opt_sa_improve_pettrap_reward: Max Pet Capture Reward Count")] - PetTrapReward = 156, - [Description("s_item_opt_sa_ming_multiaction: Mining Efficiency")] - MiningEfficiency = 157, - [Description("s_item_opt_sa_breeding_multiaction: Ranching Efficiency")] - BreedingEfficiency = 158, - [Description("s_item_opt_sa_gathering_multiaction: Foraging Efficiency")] - GatheringEfficiency = 159, - [Description("s_item_opt_sa_farming_multiaction: Farming Efficiency")] - FarmingEfficiency = 160, - [Description("s_item_opt_sa_improve_massive_sh_crazyrunner_exp: Shanghai Crazy Runners Experience")] - MassiveShCrazyRunnerExp = 161, - [Description("s_item_opt_sa_improve_massive_sh_crazyrunner_msp: Shanghai Crazy Runners Movement Speed")] - MassiveShCrazyRunnerSpeed = 162, - [Description("s_item_opt_sa_reduce_damage_by_targetmaxhp: Health-Based Damage Reduction")] - ReduceDamageByTargetMaxHp = 163, - [Description("s_item_opt_sa_reduce_meso_revival_fee")] - ReduceMesoRevivalFee = 164, - [Description("s_item_opt_sa_improve_riding_run_speed")] - RidingRunSpeed = 165, - [Description("s_item_opt_sa_improve_dungeon_reward_meso")] - DungeonRewardMeso = 166, - [Description("s_item_opt_sa_improve_shop_buying_meso")] - ShopBuyingMeso = 167, - [Description("s_item_opt_sa_improve_itembox_reward_meso")] - ItemBoxRewardMeso = 168, - [Description("s_item_opt_sa_reduce_remakeoption_fee")] - ReduceRemakeOptionFee = 169, - [Description("s_item_opt_sa_reduce_airtaxi_fee")] - ReduceAirTaxiFee = 170, - [Description("s_item_opt_sa_improve_socket_unlock_probability")] - SocketUnlockProbability = 171, - [Description("s_item_opt_sa_reduce_gemstone_upgrade_fee")] - ReduceGemstoneUpgradeFee = 172, - [Description("s_item_opt_sa_reduce_pet_remakeoption_fee")] - ReducePetRemakeOptionFee = 173, - [Description("s_item_opt_sa_improve_riding_speed")] - RidingSpeed = 174, - [Description("s_item_opt_sa_improve_survival_kill_exp")] - ImproveSurvivalKillExp = 175, - [Description("s_item_opt_sa_improve_survival_time_exp")] - ImproveSurvivalTimeExp = 176, - [Description("s_item_opt_sa_offensive_physicaldamage")] - OffensivePhysicalDamage = 177, - [Description("s_item_opt_sa_offensive_magicaldamage")] - OffensiveMagicalDamage = 178, - [Description("s_item_opt_sa_reduce_gameitem_socket_unlock_fee")] - ReduceGameItemSocketUnlockFee = 179, -} +using System.ComponentModel; + +namespace Maple2.Model.Enum; + +public enum SpecialAttribute : byte { + None = 0, + [Description("s_item_opt_sa_improve_acquire_exp: Experience from Enemies")] + Experience = 1, + [Description("s_item_opt_sa_improve_acquire_meso: Mesos from Monsters")] + Meso = 2, + [Description("s_item_opt_sa_improve_speed_swim: Swim Speed")] + SwimSpeed = 3, + [Description("s_item_opt_sa_improve_speed_dash: Dash Distance")] + DashDistance = 4, + [Description("s_item_opt_sa_improve_acquire_potion: Tonic Drop Rate")] + TonicDropRate = 5, + [Description("s_item_opt_sa_improve_acquire_equipment: Gear Drop Rate")] + GearDropRate = 6, + [Description("s_item_opt_sa_improve_damage_final: Total Damage Bonus")] + TotalDamage = 7, + [Description("s_item_opt_sa_improve_damage_critical: Critical Damage")] + CriticalDamage = 8, + [Description("s_item_opt_sa_improve_damage_normalNpc: Common Monster Damage")] + NormalNpcDamage = 9, + [Description("s_item_opt_sa_improve_damage_leaderNpc: Leader Monster Damage")] + LeaderNpcDamage = 10, + [Description("s_item_opt_sa_improve_damage_namedNpc: Elite Monster Damage")] + EliteNpcDamage = 11, + [Description("s_item_opt_sa_improve_damage_bossNpc: Bonus Boss Damage")] + BossNpcDamage = 12, + [Description("s_item_opt_sa_improve_recovery_hp_dokill: Heal from Beating Enemies")] + HpOnKill = 13, + [Description("s_item_opt_sa_improve_recovery_sp_dokill: Spirit from Beating Enemies")] + SpiritOnKill = 14, + [Description("s_item_opt_sa_improve_recovery_ep_dokill: Stamina from Beating Enemies")] + StaminaOnKill = 15, + [Description("s_item_opt_sa_improve_recovery_regen_doheal: Recovery Bonus")] + RecoveryBonus = 16, + [Description("s_item_opt_sa_improve_recovery_regen_receiveheal: Bonus Recovery from Allies")] + BonusRecoveryFromAlly = 17, + [Description("s_item_opt_sa_improve_elements_ice: Ice Damage Bonus")] + IceDamage = 18, + [Description("s_item_opt_sa_improve_elements_fire: Fire Damage Bonus")] + FireDamage = 19, + [Description("s_item_opt_sa_improve_elements_dark: Dark Damage Bonus")] + DarkDamage = 20, + [Description("s_item_opt_sa_improve_elements_light: Holy Damage Bonus")] + HolyDamage = 21, + [Description("s_item_opt_sa_improve_elements_poison: Poison Damage Bonus")] + PoisonDamage = 22, + [Description("s_item_opt_sa_improve_elements_thunder: Electric Damage Bonus")] + ElectricDamage = 23, + [Description("s_item_opt_sa_improve_damage_nearrange: Melee Damage Bonus")] + MeleeDamage = 24, + [Description("s_item_opt_sa_improve_damage_longrange: Ranged Damage Bonus")] + RangedDamage = 25, + [Description("s_item_opt_sa_improve_piercing_par: Physical Piercing")] + PhysicalPiercing = 26, + [Description("s_item_opt_sa_improve_piercing_mar: Magic Piercing")] + MagicalPiercing = 27, + [Description("s_item_opt_sa_reduce_elements_ice: Ice Damage Reduction")] + ReduceIceDamage = 28, + [Description("s_item_opt_sa_reduce_elements_fire: Fire Damage Reduction")] + ReduceFireDamage = 29, + [Description("s_item_opt_sa_reduce_elements_dark: Dark Damage Reduction")] + ReduceDarkDamage = 30, + [Description("s_item_opt_sa_reduce_elements_light: Holy Damage Reduction")] + ReduceHolyDamage = 31, + [Description("s_item_opt_sa_reduce_elements_poison: Poison Damage Reduction")] + ReducePoisonDamage = 32, + [Description("s_item_opt_sa_reduce_elements_thunder: Electric Damage Reduction")] + ReduceElectricDamage = 33, + [Description("s_item_opt_sa_reduce_time_stun: Stun Duration Reduction")] + ReduceStun = 34, + [Description("s_item_opt_sa_reduce_time_cooldown: Skill Cooldown Reduction")] + ReduceCooldown = 35, + [Description("s_item_opt_sa_reduce_time_condition: Debuff Duration Reduction")] + ReduceDebuff = 36, + [Description("s_item_opt_sa_reduce_damage_nearrange: Melee Reduction")] + ReduceMeleeDamage = 37, + [Description("s_item_opt_sa_reduce_damage_longrange: Ranged Damage Reduction")] + ReduceRangedDamage = 38, + [Description("s_item_opt_sa_reduce_distance_knockBack: Knockback Distance Reduction")] + ReduceKnockBack = 39, + [Description("s_item_opt_sa_probability_stun_nearrange: Melee Attacks Stun")] + MeleeStun = 40, + [Description("s_item_opt_sa_probability_stun_longrange: Ranged Attacks Stun")] + RangedStun = 41, + [Description("s_item_opt_sa_probability_knockback_nearrange: Melee Attacks Gain Knockback")] + MeleeKnockBack = 42, + [Description("s_item_opt_sa_probability_knockback_longrange: Ranged Attacks Gain Knockback")] + RangedKnockBack = 43, + [Description("s_item_opt_sa_probability_cannotmove_nearrange: Melee Attacks Immobilize Target")] + MeleeImmobilize = 44, + [Description("s_item_opt_sa_probability_cannotmove_longrange: Ranged Attacks Immobilize Target")] + RangedImmobilize = 45, + [Description("s_item_opt_sa_probability_splashdamage_nearrange: Melee Attacks Damage in Area")] + MeleeSplashDamage = 46, + [Description("s_item_opt_sa_probability_splashdamage_longrange: Ranged Attacks Damage in Area")] + RangedSplashDamage = 47, + [Description("s_item_opt_sa_improve_npckill_dropitem_incrate: Enemy Item Drop Chance")] + DropRate = 48, + [Description("s_item_opt_sa_improve_acquire_questreward_exp: Experience from Quests")] + QuestExp = 49, + [Description("s_item_opt_sa_improve_acquire_questreward_meso: Mesos from Quests")] + QuestMeso = 50, + [Description("")] + InvokeEffect1 = 51, + [Description("")] + InvokeEffect2 = 52, + [Description("")] + InvokeEffect3 = 53, + [Description("s_item_opt_sa_improve_damage_pvp: PvP Damage")] + PvpDamage = 54, + [Description("s_item_opt_sa_reduce_damage_pvp: PvP Defense")] + ReducePvpDamage = 55, + [Description("s_item_opt_sa_improve_guild_exp: Guild Experience")] + GuildExp = 56, + [Description("s_item_opt_sa_improve_guild_coin: Guild Coins")] + GuildCoin = 57, + [Description("s_item_opt_sa_improve_massive_event_expball: Experience Orb Drop Rate")] + MassiveEventExpBall = 58, + [Description("s_item_opt_sa_improve_acquire_fishing_exp: Experience from Fishing")] + FishingExp = 59, + [Description("s_item_opt_sa_improve_acquire_arcade_exp: Arcade Clear Experience")] + ArcadeExp = 60, + [Description("s_item_opt_sa_improve_acquire_playinstrument_exp: Experience from Performance")] + PlayInstrumentExp = 61, + [Description("s_item_opt_sa_improve_maid_mood: Assistant Experience")] + MaidExp = 62, + [Description("s_item_opt_sa_reduce_maid_recipe: Assistant Craft Material Discount")] + ReduceMaidRecipe = 63, + [Description("s_item_opt_sa_reduce_meso_trade_fee: Meso Handling Fee Discount")] + ReduceMesoTradeFee = 64, + [Description("s_item_opt_sa_reduce_enchant_material_fee: Enchant Material Discount")] + ReduceEnchantMaterialFee = 65, + [Description("s_item_opt_sa_reduce_merat_revival_fee: Meret Revive Discount")] + ReduceMeretRevivalFee = 66, + [Description("s_item_opt_sa_improve_mining_reward_item: Mining Quantity Earned")] + MiningRewardItem = 67, + [Description("s_item_opt_sa_improve_breeding_reward_item: Ranching Quantity Earned")] + BreedingRewardItem = 68, + [Description("s_item_opt_sa_improve_blacksmithing_reward_mastery: Smithing Experience Earned")] + SmithingRewardMastery = 69, + [Description("s_item_opt_sa_improve_engraving_reward_mastery: Handicraft Mastery Earned")] + EngravingRewardMastery = 70, + [Description("s_item_opt_sa_improve_gathering_reward_item: Foraging Quantity Earned")] + GatheringRewardItem = 71, + [Description("s_item_opt_sa_improve_farming_reward_item: Farming Quantity Earned")] + FarmingRewardItem = 72, + [Description("s_item_opt_sa_improve_alchemist_reward_mastery: Alchemy Mastery Earned")] + AlchemistRewardMastery = 73, + [Description("s_item_opt_sa_improve_cooking_reward_mastery: Cooking Mastery Earned")] + CookingRewardMastery = 74, + [Description("s_item_opt_sa_improve_acquire_gathering_exp: Experience from Foraging")] + AcquireGatheringExp = 75, + [Description("s_item_opt_sa_improve_acquire_manufacturing_exp: Experience from Crafting")] + AcquireManufacturingExp = 76, + [Description("s_item_opt_sa_skill_levelup_tier_1: Lv. 1 (1) Acquired Skill Level")] + SkillLevelUpTier1 = 77, + [Description("s_item_opt_sa_skill_levelup_tier_2: Lv. 1 (2) Acquired Skill Level")] + SkillLevelUpTier2 = 78, + [Description("s_item_opt_sa_skill_levelup_tier_3: Lv. 10 Acquired Skill Level")] + SkillLevelUpTier3 = 79, + [Description("s_item_opt_sa_skill_levelup_tier_4: Lv. 13 Acquired Skill Level")] + SkillLevelUpTier4 = 80, + [Description("s_item_opt_sa_skill_levelup_tier_5: Lv. 16 Acquired Skill Level")] + SkillLevelUpTier5 = 81, + [Description("s_item_opt_sa_skill_levelup_tier_6: Lv. 19 Acquired Skill Level")] + SkillLevelUpTier6 = 82, + [Description("s_item_opt_sa_skill_levelup_tier_7: Lv. 22 Acquired Skill Level")] + SkillLevelUpTier7 = 83, + [Description("s_item_opt_sa_skill_levelup_tier_8: Lv. 25 Acquired Skill Level")] + SkillLevelUpTier8 = 84, + [Description("s_item_opt_sa_skill_levelup_tier_9: Lv. 28 Acquired Skill Level")] + SkillLevelUpTier9 = 85, + [Description("s_item_opt_sa_skill_levelup_tier_10: Lv. 31 Acquired Skill Level")] + SkillLevelUpTier10 = 86, + [Description("s_item_opt_sa_skill_levelup_tier_11: Lv. 34 Acquired Skill Level")] + SkillLevelUpTier11 = 87, + [Description("s_item_opt_sa_skill_levelup_tier_12: Lv. 37 Acquired Skill Level")] + SkillLevelUpTier12 = 88, + [Description("s_item_opt_sa_skill_levelup_tier_13: Lv. 40 Acquired Skill Level")] + SkillLevelUpTier13 = 89, + [Description("s_item_opt_sa_skill_levelup_tier_14: Lv. 43 Acquired Skill Level")] + SkillLevelUpTier14 = 90, + [Description("s_item_opt_sa_improve_massive_ox_exp: OX Quiz Experience")] + MassiveOxExp = 91, + [Description("s_item_opt_sa_improve_massive_trapmaster_exp: Trap Master Experience")] + MassiveTrapMasterExp = 92, + [Description("s_item_opt_sa_improve_massive_finalsurvival_exp: Sole Survivor Experience")] + MassiveFinalSurvivalExp = 93, + [Description("s_item_opt_sa_improve_massive_crazyrunner_exp: Crazy Runners Experience")] + MassiveCrazyRunnerExp = 94, + [Description("s_item_opt_sa_improve_massive_escape_exp: Ludibrium Escape Experience")] + MassiveEscapeExp = 95, + [Description("s_item_opt_sa_improve_massive_springbeach_exp: Spring Beach Experience")] + MassiveSpringBeachExp = 96, + [Description("s_item_opt_sa_improve_massive_dancedance_exp: Dance Dance Stop Experience")] + MassiveDanceDanceExp = 97, + [Description("s_item_opt_sa_improve_massive_ox_msp: OX Quiz Movement Speed")] + MassiveOxSpeed = 98, + [Description("s_item_opt_sa_improve_massive_trapmaster_msp: Trap Master Movement Speed")] + MassiveTrapMasterSpeed = 99, + [Description("s_item_opt_sa_improve_massive_finalsurvival_msp: Sole Survivor Movement Speed")] + MassiveFinalSurvivalSpeed = 100, + [Description("s_item_opt_sa_improve_massive_crazyrunner_msp: Crazy Runners Movement Speed")] + MassiveCrazyRunnerSpeed = 101, + [Description("s_item_opt_sa_improve_massive_escape_msp: Ludibrium Escape Movement Speed")] + MassiveEscapeSpeed = 102, + [Description("s_item_opt_sa_improve_massive_springbeach_msp: Spring Beach Movement Speed")] + MassiveSpringBeachSpeed = 103, + [Description("s_item_opt_sa_improve_massive_dancedance_msp: Dance Dance Stop Movement Speed")] + MassiveDanceDanceSpeed = 104, + [Description("s_item_opt_sa_npc_hit_reward_sp_ball: Chance to Generate Spirit Orbs on Attack")] + NpcHitRewardSpBall = 105, + [Description("s_item_opt_sa_npc_hit_reward_ep_ball: Chance to Generate Stamina Orbs on Attack")] + NpcHitRewardEpBall = 106, + [Description("s_item_opt_sa_improve_honor_token: Valor Tokens")] + HonorToken = 107, + [Description("s_item_opt_sa_improve_pvp_exp: PvP Experience")] + PvpExp = 108, + [Description("s_item_opt_sa_improve_darkstream_damage: Dark Descent Damage Bonus")] + DarkStreamDamage = 109, + [Description("s_item_opt_sa_reduce_darkstream_recive_damage: Decreases Damage in the Dark Descent")] + ReduceDarkStreamReceiveDamage = 110, + [Description("s_item_opt_sa_improve_darkstream_evp: Dark Descent Evasion")] + DarkStreamEvp = 111, + [Description("s_item_opt_sa_fishing_double_mastery: Chance for 2x Fishing Mastery")] + FishingDoubleMastery = 112, + [Description("s_item_opt_sa_playinstrument_double_mastery: Chance for 2x Performance Mastery")] + PlayInstrumentDoubleMastery = 113, + [Description("s_item_opt_sa_complete_fieldmission_msp: Movement Speed in Explored Areas")] + CompleteFieldMissionSpeed = 114, + [Description("s_item_opt_sa_improve_glide_vertical_velocity: Air Mount Ascent Speed")] + GlideVerticalVelocity = 115, + [Description("s_item_opt_sa_additionaleffect_95000018: Being fixed")] + AdditionalEffect95000018 = 116, + [Description("s_item_opt_sa_additionaleffect_95000012: Enemy Defense on Hit")] + AdditionalEffect95000012 = 117, + [Description("s_item_opt_sa_additionaleffect_95000014: Enemy Attack on Hit")] + AdditionalEffect95000014 = 118, + [Description("s_item_opt_sa_additionaleffect_95000020: Total Damage when Enemy within 5m")] + AdditionalEffect95000020 = 119, + [Description("s_item_opt_sa_additionaleffect_95000021: Total Damage when 3 Enemies within 5m")] + AdditionalEffect95000021 = 120, + [Description("s_item_opt_sa_additionaleffect_95000022: Total Damage when Spirit Is 80 or More")] + AdditionalEffect95000022 = 121, + [Description("s_item_opt_sa_additionaleffect_95000023: Total Damage when Stamina Full")] + AdditionalEffect95000023 = 122, + [Description("s_item_opt_sa_additionaleffect_95000024: Total Damage when Herb Effects Active")] + AdditionalEffect95000024 = 123, + [Description("s_item_opt_sa_additionaleffect_95000025: World Boss Damage")] + AdditionalEffect95000025 = 124, + [Description("s_item_opt_sa_additionaleffect_95000026: 95000026")] + AdditionalEffect95000026 = 125, + [Description("s_item_opt_sa_additionaleffect_95000027: 95000027")] + AdditionalEffect95000027 = 126, + [Description("s_item_opt_sa_additionaleffect_95000028: 95000028")] + AdditionalEffect95000028 = 127, + [Description("s_item_opt_sa_additionaleffect_95000029: 95000029")] + AdditionalEffect95000029 = 128, + [Description("s_item_opt_sa_reduce_recovery_ep_inv: Stamina Recovery Speed")] + ReduceRecoveryEpInv = 129, + [Description("s_item_opt_sa_improve_stat_wap_u: Maximum Weapon Attack")] + MaxWeaponAttack = 130, + [Description("s_item_opt_sa_mining_double_reward: Chance for 2x Mining Production")] + MiningDoubleReward = 131, + [Description("s_item_opt_sa_breeding_double_reward: Chance for 2x Ranching Production")] + BreedingDoubleReward = 132, + [Description("s_item_opt_sa_gathering_double_reward: Chance for 2x Foraging Production")] + GatheringDoubleReward = 133, + [Description("s_item_opt_sa_farming_double_reward: Chance for 2x Farming Production")] + FarmingDoubleReward = 134, + [Description("s_item_opt_sa_blacksmithing_double_reward: Chance for 2x Smithing Production")] + SmithingDoubleReward = 135, + [Description("s_item_opt_sa_engraving_double_reward: Chance for 2x Handicraft Production")] + EngravingDoubleReward = 136, + [Description("s_item_opt_sa_alchemist_double_reward: Chance for 2x Alchemy Production")] + AlchemistDoubleReward = 137, + [Description("s_item_opt_sa_cooking_double_reward: Chance for 2x Cooking Production")] + CookingDoubleReward = 138, + [Description("s_item_opt_sa_mining_double_mastery: Chance for 2x Mining Mastery")] + MiningDoubleMastery = 139, + [Description("s_item_opt_sa_breeding_double_mastery: Chance for 2x Ranching Mastery")] + BreedingDoubleMastery = 140, + [Description("s_item_opt_sa_gathering_double_mastery: Chance for 2x Foraging Mastery")] + GatheringDoubleMastery = 141, + [Description("s_item_opt_sa_farming_double_mastery: Chance for 2x Farming Mastery")] + FarmingDoubleMastery = 142, + [Description("s_item_opt_sa_blacksmithing_double_mastery: Chance for 2x Smithing Mastery")] + SmithingDoubleMastery = 143, + [Description("s_item_opt_sa_engraving_double_mastery: Chance for 2x Handicraft Mastery")] + EngravingDoubleMastery = 144, + [Description("s_item_opt_sa_alchemist_double_mastery: Chance for 2x Alchemy Mastery")] + AlchemistDoubleMastery = 145, + [Description("s_item_opt_sa_cooking_double_mastery: Chance for 2x Cooking Mastery")] + CookingDoubleMastery = 146, + [Description("s_item_opt_sa_improve_chaosraid_wap: Weapon Attack in Chaos Raids")] + ChaosRaidAttack = 147, + [Description("s_item_opt_sa_improve_chaosraid_asp: Attack Speed in Chaos Raids")] + ChaosRaidAttackSpeed = 148, + [Description("s_item_opt_sa_improve_chaosraid_atp: Accuracy in Chaos Raids")] + ChaosRaidAccuracy = 149, + [Description("s_item_opt_sa_improve_chaosraid_hp: Health in Chaos Raids")] + ChaosRaidHp = 150, + [Description("s_item_opt_sa_improve_recovery_ball: Stamina and Spirit from Orbs")] + RecoveryBall = 151, + [Description("s_item_opt_sa_improve_fieldboss_kill_exp: World Boss Experience")] + FieldBossExp = 152, + [Description("s_item_opt_sa_improve_fieldboss_kill_drop: World Boss Item Drop Rate")] + FieldBossDropRate = 153, + [Description("s_item_opt_sa_reduce_fieldboss_recive_damage: Reduced Damage from World Bosses")] + ReduceFieldBossReceiveDamage = 154, + [Description("s_item_opt_sa_additionaleffect_95000016: 95000016")] + AdditionalEffect95000016 = 155, + [Description("s_item_opt_sa_improve_pettrap_reward: Max Pet Capture Reward Count")] + PetTrapReward = 156, + [Description("s_item_opt_sa_ming_multiaction: Mining Efficiency")] + MiningEfficiency = 157, + [Description("s_item_opt_sa_breeding_multiaction: Ranching Efficiency")] + BreedingEfficiency = 158, + [Description("s_item_opt_sa_gathering_multiaction: Foraging Efficiency")] + GatheringEfficiency = 159, + [Description("s_item_opt_sa_farming_multiaction: Farming Efficiency")] + FarmingEfficiency = 160, + [Description("s_item_opt_sa_improve_massive_sh_crazyrunner_exp: Shanghai Crazy Runners Experience")] + MassiveShCrazyRunnerExp = 161, + [Description("s_item_opt_sa_improve_massive_sh_crazyrunner_msp: Shanghai Crazy Runners Movement Speed")] + MassiveShCrazyRunnerSpeed = 162, + [Description("s_item_opt_sa_reduce_damage_by_targetmaxhp: Health-Based Damage Reduction")] + ReduceDamageByTargetMaxHp = 163, + [Description("s_item_opt_sa_reduce_meso_revival_fee")] + ReduceMesoRevivalFee = 164, + [Description("s_item_opt_sa_improve_riding_run_speed")] + RidingRunSpeed = 165, + [Description("s_item_opt_sa_improve_dungeon_reward_meso")] + DungeonRewardMeso = 166, + [Description("s_item_opt_sa_improve_shop_buying_meso")] + ShopBuyingMeso = 167, + [Description("s_item_opt_sa_improve_itembox_reward_meso")] + ItemBoxRewardMeso = 168, + [Description("s_item_opt_sa_reduce_remakeoption_fee")] + ReduceRemakeOptionFee = 169, + [Description("s_item_opt_sa_reduce_airtaxi_fee")] + ReduceAirTaxiFee = 170, + [Description("s_item_opt_sa_improve_socket_unlock_probability")] + SocketUnlockProbability = 171, + [Description("s_item_opt_sa_reduce_gemstone_upgrade_fee")] + ReduceGemstoneUpgradeFee = 172, + [Description("s_item_opt_sa_reduce_pet_remakeoption_fee")] + ReducePetRemakeOptionFee = 173, + [Description("s_item_opt_sa_improve_riding_speed")] + RidingSpeed = 174, + [Description("s_item_opt_sa_improve_survival_kill_exp")] + ImproveSurvivalKillExp = 175, + [Description("s_item_opt_sa_improve_survival_time_exp")] + ImproveSurvivalTimeExp = 176, + [Description("s_item_opt_sa_offensive_physicaldamage")] + OffensivePhysicalDamage = 177, + [Description("s_item_opt_sa_offensive_magicaldamage")] + OffensiveMagicalDamage = 178, + [Description("s_item_opt_sa_reduce_gameitem_socket_unlock_fee")] + ReduceGameItemSocketUnlockFee = 179, +} diff --git a/Maple2.Model/Enum/StringCode.cs b/Maple2.Model/Enum/StringCode.cs index 9f51fa3cf..f7f2601da 100644 --- a/Maple2.Model/Enum/StringCode.cs +++ b/Maple2.Model/Enum/StringCode.cs @@ -1,3455 +1,3455 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Enum; - -public enum StringCode { - s_empty_string = 0, - s_char_input_name = 1, - s_char_level_job = 2, - s_char_delete_waiting = 3, - s_char_ask_delete = 4, - s_char_ask_delete_with_wait = 5, - s_char_ask_delete_final = 6, - s_char_ask_delete_confirm_checkbox = 7, - s_char_ask_revival_char = 8, - s_char_err_char_count = 9, - s_char_err_char_count_by_gameevent = 10, - s_char_err_name = 11, - s_char_err_system = 12, - s_char_err_input = 13, - s_char_err_ban_any = 14, - s_char_err_ban_all = 15, - s_char_err_already_taken = 16, - s_char_err_invalid_def_item = 17, - s_char_err_job_forbidden = 18, - s_char_err_creation_restriction = 19, - s_char_err_unknown = 20, - s_char_err_exist_ugc_map = 21, - s_char_err_already_destroy = 22, - s_char_err_destroy = 23, - s_char_err_delete_name = 24, - s_char_err_guild_master = 25, - s_char_err_guild = 26, - s_char_err_ugc_market = 27, - s_char_err_black_market_count = 28, - s_char_err_unread_mail = 29, - s_char_err_no_destroy_wait = 30, - s_char_info_birthday = 31, - s_char_info_level_job = 32, - s_char_info_guild = 33, - s_char_info_ugcmap = 34, - s_char_info_home_name = 35, - s_char_info_guild_name = 36, - s_char_info_home_commend = 37, - s_char_info_err_not_found = 38, - s_change_charname_disconnect = 39, - s_change_charname_confirm = 40, - s_change_charname_err_bad_words = 41, - s_change_charname_err_already_taken = 42, - s_change_charname_err_consume_item = 43, - s_change_charname_err_system = 44, - s_err_gender = 45, - s_err_target = 46, - s_err_job = 47, - s_err_stat = 48, - s_err_lack_coupon = 49, - s_err_lack_super_coupon = 50, - s_err_lack_money = 51, - s_err_lack_meso = 52, - s_err_lack_merat = 53, - s_err_lack_merat_blue = 54, - s_err_lack_merat_red = 55, - s_err_lack_payment_item = 56, - s_err_lack_honor_token = 57, - s_err_lack_karma_token = 58, - s_err_lack_lu_token = 59, - s_err_lack_habi_token = 60, - s_err_lack_reverse_coin = 61, - s_err_lack_mentor_token = 62, - s_err_lack_mentee_token = 63, - s_err_lack_star_point = 64, - s_err_lack_meso_market_token = 65, - s_err_unable = 66, - s_err_inventory = 67, - s_err_inventory_tab_full = 68, - s_err_dropitem_pickfail_ownership = 69, - s_err_lack_hp = 70, - s_err_lack_sp = 71, - s_err_lack_ep = 72, - s_err_lack_shopitem = 73, - s_err_invalid_item = 74, - s_err_input = 75, - s_err_cannot_find_user = 76, - s_err_input_whisper_target = 77, - s_err_cannot_find_club = 78, - s_err_input_club = 79, - s_err_cannot_move = 80, - s_err_cannot_fly = 81, - s_err_cannot_use_here = 82, - s_err_cannot_use_dead = 83, - s_err_cannot_use_cooltime = 84, - s_err_cannot_use_only_shadowworld = 85, - s_err_require_additional_effect = 86, - s_err_got_of_control_unable_potion = 87, - s_err_skill_use_disable = 88, - s_err_skill_use_disable_by_require_condition = 89, - s_err_lack_guild_trophy = 90, - s_err_lack_achieve = 91, - s_err_lack_championship_grade = 92, - s_err_lack_championship_join_count = 93, - s_err_cannot_use_by_function_cube_climb = 94, - s_err_cannot_use_by_function_cube_jump = 95, - s_err_cannot_use_by_function_cube_skill = 96, - s_err_cannot_use_by_function_cube_play_instrument = 97, - s_err_cannot_use_in_design_home = 98, - s_err_cannot_buy_limited_item_more = 99, - s_err_cannot_install_blueprint = 100, - s_err_cannot_install_maid_in_practice = 101, - s_err_cannot_install_nurturing_in_design_home = 102, - s_err_cannot_install_magic_portal = 103, - s_err_cannot_install_trigger_editor = 104, - s_err_cannot_install_trigger_controlobject = 105, - s_err_cannot_install_interior_message = 106, - s_err_cannot_install_event_cube = 107, - s_err_cannot_install_trophy_relative_cube = 108, - s_err_cannot_install_workbench_cube = 109, - s_err_cannot_install_fittingdoll = 110, - s_err_cannot_install_ugcdesign_maidin_other = 111, - s_err_cannot_destroy_petitem_summon = 112, - s_err_cannot_destroy_petitem_hasitem = 113, - s_err_check_survival_with = 114, - s_err_cannot_use_maview_in_testsvr = 115, - s_job_difficulty = 116, - s_err_job_not_enough_meso = 117, - s_err_job_not_enough_level = 118, - s_err_job_bad_job = 119, - s_err_job_no_penalty = 120, - s_err_job_no_home = 121, - s_err_job_not_complete_quest = 122, - s_err_job_privilege = 123, - s_err_job_dayofweek = 124, - s_err_job_debuff = 125, - s_err_job_guild = 126, - s_err_job_inventory_full = 127, - s_err_job_unknown = 128, - s_mirror_err_not_editable_cp = 129, - s_mirror_err_not_editable_cp_by_transp_badge = 130, - s_mirror_err_not_equip_cp = 131, - s_err_empty_tabname = 132, - s_err_duplicate_tabname = 133, - s_err_lack_itemcount = 134, - s_err_null_product = 135, - s_err_near_taxi_station = 136, - s_err_taxi_same_field = 137, - s_taxi_transfer = 138, - s_cash_taxi_transfer = 139, - s_event_free_taxi_transfer = 140, - s_err_cash_taxi_continent = 141, - s_err_cash_taxi_cannot_departure = 142, - s_err_cash_taxi_cannot_destination = 143, - s_err_cash_taxi_cannot_now = 144, - s_err_cash_taxi_cannot_craft_mode = 145, - s_err_cash_taxi_cannot_place = 146, - s_err_cash_call_medic_prohibit_map = 147, - s_err_cash_call_medic_cannot_now = 148, - s_err_cash_call_medic_revival_count = 149, - s_err_cash_call_medic_cannot_place = 150, - s_cash_call_medic_complete = 151, - s_err_cash_call_market_prohibit_map = 152, - s_err_cash_call_market_cannot_now = 153, - s_err_cash_call_market_cannot_place = 154, - s_err_cash_call_market_cannot_already = 155, - s_err_cash_call_bank_cannot_now = 156, - s_err_cash_call_bank_cannot_now_already = 157, - s_err_cash_call_bank_cannot_now_place = 158, - s_err_cash_call_cancel = 159, - s_err_timeevent_move_field = 160, - s_err_timeevent_battle = 161, - s_err_timeevent_samefield = 162, - s_err_timeevent_same_continent = 163, - s_err_itemlink_cannot_other_user = 164, - s_err_worldmap_search_nothing_field = 165, - s_err_worldmap_search_invisible_field = 166, - s_worldmap_timeevent_move = 167, - s_timeevent_notice_chat = 168, - s_revival_panelty = 169, - s_fail_login = 170, - s_fail_connect = 171, - s_fail_char_create = 172, - s_notice_shutdown = 173, - s_npc_normal = 174, - s_npc_leader = 175, - s_npc_named = 176, - s_npc_boss = 177, - s_npc_field_boss = 178, - s_npc_dungeon_boss = 179, - s_npc_friendly = 180, - s_item_require_weapon = 181, - s_item_edit_time = 182, - s_item_end_time = 183, - s_item_end_time_expired = 184, - s_item_end_time_maid = 185, - s_item_end_time_maid_expired = 186, - s_item_end_time_pcbang = 187, - s_item_end_time_pcbang_expired = 188, - s_item_end_time_tooltip = 189, - s_item_end_time_tooltip_expiration = 190, - s_item_end_time_tooltip_pcbang = 191, - s_item_end_time_tooltip_maid = 192, - s_item_end_time_when_item_get_tooltip = 193, - s_item_end_time_when_item_get_tooltip_pcbang = 194, - s_item_end_time_when_item_get_tooltip_maid = 195, - s_item_word_d = 196, - s_item_word_h = 197, - s_item_word_m = 198, - s_item_type_active = 199, - s_item_type_passive = 200, - s_item_type_twohand = 201, - s_item_type_lefthand = 202, - s_item_type_righthand = 203, - s_item_err_code = 204, - s_item_err_puton = 205, - s_item_err_putoff = 206, - s_item_err_destroy_equip = 207, - s_item_err_no_weapon = 208, - s_item_err_invalid_weapon = 209, - s_item_err_Invalid_slot = 210, - s_item_err_cash_slot = 211, - s_item_err_cash_putoff = 212, - s_item_err_twohand = 213, - s_item_err_drop = 214, - s_item_err_cannot_drop = 215, - s_item_err_cannot_drop_if_binding = 216, - s_item_err_binding_destroy = 217, - s_item_err_binding_destroy_at_fittingdoll = 218, - s_item_err_binditem_in_socket_store_out = 219, - s_item_err_binding_in_socket_destroy = 220, - s_item_err_binding_in_socket_destroy_at_fittingdoll = 221, - s_item_err_cubeitem_destroy = 222, - s_item_err_puton_low_level = 223, - s_item_err_puton_expired = 224, - s_item_err_puton_pcbang = 225, - s_item_err_use_low_level = 226, - s_item_invalid_function_item = 227, - s_item_invalid_function_not_use_item = 228, - s_item_invalid_do_not_have = 229, - s_word_item_change_name_samename = 230, - s_item_err_puton_job = 231, - s_item_err_disable_job = 232, - s_item_err_puton_invalid = 233, - s_item_err_invalid_count = 234, - s_item_err_cant_sell = 235, - s_item_err_transfer_item_bound = 236, - s_item_err_range = 237, - s_item_err_skill_level = 238, - s_item_err_puton_skill_samekinds = 239, - s_item_err_invaild_store_type = 240, - s_item_err_store_full = 241, - s_item_tooltip_ugc_removed = 242, - s_item_ugc_name_blocked = 243, - s_item_err_cant_sell_riding_item = 244, - s_item_err_cant_attach_to_mail_riding_item = 245, - s_item_err_cant_drop_riding_item = 246, - s_item_err_cant_trade_riding_item = 247, - s_item_err_donot_preview = 248, - s_item_err_donot_preview_expire = 249, - s_item_err_binditem = 250, - s_item_err_binditem_store_out = 251, - s_item_err_puton_invalid_binding = 252, - s_item_err_use_invalid_binding = 253, - s_item_err_moveDisableitem_store_out = 254, - s_item_opt_error = 255, - s_item_opt_sa_improve_acquire_exp = 256, - s_item_opt_sa_improve_acquire_exp_v = 257, - s_item_opt_sa_improve_acquire_exp_r = 258, - s_item_opt_sa_improve_acquire_meso = 259, - s_item_opt_sa_improve_acquire_meso_v = 260, - s_item_opt_sa_improve_acquire_meso_r = 261, - s_item_opt_sa_improve_speed_swim = 262, - s_item_opt_sa_improve_speed_swim_v = 263, - s_item_opt_sa_improve_speed_swim_r = 264, - s_item_opt_sa_improve_speed_dash = 265, - s_item_opt_sa_improve_speed_dash_v = 266, - s_item_opt_sa_improve_speed_dash_r = 267, - s_item_opt_sa_improve_acquire_potion = 268, - s_item_opt_sa_improve_acquire_potion_v = 269, - s_item_opt_sa_improve_acquire_potion_r = 270, - s_item_opt_sa_improve_acquire_equipment = 271, - s_item_opt_sa_improve_acquire_equipment_v = 272, - s_item_opt_sa_improve_acquire_equipment_r = 273, - s_item_opt_sa_improve_damage_critical = 274, - s_item_opt_sa_improve_damage_critical_v = 275, - s_item_opt_sa_improve_damage_critical_r = 276, - s_item_opt_sa_improve_damage_normalNpc = 277, - s_item_opt_sa_improve_damage_normalNpc_v = 278, - s_item_opt_sa_improve_damage_normalNpc_r = 279, - s_item_opt_sa_improve_damage_leaderNpc = 280, - s_item_opt_sa_improve_damage_leaderNpc_v = 281, - s_item_opt_sa_improve_damage_leaderNpc_r = 282, - s_item_opt_sa_improve_damage_namedNpc = 283, - s_item_opt_sa_improve_damage_namedNpc_v = 284, - s_item_opt_sa_improve_damage_namedNpc_r = 285, - s_item_opt_sa_improve_damage_bossNpc = 286, - s_item_opt_sa_improve_damage_bossNpc_v = 287, - s_item_opt_sa_improve_damage_bossNpc_r = 288, - s_item_opt_sa_improve_recovery_regen_doheal = 289, - s_item_opt_sa_improve_recovery_regen_doheal_v = 290, - s_item_opt_sa_improve_recovery_regen_doheal_r = 291, - s_item_opt_sa_improve_recovery_regen_receiveheal = 292, - s_item_opt_sa_improve_recovery_regen_receiveheal_v = 293, - s_item_opt_sa_improve_recovery_regen_receiveheal_r = 294, - s_item_opt_sa_reduce_time_stun = 295, - s_item_opt_sa_reduce_time_stun_v = 296, - s_item_opt_sa_reduce_time_stun_r = 297, - s_item_opt_sa_improve_recovery_hp_dokill = 298, - s_item_opt_sa_improve_recovery_hp_dokill_v = 299, - s_item_opt_sa_improve_recovery_hp_dokill_r = 300, - s_item_opt_sa_improve_recovery_sp_dokill = 301, - s_item_opt_sa_improve_recovery_sp_dokill_v = 302, - s_item_opt_sa_improve_recovery_sp_dokill_r = 303, - s_item_opt_sa_improve_recovery_ep_dokill = 304, - s_item_opt_sa_improve_recovery_ep_dokill_v = 305, - s_item_opt_sa_improve_recovery_ep_dokill_r = 306, - s_item_opt_sa_reduce_time_cooldown = 307, - s_item_opt_sa_reduce_time_cooldown_v = 308, - s_item_opt_sa_reduce_time_cooldown_r = 309, - s_item_opt_sa_stat_attackpoint = 310, - s_item_opt_sa_stat_attackpoint_v = 311, - s_item_opt_sa_stat_attackpoint_r = 312, - s_item_opt_sa_improve_elements_ice = 313, - s_item_opt_sa_improve_elements_ice_v = 314, - s_item_opt_sa_improve_elements_ice_r = 315, - s_item_opt_sa_improve_elements_fire = 316, - s_item_opt_sa_improve_elements_fire_v = 317, - s_item_opt_sa_improve_elements_fire_r = 318, - s_item_opt_sa_improve_elements_dark = 319, - s_item_opt_sa_improve_elements_dark_v = 320, - s_item_opt_sa_improve_elements_dark_r = 321, - s_item_opt_sa_improve_elements_light = 322, - s_item_opt_sa_improve_elements_light_v = 323, - s_item_opt_sa_improve_elements_light_r = 324, - s_item_opt_sa_improve_elements_poison = 325, - s_item_opt_sa_improve_elements_poison_v = 326, - s_item_opt_sa_improve_elements_poison_r = 327, - s_item_opt_sa_improve_elements_thunder = 328, - s_item_opt_sa_improve_elements_thunder_v = 329, - s_item_opt_sa_improve_elements_thunder_r = 330, - s_item_opt_sa_improve_damage_nearrange = 331, - s_item_opt_sa_improve_damage_nearrange_r = 332, - s_item_opt_sa_improve_damage_nearrange_v = 333, - s_item_opt_sa_improve_damage_longrange = 334, - s_item_opt_sa_improve_damage_longrange_r = 335, - s_item_opt_sa_improve_damage_longrange_v = 336, - s_item_opt_sa_improve_piercing_par = 337, - s_item_opt_sa_improve_piercing_par_r = 338, - s_item_opt_sa_improve_piercing_par_v = 339, - s_item_opt_sa_improve_piercing_mar = 340, - s_item_opt_sa_improve_piercing_mar_r = 341, - s_item_opt_sa_improve_piercing_mar_v = 342, - s_item_opt_sa_reduce_elements_ice = 343, - s_item_opt_sa_reduce_elements_ice_v = 344, - s_item_opt_sa_reduce_elements_ice_r = 345, - s_item_opt_sa_reduce_elements_fire = 346, - s_item_opt_sa_reduce_elements_fire_v = 347, - s_item_opt_sa_reduce_elements_fire_r = 348, - s_item_opt_sa_reduce_elements_dark = 349, - s_item_opt_sa_reduce_elements_dark_v = 350, - s_item_opt_sa_reduce_elements_dark_r = 351, - s_item_opt_sa_reduce_elements_light = 352, - s_item_opt_sa_reduce_elements_light_v = 353, - s_item_opt_sa_reduce_elements_light_r = 354, - s_item_opt_sa_reduce_elements_poison = 355, - s_item_opt_sa_reduce_elements_poison_v = 356, - s_item_opt_sa_reduce_elements_poison_r = 357, - s_item_opt_sa_reduce_elements_thunder = 358, - s_item_opt_sa_reduce_elements_thunder_v = 359, - s_item_opt_sa_reduce_elements_thunder_r = 360, - s_item_opt_sa_reduce_time_condition = 361, - s_item_opt_sa_reduce_time_condition_v = 362, - s_item_opt_sa_reduce_time_condition_r = 363, - s_item_opt_sa_reduce_distance_knockBack = 364, - s_item_opt_sa_reduce_distance_knockBack_v = 365, - s_item_opt_sa_reduce_distance_knockBack_r = 366, - s_item_opt_sa_reduce_damage_nearrange = 367, - s_item_opt_sa_reduce_damage_nearrange_r = 368, - s_item_opt_sa_reduce_damage_nearrange_v = 369, - s_item_opt_sa_reduce_damage_longrange = 370, - s_item_opt_sa_reduce_damage_longrange_r = 371, - s_item_opt_sa_reduce_damage_longrange_v = 372, - s_item_opt_sa_improve_damage_final = 373, - s_item_opt_sa_improve_damage_final_r = 374, - s_item_opt_sa_improve_damage_final_v = 375, - s_item_opt_sa_probability_stun_nearrange = 376, - s_item_opt_sa_probability_stun_nearrange_r = 377, - s_item_opt_sa_probability_stun_nearrange_v = 378, - s_item_opt_sa_probability_stun_longrange = 379, - s_item_opt_sa_probability_stun_longrange_r = 380, - s_item_opt_sa_probability_stun_longrange_v = 381, - s_item_opt_sa_probability_knockback_nearrange = 382, - s_item_opt_sa_probability_knockback_nearrange_r = 383, - s_item_opt_sa_probability_knockback_nearrange_v = 384, - s_item_opt_sa_probability_knockback_longrange = 385, - s_item_opt_sa_probability_knockback_longrange_r = 386, - s_item_opt_sa_probability_knockback_longrange_v = 387, - s_item_opt_sa_probability_cannotmove_nearrange = 388, - s_item_opt_sa_probability_cannotmove_nearrange_r = 389, - s_item_opt_sa_probability_cannotmove_nearrange_v = 390, - s_item_opt_sa_probability_cannotmove_longrange = 391, - s_item_opt_sa_probability_cannotmove_longrange_r = 392, - s_item_opt_sa_probability_cannotmove_longrange_v = 393, - s_item_opt_sa_probability_splashdamage_nearrange = 394, - s_item_opt_sa_probability_splashdamage_nearrange_r = 395, - s_item_opt_sa_probability_splashdamage_nearrange_v = 396, - s_item_opt_sa_probability_splashdamage_longrange = 397, - s_item_opt_sa_probability_splashdamage_longrange_r = 398, - s_item_opt_sa_probability_splashdamage_longrange_v = 399, - s_item_opt_sa_improve_npckill_dropitem_incrate = 400, - s_item_opt_sa_improve_npckill_dropitem_incrate_r = 401, - s_item_opt_sa_improve_npckill_dropitem_incrate_v = 402, - s_item_opt_sa_improve_acquire_questreward_exp = 403, - s_item_opt_sa_improve_acquire_questreward_exp_v = 404, - s_item_opt_sa_improve_acquire_questreward_exp_r = 405, - s_item_opt_sa_improve_acquire_questreward_meso = 406, - s_item_opt_sa_improve_acquire_questreward_meso_v = 407, - s_item_opt_sa_improve_acquire_questreward_meso_r = 408, - s_item_opt_sa_improve_acquire_fishing_exp = 409, - s_item_opt_sa_improve_acquire_fishing_exp_v = 410, - s_item_opt_sa_improve_acquire_fishing_exp_r = 411, - s_item_opt_sa_improve_acquire_arcade_exp = 412, - s_item_opt_sa_improve_acquire_arcade_exp_v = 413, - s_item_opt_sa_improve_acquire_arcade_exp_r = 414, - s_item_opt_sa_improve_acquire_playinstrument_exp = 415, - s_item_opt_sa_improve_acquire_playinstrument_exp_v = 416, - s_item_opt_sa_improve_acquire_playinstrument_exp_r = 417, - s_item_opt_sa_invoke_effect = 418, - s_item_opt_sa_invoke_skill_decrease_cooldowntime_v = 419, - s_item_opt_sa_invoke_skill_increase_cooldowntime_v = 420, - s_item_opt_sa_invoke_skill_damage_v = 421, - s_item_opt_sa_invoke_effect_duration_v = 422, - s_item_opt_sa_invoke_effect_conditioneffect_probability_v = 423, - s_item_opt_sa_invoke_effect_dotdamage_v = 424, - s_item_opt_sa_invoke_effect_hprecovery_v = 425, - s_item_opt_sa_invoke_effect_hp_v = 426, - s_item_opt_sa_invoke_effect_hp_rgp_v = 427, - s_item_opt_sa_invoke_effect_hp_inv_v = 428, - s_item_opt_sa_invoke_effect_sp_v = 429, - s_item_opt_sa_invoke_effect_sp_rgp_v = 430, - s_item_opt_sa_invoke_effect_sp_inv_v = 431, - s_item_opt_sa_invoke_effect_ep_v = 432, - s_item_opt_sa_invoke_effect_ep_rgp_v = 433, - s_item_opt_sa_invoke_effect_ep_inv_v = 434, - s_item_opt_sa_invoke_effect_str_v = 435, - s_item_opt_sa_invoke_effect_atp_v = 436, - s_item_opt_sa_invoke_effect_pap_v = 437, - s_item_opt_sa_invoke_effect_dex_v = 438, - s_item_opt_sa_invoke_effect_evp_v = 439, - s_item_opt_sa_invoke_effect_map_v = 440, - s_item_opt_sa_invoke_effect_int_v = 441, - s_item_opt_sa_invoke_effect_cap_v = 442, - s_item_opt_sa_invoke_effect_par_v = 443, - s_item_opt_sa_invoke_effect_luk_v = 444, - s_item_opt_sa_invoke_effect_cad_v = 445, - s_item_opt_sa_invoke_effect_mar_v = 446, - s_item_opt_sa_invoke_effect_car_v = 447, - s_item_opt_sa_invoke_effect_pen_v = 448, - s_item_opt_sa_invoke_effect_asp_v = 449, - s_item_opt_sa_invoke_effect_ndd_v = 450, - s_item_opt_sa_invoke_effect_msp_v = 451, - s_item_opt_sa_invoke_effect_abp_v = 452, - s_item_opt_sa_invoke_effect_rmsp_v = 453, - s_item_opt_sa_invoke_effect_jmp_v = 454, - s_item_opt_sa_invoke_effect_wap_min_v = 455, - s_item_opt_sa_invoke_effect_wap_max_v = 456, - s_item_opt_sa_invoke_effect_offensive_physical_damage_v = 457, - s_item_opt_sa_invoke_effect_defensive_physical_damage_v = 458, - s_item_opt_sa_invoke_effect_offensive_magical_damage_v = 459, - s_item_opt_sa_invoke_effect_defensive_magical_damage_v = 460, - s_item_opt_sa_invoke_effect_defensive_neardistance_damage_v = 461, - s_item_opt_sa_invoke_effect_defensive_longdistance_damage_v = 462, - s_item_opt_sa_invoke_effect_improve_elements_fire_v = 463, - s_item_opt_sa_invoke_effect_improve_elements_ice_v = 464, - s_item_opt_sa_invoke_effect_improve_elements_thunder_v = 465, - s_item_opt_sa_invoke_effect_improve_elements_poison_v = 466, - s_item_opt_sa_invoke_effect_improve_elements_holy_v = 467, - s_item_opt_sa_invoke_effect_improve_elements_dark_v = 468, - s_item_opt_sa_invoke_effect_reduce_elements_fire_v = 469, - s_item_opt_sa_invoke_effect_reduce_elements_ice_v = 470, - s_item_opt_sa_invoke_effect_reduce_elements_thunder_v = 471, - s_item_opt_sa_invoke_effect_reduce_elements_poison_v = 472, - s_item_opt_sa_invoke_effect_reduce_elements_holy_v = 473, - s_item_opt_sa_invoke_effect_reduce_elements_dark_v = 474, - s_item_opt_sa_invoke_skill_decrease_cooldowntime_r = 475, - s_item_opt_sa_invoke_skill_increase_cooldowntime_r = 476, - s_item_opt_sa_invoke_skill_damage_r = 477, - s_item_opt_sa_invoke_effect_duration_r = 478, - s_item_opt_sa_invoke_effect_conditioneffect_probability_r = 479, - s_item_opt_sa_invoke_effect_dotdamage_r = 480, - s_item_opt_sa_invoke_effect_hprecovery_r = 481, - s_item_opt_sa_invoke_effect_hp_r = 482, - s_item_opt_sa_invoke_effect_hp_rgp_r = 483, - s_item_opt_sa_invoke_effect_hp_inv_r = 484, - s_item_opt_sa_invoke_effect_sp_r = 485, - s_item_opt_sa_invoke_effect_sp_rgp_r = 486, - s_item_opt_sa_invoke_effect_sp_inv_r = 487, - s_item_opt_sa_invoke_effect_ep_r = 488, - s_item_opt_sa_invoke_effect_ep_rgp_r = 489, - s_item_opt_sa_invoke_effect_ep_inv_r = 490, - s_item_opt_sa_invoke_effect_str_r = 491, - s_item_opt_sa_invoke_effect_atp_r = 492, - s_item_opt_sa_invoke_effect_pap_r = 493, - s_item_opt_sa_invoke_effect_dex_r = 494, - s_item_opt_sa_invoke_effect_evp_r = 495, - s_item_opt_sa_invoke_effect_map_r = 496, - s_item_opt_sa_invoke_effect_int_r = 497, - s_item_opt_sa_invoke_effect_cap_r = 498, - s_item_opt_sa_invoke_effect_par_r = 499, - s_item_opt_sa_invoke_effect_luk_r = 500, - s_item_opt_sa_invoke_effect_cad_r = 501, - s_item_opt_sa_invoke_effect_mar_r = 502, - s_item_opt_sa_invoke_effect_car_r = 503, - s_item_opt_sa_invoke_effect_pen_r = 504, - s_item_opt_sa_invoke_effect_asp_r = 505, - s_item_opt_sa_invoke_effect_ndd_r = 506, - s_item_opt_sa_invoke_effect_msp_r = 507, - s_item_opt_sa_invoke_effect_abp_r = 508, - s_item_opt_sa_invoke_effect_rmsp_r = 509, - s_item_opt_sa_invoke_effect_jmp_r = 510, - s_item_opt_sa_invoke_effect_wap_min_r = 511, - s_item_opt_sa_invoke_effect_wap_max_r = 512, - s_item_opt_sa_invoke_effect_offensive_physical_damage_r = 513, - s_item_opt_sa_invoke_effect_defensive_physical_damage_r = 514, - s_item_opt_sa_invoke_effect_offensive_magical_damage_r = 515, - s_item_opt_sa_invoke_effect_defensive_magical_damage_r = 516, - s_item_opt_sa_invoke_effect_defensive_neardistance_damage_r = 517, - s_item_opt_sa_invoke_effect_defensive_longdistance_damage_r = 518, - s_item_opt_sa_invoke_effect_improve_elements_fire_r = 519, - s_item_opt_sa_invoke_effect_improve_elements_ice_r = 520, - s_item_opt_sa_invoke_effect_improve_elements_thunder_r = 521, - s_item_opt_sa_invoke_effect_improve_elements_poison_r = 522, - s_item_opt_sa_invoke_effect_improve_elements_holy_r = 523, - s_item_opt_sa_invoke_effect_improve_elements_dark_r = 524, - s_item_opt_sa_invoke_effect_reduce_elements_fire_r = 525, - s_item_opt_sa_invoke_effect_reduce_elements_ice_r = 526, - s_item_opt_sa_invoke_effect_reduce_elements_thunder_r = 527, - s_item_opt_sa_invoke_effect_reduce_elements_poison_r = 528, - s_item_opt_sa_invoke_effect_reduce_elements_holy_r = 529, - s_item_opt_sa_invoke_effect_reduce_elements_dark_r = 530, - s_item_opt_has_additional_effect = 531, - s_item_opt_sa_improve_damage_pvp = 532, - s_item_opt_sa_improve_damage_pvp_r = 533, - s_item_opt_sa_improve_damage_pvp_v = 534, - s_item_opt_sa_reduce_damage_pvp = 535, - s_item_opt_sa_reduce_damage_pvp_v = 536, - s_item_opt_sa_reduce_damage_pvp_r = 537, - s_login_err_connect = 538, - s_login_err_disconnected = 539, - s_login_err_id = 540, - s_login_err_pwd = 541, - s_login_err_access = 542, - s_login_err_full_server = 543, - s_login_err_version = 544, - s_login_err_full_ch = 545, - s_login_err_db = 546, - s_login_err_unknown = 547, - s_login_err_check_passport = 548, - s_login_err_restrict_title = 549, - s_login_err_restrict = 550, - s_login_err_alphatester = 551, - s_login_err_block_new_account = 552, - s_login_err_guest_nopcbang = 553, - s_login_err_session_error = 554, - s_login_err_external_block_nsn = 555, - s_login_err_external_block_ip = 556, - s_login_err_admin_ip = 557, - s_login_err_main_atl = 558, - s_login_err_auto_external_block = 559, - s_login_err_tencent_signature = 560, - s_ah_err_close = 561, - s_ngs_err_login_error = 562, - s_relocate_world_err = 563, - s_mail_send = 564, - s_mail_return = 565, - s_mail_delete = 566, - s_notify_mail_recieve = 567, - s_mail_err_cannot_attach_item = 568, - s_mail_read_list = 569, - s_mail_delete_list = 570, - s_mail_receive_list = 571, - s_mail_delete_list_received_mail = 572, - s_mail_delete_list_attached_mail = 573, - s_mail_delete_list_attached_mail_checkbox = 574, - s_mail_send_date = 575, - s_mail_attach_item = 576, - s_mail_delete_confirm = 577, - s_mail_delete_exist_ad = 578, - s_mail_delete_exist_attach = 579, - s_mail_send_really = 580, - s_mail_read_fail = 581, - s_mail_error = 582, - s_mail_error_already_attach_meso = 583, - s_mail_error_range = 584, - s_mail_error_not_select = 585, - s_mail_error_username = 586, - s_mail_error_cannot_attach_item = 587, - s_mail_error_attachcount = 588, - s_mail_error_already_receive = 589, - s_mail_error_recipient_equal_sender = 590, - s_mail_error_createmail = 591, - s_mail_error_sendmail = 592, - s_mail_error_empty_cleaning_user = 593, - s_mail_error_empty_cleaning_system = 594, - s_mail_error_empty_title = 595, - s_mail_error_empty_content = 596, - s_mail_error_alreadyread = 597, - s_mail_error_receiveitem_to_inven = 598, - s_mail_error_receive_expired = 599, - s_mail_error_ad_expired = 600, - s_mail_error_block_from_me = 601, - s_mail_error_block_from_other = 602, - s_mail_error_admin_character = 603, - s_mail_error_from_admin_to_user = 604, - s_mail_error_bancheck = 605, - s_mail_error_admin_block = 606, - s_mail_cleaning_title = 607, - s_mail_cleaning_usermail = 608, - s_mail_cleaning_systemmail = 609, - s_mail_error_limit_input = 610, - s_mail_period_item_include = 611, - s_mail_period_item_include_chat = 612, - s_inventory_tab_equip = 613, - s_inventory_tab_life = 614, - s_inventory_tab_etc = 615, - s_inventory_tab_summon = 616, - s_inventory_tab_petequip = 617, - s_inventory_tab_skin = 618, - s_inventory_tab_gem = 619, - s_inventory_tab_quest = 620, - s_inventory_tab_material = 621, - s_inventory_tab_mastery = 622, - s_inventory_tab_pet = 623, - s_inventory_tab_activeskill = 624, - s_inventory_tab_coin = 625, - s_inventory_tab_badge = 626, - s_inventory_tab_survival = 627, - s_inventory_ask_expand = 628, - s_inventory_err_expand_max = 629, - s_move_err_no_server = 630, - s_move_err_over_user = 631, - s_move_err_dungeon_not_exist = 632, - s_move_err_member_limit = 634, - s_move_err_time_out = 635, - s_move_err_field_limit = 636, - s_beauty_skin_name = 637, - s_beauty_notice_hide_cap_by_transp_badge = 638, - s_beauty_msg_back_game_no_save = 639, - s_beauty_msg_error_code = 640, - s_beauty_tooltip_price = 641, - s_beauty_tooltip_style = 642, - s_beauty_tooltip_style_save_date = 643, - s_beauty_msg_random = 644, - s_beauty_msg_coloring_confirm = 645, - s_beauty_goto_map_invalid_dead = 646, - s_beauty_goto_map_invalid_battle = 647, - s_beauty_goto_map_invalid_samefield = 648, - s_msg_die_beginner = 649, - s_msg_die_warring_different_map_revive = 650, - s_msg_revival_btn = 651, - s_msg_revival_mapleworld_btn = 652, - s_msg_revival_merat_btn = 653, - s_msg_revival_merat_cannot_debuff = 654, - s_msg_revival_merat_cannot_here = 655, - s_msg_revival_merat_msg_box = 656, - s_msg_revival_merat_not_dead = 657, - s_msg_revival_meso_btn = 658, - s_msg_revival_meso_event_btn = 659, - s_msg_revival_dungeon_warning = 660, - s_msg_game_over = 661, - s_msg_disconnect_kickuser = 662, - s_msg_item_sell_request = 663, - s_msg_item_sell_count_request = 664, - s_msg_item_sell_confirm = 665, - s_msg_item_buy_confirm = 666, - s_msg_revial_meso = 667, - s_msg_not_visit = 668, - s_msg_not_visitable = 669, - s_msg_admin_input_accountsn = 670, - s_msg_tombstone = 671, - s_msg_meso_drop = 672, - s_msg_item_drop = 673, - s_msg_item_buy = 674, - s_msg_item_sell = 675, - s_msg_item_repurchase = 676, - s_msg_item_upgrade_level = 677, - s_msg_item_upgrade_failed = 678, - s_msg_item_upgrade_disabled = 679, - s_msg_item_upgrade_complete = 680, - s_msg_item_default = 681, - s_msg_item_open_item_box = 682, - s_msg_item_open_item_dont_ask_check = 683, - s_msg_item_open_item_dont_ask_check_buycube = 684, - s_msg_item_use_name = 685, - s_msg_item_use_only_shadowcontinent = 686, - s_msg_item_remove_expired_item_in_inventory = 687, - s_msg_expand_inven_complete = 688, - s_msg_expand_inven_already_maximum = 689, - s_msg_expand_character_slot_complete = 690, - s_msg_expand_character_slot_already_maximum = 691, - s_msg_expand_inven_forced = 692, - s_msg_currency_overflow = 693, - s_store_ask_expand = 694, - s_store_ask_deposit = 695, - s_store_ask_withdraw = 696, - s_store_ask_in = 697, - s_store_ask_out = 698, - s_store_ask_close = 699, - s_store_ask_homebank_close = 700, - s_store_err_code = 701, - s_store_err_expand_max = 702, - s_store_err_deposit_disable_type = 703, - s_store_err_deposit_invalid_money = 704, - s_store_err_deposit_max_money = 705, - s_store_err_withdraw_invalid_money = 706, - s_store_err_withdraw_not_enough_balance = 707, - s_store_err_deposit_not_enough_balance = 708, - s_msg_party_invite = 709, - s_msg_popup_time = 710, - s_msg_take_item = 711, - s_msg_take_item_count = 712, - s_msg_take_item_ugc_cube = 713, - s_msg_take_item_count_ugc_cube = 714, - s_msg_take_exp = 715, - s_msg_take_assist_bonus_exp = 716, - s_msg_take_assist_bonus_exp_system = 717, - s_msg_take_map_exp = 718, - s_msg_take_taxi_exp = 719, - s_msg_take_telescope_exp = 720, - s_msg_take_meso = 721, - s_msg_consume_meso = 722, - s_msg_take_pcbang = 723, - s_msg_take_merat = 724, - s_msg_take_merat_blue = 725, - s_msg_take_merat_red = 726, - s_msg_take_honor_token = 727, - s_msg_take_karma_token = 728, - s_msg_take_lu_token = 729, - s_msg_take_habi_token = 730, - s_msg_take_star_point = 731, - s_msg_take_meso_market_token = 732, - s_msg_cant_sell_trade_drop = 733, - s_msg_cant_sell_trade = 734, - s_msg_cant_sell_drop = 735, - s_msg_cant_sell = 736, - s_msg_cant_trade_drop = 737, - s_msg_cant_trade = 738, - s_msg_cant_drop = 739, - s_msg_chatting_welcome = 740, - s_msg_chatting_changechannel = 741, - s_msg_chatting_consume_merat = 742, - s_msg_chatting_consume_merat_blue = 743, - s_msg_chatting_consume_merat_red = 744, - s_msg_chatting_consume_merat_message = 745, - s_msg_skillbook_learn_messagebox = 746, - s_msg_skillbook_learn_notexistslot = 747, - s_msg_skillbook_error_job = 748, - s_msg_skillbook_error_level = 749, - s_msg_skillbook_error_master = 750, - s_msg_skillbook_error_expired = 751, - s_msg_skillbook_reset_all = 752, - s_mas_move_connect = 753, - s_msg_ugc_cannot_edit = 754, - s_msg_ugc_cannot_find = 755, - s_msg_ugc_not_select = 756, - s_msg_ugc_not_file_preview = 757, - s_msg_ugc_not_file_noexist = 758, - s_msg_ugc_too_many_files = 759, - s_msg_ugc_not_select_reserve = 760, - s_msg_ugc_error_item_name = 761, - s_msg_ugc_admin_banner = 762, - s_msg_ugc_shutdown = 763, - s_msg_ugc_cant_register_shortcut = 764, - s_msg_ugc_fail_file_exist = 765, - s_msg_ugc_fail_file_size = 766, - s_msg_ugc_item_upload_confirm = 767, - s_msg_ugc_item_edit_thumbnail_tooltip = 768, - s_msg_ugc_item_edit_text_title = 769, - s_msg_ugc_item_edit_complete = 770, - s_msg_ugc_expired_item = 771, - s_msg_cant_movechannel = 772, - s_change_gender_confirm = 773, - s_change_gender_err_equip_items = 774, - s_change_gender_result_success = 775, - s_change_gender_result_failed = 776, - s_notify_currency_overflow = 777, - s_content_shutdown_notice = 778, - s_banner_billboard_shutdown = 779, - s_msg_warehouse_container_out = 780, - s_msg_warehouse_container_loading = 781, - s_msg_warehouse_container_count = 782, - s_msg_warehouse_container_count_empty = 783, - s_msg_skill_upgrade = 784, - s_msg_crystal_upgrade_empty = 785, - s_msg_crystal_upgrade_disabled = 786, - s_msg_crystal_upgrade_complete = 787, - s_msg_memo_today = 788, - s_msg_preparing = 789, - s_msg_transfer_bind_when_transform = 790, - s_shop_reset_time_ddhhmmss = 791, - s_shop_reset_time_hhmmss = 792, - s_shop_reset_time_mmss = 793, - s_shop_reset_time_ss = 794, - s_shop_require_achieve = 795, - s_shop_require_guild_trophy = 796, - s_shop_require_championship_grade = 797, - s_shop_err_item_restricted = 798, - s_option_fullscreen = 799, - s_option_windowmode = 800, - s_option_windowmode_full = 801, - s_option_resolution = 802, - s_option_resolution_wide = 803, - s_option_perf_very_low = 804, - s_option_perf_low = 805, - s_option_perf_normal = 806, - s_option_perf_high = 807, - s_option_perf_very_high = 808, - s_option_category_action = 809, - s_option_category_menu = 810, - s_option_category_emotion = 811, - s_option_category_etc = 812, - s_option_category_debug = 813, - s_party_join_me = 814, - s_party_join_someone = 815, - s_party_leave_me = 816, - s_party_leave_someone = 817, - s_party_expel_me = 818, - s_party_expel_someone = 819, - s_party_break = 820, - s_party_member_login = 821, - s_party_member_logout = 822, - s_party_chief_me = 823, - s_party_chief_someone = 824, - s_party_member_dead_tomb = 825, - s_party_member_dead_dark_tomb = 826, - s_party_err_code = 827, - s_party_err_not_exist = 828, - s_party_err_already = 829, - s_party_err_alreadyInvite = 830, - s_party_err_not_chief = 831, - s_party_err_full = 832, - s_party_err_myself = 833, - s_party_err_cannot_invite = 834, - s_party_err_deny = 835, - s_party_err_deny_by_auto = 836, - s_party_err_deny_by_system = 837, - s_party_err_deny_by_timeout = 838, - s_party_err_leave_no_party = 839, - s_party_err_no_party = 840, - s_party_err_fail_enterable_result = 841, - s_party_err_lack_level = 842, - s_party_err_lack_gear_score = 843, - s_party_err_full_limit_player = 844, - s_party_err_invalid_recruit = 845, - s_party_err_invalid_party = 846, - s_party_err_invalid_chief = 847, - s_party_err_wrong_party = 848, - s_party_err_wrong_recruit = 849, - s_party_check_change_chief = 850, - s_party_check_expel_member = 851, - s_party_expel_boss_room = 852, - s_party_someone_get_high_item = 853, - s_party_auto_join_confirm = 854, - s_club_create = 855, - s_club_create_ask = 856, - s_club_break = 857, - s_club_invite_someone = 858, - s_club_invite_me = 859, - s_club_invite_cant_me = 860, - s_club_invite_invalid_charname = 861, - s_club_join = 862, - s_club_join_reject = 863, - s_club_join_reject_invite = 864, - s_club_join_reject_timeout = 865, - s_club_join_reject_logout = 866, - s_club_leave = 867, - s_club_notify_leave = 868, - s_club_notify_accept_invite = 869, - s_club_notify_login_member = 870, - s_club_notify_logout_member = 871, - s_club_notify_change_master = 872, - s_club_notify_change_master_me = 873, - s_club_notify_change_buff = 874, - s_club_notify_change_name = 875, - s_club_ui_offline_time_day = 876, - s_club_ui_offline_time_hour = 877, - s_club_ui_offline_time_min = 878, - s_club_ui_offline_time_sec = 879, - s_club_ui_offline_unknown = 880, - s_club_ui_offline = 881, - s_club_ui_member_location = 882, - s_club_ui_member_detail = 883, - s_club_ui_invite_member = 884, - s_club_ui_create = 885, - s_club_ui_leave = 886, - s_club_ui_change_master = 887, - s_club_ui_create_time = 888, - s_club_ui_select_target_member = 889, - s_club_ui_current_member = 890, - s_club_err_unknown = 891, - s_club_err_create = 892, - s_club_err_create_reject = 893, - s_club_err_null_club = 894, - s_club_err_create_no_party = 895, - s_club_err_already_exist = 896, - s_club_err_wait_inviting = 897, - s_club_err_blocked = 898, - s_club_err_has_guild = 899, - s_club_err_invalid_guild = 900, - s_club_err_null_user = 901, - s_club_err_name_exist = 902, - s_club_err_name_value = 903, - s_club_err_null_member = 904, - s_club_err_exist_member = 905, - s_club_err_full_member = 906, - s_club_err_not_join_member = 907, - s_club_err_cannot_leave_master = 908, - s_club_err_expel_target_master = 909, - s_club_err_no_master = 910, - s_club_err_fail_addmember = 911, - s_club_err_null_invite_member = 912, - s_club_err_none = 913, - s_club_err_block = 914, - s_club_err_fail_this_field = 915, - s_club_err_full_club = 916, - s_club_err_full_club_member = 917, - s_club_err_notparty_alllogin = 918, - s_club_err_remain_time = 919, - s_club_err_same_club_name = 920, - s_club_err_clubname_has_blank = 921, - s_guild_err_no_guild = 923, - s_guild_create = 924, - s_guild_break = 925, - s_guild_invite_someone = 926, - s_guild_invite_me = 927, - s_guild_invite_cant_me = 928, - s_guild_invite_invalid_charname = 929, - s_guild_join = 930, - s_guild_join_reject = 931, - s_guild_join_accecpt_invite = 932, - s_guild_join_reject_invite = 933, - s_guild_join_reject_logout = 934, - s_guild_join_reject_timeout = 935, - s_guild_leave = 936, - s_guild_leave_master_cant = 937, - s_guild_change_notify = 938, - s_guild_change_grade_sucess = 939, - s_guild_grade_default_master = 940, - s_guild_grade_default_group1 = 941, - s_guild_grade_default_group2 = 942, - s_guild_grade_default_group3 = 943, - s_guild_grade_default_group4 = 944, - s_guild_grade_default_group5 = 945, - s_guild_extend_capacity_success = 946, - s_guild_extend_capacity_err_cannot = 947, - s_guild_extend_capacity_err_current = 948, - s_guild_search_same_propensity = 949, - s_guild_search_max_join_request = 950, - s_guild_search_last_request = 951, - s_guild_search_null_join_guild_request = 952, - s_guild_notify_leave = 953, - s_guild_notify_change_grade = 954, - s_guild_notify_accept_invite = 955, - s_guild_notify_expel_member = 956, - s_guild_notify_expeled = 957, - s_guild_notify_expeled_from = 958, - s_guild_notify_login_member = 959, - s_guild_notify_logout_member = 960, - s_guild_notify_change_member_grade = 961, - s_guild_notify_change_member_grade_me = 962, - s_guild_notify_change_master = 963, - s_guild_notify_change_master_me = 964, - s_guild_notify_change_notify = 965, - s_guild_notify_change_mark = 966, - s_guild_notify_change_name = 967, - s_guild_notify_change_capacity = 968, - s_guild_notify_achieve_progress = 969, - s_guild_notify_achieve_complete = 970, - s_guild_notify_pvp_get_grade = 971, - s_guild_notify_pvp_regist = 972, - s_guild_notify_pvp_unregist = 973, - s_guild_notify_pvp_result_win = 974, - s_guild_notify_pvp_result_lose = 975, - s_guild_notify_search_join_accept = 976, - s_guild_notify_search_join_reject = 977, - s_guild_pvp_matching_complete_success = 978, - s_guild_pvp_matching_complete_fail = 979, - s_guild_pvp_matching_complete_popup = 980, - s_guild_pvp_already_pvp_field = 981, - s_guild_pvp_can_regist_when_login = 982, - s_guild_pvp_can_regist_when_playing = 983, - s_guild_pvp_join_only_matched = 984, - s_guild_pvp_join_not_equal_championship_guild = 986, - s_guild_pvp_done_choose_championship_guild = 987, - s_guild_search_choose_propensity = 988, - s_guild_search_guild_search_no_result = 989, - s_guild_search_join_reject_mail_sender = 990, - s_guild_search_join_reject_mail_title = 991, - s_guild_search_join_reject_mail_content = 992, - s_guild_search_request_join_guild = 993, - s_guild_search_cancel_request_join_guild = 994, - s_guild_search_accept_requested_member = 995, - s_guild_search_reject_requested_member = 996, - s_guild_ui_offline_time_day = 997, - s_guild_ui_offline_time_hour = 998, - s_guild_ui_offline_time_min = 999, - s_guild_ui_offline_time_sec = 1000, - s_guild_ui_offline_unknown = 1001, - s_guild_ui_offline = 1002, - s_guild_ui_member_location = 1003, - s_guild_ui_member_detail = 1004, - s_guild_ui_invite_member = 1005, - s_guild_ui_create = 1006, - s_guild_ui_change_notify = 1007, - s_guild_ui_extend_member = 1008, - s_guild_ui_extend_max = 1009, - s_guild_ui_break = 1010, - s_guild_ui_leave = 1011, - s_guild_ui_not_to_master = 1012, - s_guild_ui_change_master = 1013, - s_guild_ui_expel = 1014, - s_guild_ui_guild_create_time = 1015, - s_guild_ui_must_input_group_name = 1016, - s_guild_ui_already_member_have_group = 1017, - s_guild_ui_select_target_member = 1018, - s_guild_ui_current_member = 1019, - s_guild_ui_championship_history_score_normal = 1020, - s_guild_ui_championship_history_no_fight_win = 1021, - s_guild_ui_championship_history_no_fight_lose = 1022, - s_guild_ui_championship_condition = 1023, - s_guild_ui_championship_rating = 1024, - s_guild_ui_championship_combat = 1025, - s_guild_ui_championship_rank = 1026, - s_guild_ui_championship_grade_tooltip = 1027, - s_guild_ui_championship_really_unregist = 1028, - s_guild_ui_championship_no_regist_auth = 1029, - s_guild_ui_championship_no_period_day = 1030, - s_guild_ui_championship_no_period_time = 1031, - s_guild_ui_championship_my_participate = 1032, - s_guild_ui_championship_member_participate = 1033, - s_guild_ui_championship_member_grade_score = 1034, - s_guild_ui_championship_reward_player = 1035, - s_guild_ui_championship_reward_supporter = 1036, - s_guild_ui_championship_choose_championship_guild = 1037, - s_guild_ui_championship_info_championship_guild = 1038, - s_guild_ui_pvp_draw = 1039, - s_guild_ui_pvp_win = 1040, - s_guild_ui_pvp_lose = 1041, - s_guild_ui_pvp_result_winner_rating = 1042, - s_guild_ui_pvp_result_loser_rating_add = 1043, - s_guild_ui_pvp_result_reward = 1044, - s_guild_search_join_requester_info = 1045, - s_guild_err_unknown = 1046, - s_guild_err_null_guild = 1047, - s_guild_err_already_exist = 1048, - s_guild_err_wait_inviting = 1049, - s_guild_err_blocked = 1050, - s_guild_err_has_guild = 1051, - s_guild_err_invalid_guild = 1052, - s_guild_err_null_user = 1053, - s_guild_err_name_exist = 1054, - s_guild_err_name_value = 1055, - s_guild_err_null_member = 1056, - s_guild_err_exist_member = 1057, - s_guild_err_full_member = 1058, - s_guild_err_not_join_member = 1059, - s_guild_err_cannot_leave_master = 1060, - s_guild_err_expel_target_master = 1061, - s_guild_err_not_enough_level = 1062, - s_guild_err_no_money = 1063, - s_guild_err_no_authority = 1064, - s_guild_err_no_master = 1065, - s_guild_err_invalid_grade_range = 1066, - s_guild_err_invalid_capacity_range = 1067, - s_guild_err_invalid_grade_data = 1068, - s_guild_err_invalid_grade_index = 1069, - s_guild_err_exist_empty_grade_index = 1070, - s_guild_err_set_grade_failed = 1071, - s_guild_err_fail_addmember = 1072, - s_guild_err_null_invite_member = 1073, - s_guild_err_cant_during_pvp = 1074, - s_guild_err_none = 1075, - s_guild_err_block = 1076, - s_guild_err_fail_change_gradename = 1077, - s_guild_err_fail_change_gradename_row = 1078, - s_guild_err_fail_this_field = 1079, - s_individual_waiting_unknown = 1080, - s_individual_waiting_min = 1081, - s_individual_waiting_hour = 1082, - s_individual_register_other_arena = 1083, - s_individual_register_call_of_arena = 1084, - s_individual_register_low_level = 1085, - s_individual_register_done = 1086, - s_individual_unregister_done = 1087, - s_individual_matching_done = 1088, - s_individual_join_error_dead = 1089, - s_individual_join_error_field = 1090, - s_whisper_err_myself = 1091, - s_whisper_err_target = 1092, - s_admin_block_velma_notice = 1093, - s_admin_block_velma_notice_chat = 1094, - s_admin_block_velma_add = 1095, - s_admin_block_velma_endtime_dec = 1096, - s_admin_block_velma_msgbox_title = 1097, - s_admin_block_velma_msgbox_content = 1098, - s_admin_block_velma_ugc_notice = 1099, - s_admin_block_velma_ugc_add = 1100, - s_admin_block_velma_ugc_endtime_dec = 1101, - s_admin_block_velma_ugc_msgbox_title = 1102, - s_admin_block_velma_ugc_msgbox_content = 1103, - s_admin_block_transfer_msgbox_title = 1104, - s_admin_block_transfer_msgbox_content = 1105, - s_admin_block_transfer_msgbox_title_kr = 1106, - s_admin_block_transfer_msgbox_content_kr = 1107, - s_admin_block_login = 1108, - s_admin_block_chat = 1109, - s_admin_block_banner_reg = 1110, - s_admin_block_ugcmarket_reg = 1111, - s_admin_block_ugc_equip = 1112, - s_admin_block_ugc_create = 1113, - s_admin_block_guild_create = 1114, - s_admin_block_guild_mark_change = 1115, - s_admin_block_profile_change = 1116, - s_admin_block_char_name_change = 1117, - s_admin_block_mail_send = 1118, - s_admin_block_ugcmap_create = 1119, - s_admin_block_party_search = 1120, - s_admin_block_play_score = 1121, - s_admin_block_write_music = 1122, - s_admin_block_period = 1123, - s_admin_block_transfer = 1124, - s_admin_block_profit = 1125, - s_admin_block_default = 1126, - s_admin_block_create_guild_msgbox_title = 1127, - s_admin_block_create_guild_msgbox_content = 1128, - s_quest_clear_time = 1129, - s_quest_clear_morning = 1130, - s_quest_clear_afternoon = 1131, - s_quest_standard_level = 1132, - s_skill_err_male = 1133, - s_skill_err_female = 1134, - s_skill_err_low_level = 1135, - s_skill_err_weapon = 1136, - s_title_shop = 1137, - s_title_buyitem = 1138, - s_title_sellitem = 1139, - s_ugc_err_code = 1140, - s_ugc_err_url = 1141, - s_ugc_regist_ok = 1142, - s_ugc_upload_ok_item = 1143, - s_ugc_upload_ok_item_screenshot = 1144, - s_ugc_upload_ok_home_screenshot = 1145, - s_ugc_upload_ok_profile = 1146, - s_ugc_upload_ok_banner_sch = 1147, - s_ugc_upload_ok_banner = 1148, - s_ugc_upload_must_profile = 1149, - s_ugc_camera_profile_upload = 1150, - s_ugc_sign_buy = 1151, - s_ugc_sign_sell = 1152, - s_ugc_err_craft_mode = 1153, - s_ugc_confirm_destroy_bank = 1154, - s_ugc_confirm_destroy_fittingdoll = 1155, - s_ugc_confirm_destroy_fittingdoll_v2 = 1156, - s_ugc_confirm_destroy_password = 1157, - s_ugc_confirm_destroy_trigger_editor = 1158, - s_ugc_confirm_destroy_field_affected = 1159, - s_word_ok = 1160, - s_word_cancel = 1161, - s_word_yes = 1162, - s_word_no = 1163, - s_word_accept = 1164, - s_word_deny = 1165, - s_word_complete = 1166, - s_word_next = 1167, - s_word_buy = 1168, - s_word_sell = 1169, - s_word_release = 1170, - s_word_store_unlock = 1171, - s_word_system = 1172, - s_word_entire = 1173, - s_word_friend = 1174, - s_word_party = 1175, - s_word_guild = 1176, - s_word_increase = 1177, - s_word_man = 1178, - s_word_woman = 1179, - s_word_man_or_woman = 1180, - s_word_usercount = 1181, - s_word_stat_increase = 1182, - s_word_stat_increase_near_zero = 1183, - s_word_stat_max = 1184, - s_word_exp = 1185, - s_word_stat_hp = 1186, - s_word_stat_sp = 1187, - s_word_stat_ep = 1188, - s_word_stat_msp = 1189, - s_word_stat_str = 1190, - s_word_stat_dex = 1191, - s_word_stat_int = 1192, - s_word_stat_luk = 1193, - s_word_stat_pap = 1194, - s_word_stat_map = 1195, - s_word_stat_par = 1196, - s_word_stat_mar = 1197, - s_word_stat_asp = 1198, - s_word_stat_atp = 1199, - s_word_stat_cap = 1200, - s_word_stat_cad = 1201, - s_word_stat_ndd = 1202, - s_word_stat_evp = 1203, - s_word_stat_car = 1204, - s_word_stat_abp = 1205, - s_word_stat_jmp = 1206, - s_word_stat_hp_rgp = 1207, - s_word_stat_hp_inv = 1208, - s_word_stat_sp_rgp = 1209, - s_word_stat_sp_inv = 1210, - s_word_stat_ep_rgp = 1211, - s_word_stat_ep_inv = 1212, - s_word_stat_wap = 1213, - s_word_stat_dmg = 1214, - s_word_stat_pen = 1215, - s_word_stat_rmsp = 1216, - s_word_stat_bap = 1217, - s_word_stat_bap_pet = 1218, - s_word_stat_hp_v = 1219, - s_word_stat_sp_v = 1220, - s_word_stat_ep_v = 1221, - s_word_stat_msp_v = 1222, - s_word_stat_str_v = 1223, - s_word_stat_dex_v = 1224, - s_word_stat_int_v = 1225, - s_word_stat_luk_v = 1226, - s_word_stat_pap_v = 1227, - s_word_stat_map_v = 1228, - s_word_stat_par_v = 1229, - s_word_stat_mar_v = 1230, - s_word_stat_asp_v = 1231, - s_word_stat_atp_v = 1232, - s_word_stat_cap_v = 1233, - s_word_stat_cad_v = 1234, - s_word_stat_ndd_v = 1235, - s_word_stat_evp_v = 1236, - s_word_stat_car_v = 1237, - s_word_stat_abp_v = 1238, - s_word_stat_jmp_v = 1239, - s_word_stat_hp_rgp_v = 1240, - s_word_stat_hp_inv_v = 1241, - s_word_stat_sp_rgp_v = 1242, - s_word_stat_sp_inv_v = 1243, - s_word_stat_ep_rgp_v = 1244, - s_word_stat_ep_inv_v = 1245, - s_word_stat_wap_d_v = 1246, - s_word_stat_wap_u_v = 1247, - s_word_stat_wap_common_v = 1248, - s_word_stat_dmg_v = 1249, - s_word_stat_pen_v = 1250, - s_word_stat_rmsp_v = 1251, - s_word_stat_bap_v = 1252, - s_word_stat_bap_pet_v = 1253, - s_word_stat_hp_r = 1254, - s_word_stat_sp_r = 1255, - s_word_stat_ep_r = 1256, - s_word_stat_msp_r = 1257, - s_word_stat_str_r = 1258, - s_word_stat_dex_r = 1259, - s_word_stat_int_r = 1260, - s_word_stat_luk_r = 1261, - s_word_stat_pap_r = 1262, - s_word_stat_map_r = 1263, - s_word_stat_par_r = 1264, - s_word_stat_mar_r = 1265, - s_word_stat_asp_r = 1266, - s_word_stat_atp_r = 1267, - s_word_stat_cap_r = 1268, - s_word_stat_cad_r = 1269, - s_word_stat_ndd_r = 1270, - s_word_stat_evp_r = 1271, - s_word_stat_car_r = 1272, - s_word_stat_abp_r = 1273, - s_word_stat_jmp_r = 1274, - s_word_stat_hp_rgp_r = 1275, - s_word_stat_hp_inv_r = 1276, - s_word_stat_sp_rgp_r = 1277, - s_word_stat_sp_inv_r = 1278, - s_word_stat_ep_rgp_r = 1279, - s_word_stat_ep_inv_r = 1280, - s_word_stat_wap_d_r = 1281, - s_word_stat_wap_u_r = 1282, - s_word_stat_wap_common_r = 1283, - s_word_stat_dmg_r = 1284, - s_word_stat_pen_r = 1285, - s_word_stat_rmsp_r = 1286, - s_word_stat_bap_r = 1287, - s_word_stat_bap_pet_r = 1288, - s_word_stat_tnap = 1289, - s_word_stat_twap = 1290, - s_word_stat_weapon_damage = 1291, - s_word_rank_no = 1292, - s_word_rank_01 = 1293, - s_word_rank_02 = 1294, - s_word_rank_03 = 1295, - s_word_rank_04 = 1296, - s_word_rank_05 = 1297, - s_word_rank_06 = 1298, - s_word_rank_bonus = 1299, - s_word_item_sk = 1300, - s_word_item_hr = 1301, - s_word_item_fa = 1302, - s_word_item_fd = 1303, - s_word_item_lh = 1304, - s_word_item_rh = 1305, - s_word_item_cp = 1306, - s_word_item_mt = 1307, - s_word_item_cl = 1308, - s_word_item_pa = 1309, - s_word_item_gl = 1310, - s_word_item_sh = 1311, - s_word_item_fh = 1312, - s_word_item_ey = 1313, - s_word_item_ea = 1314, - s_word_item_pd = 1315, - s_word_item_ri = 1316, - s_word_item_be = 1317, - s_word_item_er = 1318, - s_word_item_bu = 1319, - s_word_item_de = 1320, - s_word_item_oh = 1321, - s_word_item_look = 1322, - s_word_item_equip = 1323, - s_word_item_category_clothes = 1324, - s_word_item_category_construction = 1325, - s_word_item_unlimited_period = 1326, - s_word_item_soldout = 1327, - s_word_item_soldout_html = 1328, - s_word_item_limit_sell = 1329, - s_word_item_limit_trade = 1330, - s_word_item_limit_sell_trade = 1331, - s_word_item_limit_blackmarket = 1332, - s_word_item_limit_blackmarket_sell = 1333, - s_word_item_limit_trade_count = 1334, - s_word_item_limit_trade_count_without_sell = 1335, - s_word_item_limit_trade_count_without_sell_for_itemlock = 1336, - s_word_item_limit_bind = 1337, - s_word_item_limit_bind_format = 1338, - s_word_item_notuse = 1339, - s_word_item_notuse_hpfull = 1340, - s_word_item_notuse_spfull = 1341, - s_word_item_notuse_epfull = 1342, - s_word_iconcode_none = 1343, - s_word_iconcode_weapon = 1344, - s_word_iconcode_armor = 1345, - s_word_iconcode_accessory = 1346, - s_word_iconcode_active = 1347, - s_word_iconcode_passive = 1348, - s_word_iconcode_potion = 1349, - s_word_iconcode_scroll = 1350, - s_word_iconcode_action = 1351, - s_word_iconcode_etc = 1352, - s_word_iconcode_land = 1353, - s_word_iconcode_building = 1354, - s_word_iconcode_interior = 1355, - s_word_iconcode_souvenir = 1356, - s_word_iconcode_pet = 1357, - s_word_iconcode_riding = 1358, - s_word_iconcode_mannequin = 1359, - s_word_iconcode_store = 1360, - s_word_iconcode_electronics = 1361, - s_word_iconcode_coupon = 1362, - s_word_iconcode_action_skillbook = 1363, - s_word_iconcode_gem = 1364, - s_word_iconcode_storybook = 1365, - s_word_iconcode_maid = 1366, - s_word_iconcode_package = 1367, - s_word_iconcode_random = 1368, - s_word_iconcode_crystal = 1369, - s_word_iconcode_airtaxi = 1370, - s_word_iconcode_dungeonkey = 1371, - s_word_iconcode_workbench_cook = 1372, - s_word_iconcode_workbench_alchemy = 1373, - s_word_iconcode_workbench_biz = 1374, - s_word_iconcode_buffportion = 1375, - s_word_iconcode_trigger_controller = 1376, - s_word_iconcode_interior_pack = 1377, - s_word_iconcode_fishing_rod = 1378, - s_word_iconcode_music_note = 1379, - s_word_iconcode_music_instrument = 1380, - s_word_iconcode_petfood = 1381, - s_word_iconcode_bait = 1382, - s_word_iconcode_gemstone = 1383, - s_word_iconcode_jeweldust = 1384, - s_word_iconcode_coin = 1385, - s_word_iconcode_quest = 1386, - s_word_iconcode_glide_item = 1387, - s_word_iconcode_petequip = 1388, - s_word_iconcode_blueprint = 1389, - s_word_iconcode_capsule = 1390, - s_word_week_sun = 1391, - s_word_week_mon = 1392, - s_word_week_tue = 1393, - s_word_week_wed = 1394, - s_word_week_thu = 1395, - s_word_week_fri = 1396, - s_word_week_sat = 1397, - s_word_week_sun2 = 1398, - s_word_week_mon2 = 1399, - s_word_week_tue2 = 1400, - s_word_week_wed2 = 1401, - s_word_week_thu2 = 1402, - s_word_week_fri2 = 1403, - s_word_week_sat2 = 1404, - s_word_date_week = 1405, - s_word_customize_length = 1406, - s_word_customize_size = 1407, - s_word_customize_location = 1408, - s_word_customize_angle = 1409, - s_word_customize_change = 1410, - s_word_customize_no_edit = 1411, - s_word_customize_comma = 1412, - s_word_game_over = 1413, - s_word_unknown = 1414, - s_word_welcome = 1415, - s_word_item_title_none = 1416, - s_word_item_title_kill = 1417, - s_word_item_title_master = 1418, - s_word_item_title_create = 1419, - s_word_item_title_status = 1420, - s_word_item_title_hidden = 1421, - s_word_item_damage = 1422, - s_word_item_stat_zero = 1423, - s_word_unuse = 1424, - s_word_screenshot = 1425, - s_cannot_move_to_npc = 1426, - s_word_ch = 1427, - s_word_online = 1428, - s_word_offline = 1429, - s_word_dungeon = 1430, - s_word_newTab = 1431, - s_word_level = 1432, - s_word_level_max = 1433, - s_word_learn_level = 1434, - s_word_learn_skillbook = 1435, - s_word_acquire_level = 1436, - s_word_cooltime = 1437, - s_word_needs = 1438, - s_word_fitness = 1439, - s_word_disable_job = 1440, - s_word_sellprice = 1441, - s_word_sellremind_format = 1442, - s_word_require_level = 1443, - s_word_equiped = 1444, - s_word_skillbook = 1445, - s_word_designed = 1446, - s_word_indoor_area = 1447, - s_word_indoor_only = 1448, - s_word_outdoor_only = 1449, - s_word_outdoor_indoor = 1450, - s_word_stackable = 1451, - s_word_do_not_stack = 1452, - s_word_cash = 1453, - s_word_skin = 1454, - s_word_count = 1455, - s_word_magic = 1456, - s_word_nearrange = 1457, - s_word_longrange = 1458, - s_word_taxistation = 1459, - s_word_besttaxistation = 1460, - s_word_telescope = 1461, - s_word_myhome = 1462, - s_word_myhome_property = 1463, - s_word_minimap_alpha = 1464, - s_word_mapbg_alpha = 1465, - s_word_exit_yes = 1466, - s_word_exit_no = 1467, - s_word_item = 1468, - s_word_trade = 1469, - s_word_cashshop = 1470, - s_word_blackmarket = 1471, - s_word_mesomarket = 1472, - s_word_meratmarket = 1473, - s_word_ugc = 1474, - s_word_ugc_banner = 1475, - s_word_homepage = 1476, - s_word_sunday = 1477, - s_word_monday = 1478, - s_word_tuesday = 1479, - s_word_wednesday = 1480, - s_word_thursday = 1481, - s_word_friday = 1482, - s_word_saturday = 1483, - s_word_chatting = 1484, - s_word_mail = 1485, - s_word_smart_push = 1486, - s_word_fishing = 1487, - s_word_safe_riding = 1488, - s_word_amphibious = 1489, - s_word_auto_play_instrument = 1490, - s_word_auto_mining = 1491, - s_word_auto_gathering = 1492, - s_word_auto_breeding = 1493, - s_word_auto_farming = 1494, - s_word_revival = 1496, - s_word_quest = 1497, - s_word_skill = 1498, - s_word_skill_active = 1499, - s_word_skill_passive = 1500, - s_word_skill_move = 1501, - s_word_element_physics = 1502, - s_word_element_fire = 1503, - s_word_element_ice = 1504, - s_word_element_lightning = 1505, - s_word_element_holy = 1506, - s_word_element_darkness = 1507, - s_word_element_poison = 1508, - s_word_min = 1509, - s_word_sec = 1510, - s_word_hour = 1511, - s_word_hour2 = 1512, - s_word_day = 1513, - s_word_remain = 1514, - s_word_group = 1515, - s_word_newgroup = 1516, - s_quest_chapter = 1517, - s_quest_talk_accept = 1518, - s_quest_talk_complete = 1519, - s_quest_talk_progress = 1520, - s_quest_talk_end = 1521, - s_quest_talk_reward_title = 1522, - s_quest_state_init = 1523, - s_quest_state_beginAble = 1524, - s_quest_state_progress = 1525, - s_quest_state_completeAble = 1526, - s_quest_state_complete = 1527, - s_quest_main_quest = 1528, - s_quest_sub_quest = 1529, - s_quest_event_quest = 1530, - s_quest_repeat_infinite = 1531, - s_quest_repeat_daily = 1532, - s_quest_replace_npc = 1533, - s_quest_replace_talk = 1534, - s_quest_replace_quest_object = 1535, - s_quest_replace_field = 1536, - s_quest_replace_item = 1537, - s_quest_replace_satisfied = 1538, - s_mission_replace_npc_meso = 1539, - s_quest_replace_item_move = 1540, - s_quest_not_selected_reward = 1541, - s_quest_boss_notify = 1542, - s_quest_boss_notify_chat = 1543, - s_quest_boss_notify_quest = 1544, - s_quest_error_inventory_full = 1545, - s_quest_error_consume_fail = 1546, - s_quest_error_accept_fail = 1547, - s_quest_error_count_limit = 1548, - s_quest_error_invalid_date = 1549, - s_quest_scroll_progress_quest = 1550, - s_quest_scroll_invalid_begin_quest = 1551, - s_quest_scroll_inventory_full = 1552, - s_quest_scroll_use_item = 1553, - s_title_scroll_duplicate_err = 1554, - s_buddy_confirm_del_somebody_from_list = 1555, - s_buddy_confirm_del_somebody_from_banlist = 1556, - s_buddy_confirm_ban_somebody = 1557, - s_buddy_add_somebody = 1558, - s_buddy_ban_somebody = 1559, - s_buddy_ban_memo_complete = 1560, - s_buddy_request_to_somebody = 1561, - s_buddy_decline_request_from_somebody = 1562, - s_buddy_refused_request_from_somebody = 1563, - s_buddy_cancel_request_from_somebody = 1564, - s_buddy_cancel_request = 1565, - s_buddy_del_somebody_from_list = 1566, - s_buddy_del_somebody_from_banlist = 1567, - s_buddy_alert_receive_request = 1568, - s_buddy_alert_online_somebody = 1569, - s_buddy_alert_offline_somebody = 1570, - s_buddy_request_default_msg = 1571, - s_buddy_waiting = 1572, - s_buddy_format_ch_map = 1573, - s_buddy_format_offline = 1574, - s_buddy_format_mapuser_count = 1575, - s_buddy_format_recv_count = 1576, - s_buddy_format_ban_count = 1577, - s_buddy_format_buddy_count = 1578, - s_buddy_err_unknown = 1579, - s_buddy_err_miss_id = 1580, - s_buddy_err_empty_id = 1581, - s_buddy_err_my_id = 1582, - s_buddy_err_my_id_ex = 1583, - s_buddy_err_not_exist_id = 1584, - s_buddy_err_ban_buddy_add = 1585, - s_buddy_err_already_receive = 1586, - s_buddy_err_max_block = 1587, - s_buddy_err_request_somebody = 1588, - s_buddy_err_max_buddy = 1589, - s_buddy_err_target_full = 1590, - s_buddy_err_already_request = 1591, - s_buddy_err_already_ban = 1592, - s_buddy_err_already_buddy = 1593, - s_buddy_err_already_request_somebody = 1594, - s_buddy_err_already_receive_from_this_char = 1595, - s_format_meso = 1596, - s_format_exp = 1597, - s_format_item = 1598, - s_format_reward_exp = 1599, - s_format_reward_meso = 1600, - s_msg_worldmap_title = 1601, - s_change_ch_err_field = 1602, - s_change_ch_err_battle = 1603, - s_change_ch_err_dead = 1604, - s_action_talk_normal = 1605, - s_action_talk_normal_cinematic = 1606, - s_action_talk_shop = 1607, - s_action_talk_store = 1608, - s_action_talk_mail = 1609, - s_action_enter_cube = 1610, - s_action_portal = 1611, - s_action_interact = 1612, - s_action_buy_site = 1613, - s_action_sale_apartment = 1614, - s_action_sell_cube = 1615, - s_action_regist_ugc = 1616, - s_action_construct = 1617, - s_action_changejob = 1618, - s_action_lift_cube = 1619, - s_action_ride = 1620, - s_action_pull = 1621, - s_action_putdown_liftable = 1622, - s_action_switchcube = 1623, - s_action_breedingcube = 1624, - s_action_farmingcube = 1625, - s_action_harvest = 1626, - s_action_harvesting = 1627, - s_action_remain_time = 1628, - s_action_breeding_growing = 1629, - s_action_farming_growing = 1630, - s_action_fittingdoll = 1631, - s_action_use_telescope = 1632, - s_action_view_cube_profile = 1633, - s_action_owner = 1634, - s_action_taxi_call = 1635, - s_action_taxi_call_progress = 1636, - s_action_cash_taxi_call_progress = 1637, - s_action_regist_banner = 1638, - s_action_fusion = 1639, - s_action_summon_pet = 1640, - s_action_summon_ridee = 1641, - s_action_goto_home = 1642, - s_action_hold = 1643, - s_action_hold_end = 1644, - s_action_cant_pet = 1645, - s_action_cant_ride = 1646, - s_action_privilege_portal = 1647, - s_action_recall_otheruser = 1648, - s_action_fishing = 1649, - s_action_fishing_try = 1650, - s_action_bank_call_progress = 1651, - s_action_webopen = 1652, - s_action_banner = 1653, - s_action_banner_rps = 1654, - s_action_reactor = 1655, - s_action_pickup = 1656, - s_action_homebank_call_progress = 1657, - s_err_homebank_cannot_now = 1658, - s_err_homebank_cannot_now_place = 1659, - s_err_homebank_cannot_now_already = 1660, - s_action_homedoctor_call_progress = 1661, - s_err_homedoctor_cannot_now = 1662, - s_err_homedoctor_cannot_now_place = 1663, - s_cutscene_telescope_keycap = 1664, - s_cutscene_telescope_keydesc = 1665, - s_changejob_accept = 1666, - s_take_boat_accept = 1667, - s_resolve_panelty_accept = 1668, - s_sell_ugc_map_accept = 1669, - s_roulette_accept = 1670, - s_roulette_talk_skip = 1671, - s_html_chat_super = 1672, - s_html_chat_world = 1673, - s_html_chat_channel = 1674, - s_html_chat_normal = 1675, - s_html_chat_party = 1676, - s_html_chat_guild = 1677, - s_html_chat_whisper_to = 1678, - s_html_chat_whisper_from = 1679, - s_html_chat_notice = 1680, - s_html_chat_img_notice = 1681, - s_html_chat_linkable_title = 1682, - s_html_chat_system_notice = 1683, - s_html_chat_club = 1684, - s_html_chat_ugc_event = 1685, - s_html_chat_super_custom = 1686, - s_html_chat_guild_mega_phone = 1687, - s_html_chat_system = 1688, - s_html_chat_wedding = 1689, - s_html_chat_wedding_custom = 1690, - s_system_quest_reward_exp = 1691, - s_system_quest_reward_exp_with_hottimebonus = 1692, - s_system_quest_reward_meso = 1693, - s_system_quest_reward_meso_with_hottimebonus = 1694, - s_system_quest_reward_merat = 1695, - s_system_quest_condition = 1696, - s_system_get_item = 1697, - s_system_property_protection_time = 1698, - s_system_achieve_reward_title = 1699, - s_system_achieve_reward_field_enterance = 1700, - s_system_achieve_reward_shop_unlock = 1701, - s_system_achieve_reward_honor = 1702, - s_system_achieve_reward_karma = 1703, - s_system_achieve_reward_lu = 1704, - s_system_achieve_reward_habi = 1705, - s_html_quest_reward = 1706, - s_ugcmap_ok = 1707, - s_ugcmap_create_on_non_empty_area = 1708, - s_ugcmap_not_exist_craft_item = 1709, - s_ugcmap_not_owned_item = 1710, - s_ugcmap_cant_be_created = 1711, - s_ugcmap_cant_create_on_place = 1712, - s_ugcmap_no_base_cube = 1713, - s_ugcmap_dont_have_ownership = 1714, - s_ugcmap_cant_create_ground_on_ground = 1715, - s_ugcmap_cant_create_on_ground = 1716, - s_ugcmap_only_be_created_on_ground = 1717, - s_ugcmap_cant_stack_on = 1718, - s_ugcmap_db = 1719, - s_ugcmap_center = 1720, - s_ugcmap_no_wall_to_attach = 1721, - s_ugcmap_not_wall_attachable = 1722, - s_ugcmap_only_be_created_on_wall = 1723, - s_ugcmap_cant_attached_to_this_wall = 1724, - s_ugcmap_have_already_attached = 1725, - s_ugcmap_no_cube_to_remove = 1726, - s_ugcmap_cant_be_removed = 1727, - s_ugcmap_cant_remove_before_remove_all_stacked = 1728, - s_ugcmap_cant_remove_building_with_indoor_items = 1729, - s_ugcmap_can_be_remove_from_wall = 1730, - s_ugcmap_no_attached_object = 1731, - s_ugcmap_no_cube_to_rotate = 1732, - s_ugcmap_cant_rotate_default_cube = 1733, - s_ugcmap_no_cube_to_replace = 1734, - s_ugcmap_cant_be_replaced = 1735, - s_ugcmap_attached_cube_exist = 1736, - s_ugcmap_cant_replace_stackable_with_not_stackable = 1737, - s_ugcmap_not_a_buyable = 1738, - s_ugcmap_not_enough_money = 1739, - s_ugcmap_already_owned = 1740, - s_ugcmap_salable = 1741, - s_ugcmap_no_cube_to_lift = 1742, - s_ugcmap_cant_lift_ugc_cube = 1743, - s_ugcmap_cant_lift_salable = 1744, - s_ugcmap_cant_remove_default_cube = 1745, - s_ugcmap_cant_remove_cube_with_attached = 1746, - s_ugcmap_null_cube_item_info = 1747, - s_ugcmap_height_limit = 1748, - s_ugcmap_area_limit = 1749, - s_ugcmap_building_count = 1750, - s_ugcmap_not_for_sale = 1751, - s_ugcmap_no_more_room = 1752, - s_ugcmap_no_home = 1753, - s_ugcmap_my_house = 1754, - s_ugcmap_already_expired = 1755, - s_ugcmap_system_error = 1756, - s_ugcmap_have_equipitems = 1757, - s_ugcmap_cant_replace_same_cube = 1758, - s_ugcmap_cant_buy_more_than_two_house = 1759, - s_ugcmap_need_trophy = 1760, - s_ugcmap_cant_guide_build = 1761, - s_ugcmap_wall_direction_error = 1762, - s_ugcmap_lift_error_msg = 1763, - s_ugcmap_try_place_empty = 1764, - s_ugcmap_cant_replace_type = 1765, - s_ugcmap_cant_rotate_attached = 1766, - s_ugcmap_cant_create_under_attach = 1767, - s_ugcmap_cant_attach_upper_cube = 1768, - s_ugcmap_cant_replace_under_attach = 1769, - s_ugcmap_cant_place_maid = 1770, - s_ugcmap_only_place_on_the_floor = 1771, - s_ugcmap_not_extension_date = 1772, - s_ugcmap_need_extansion_pay = 1773, - s_ugcmap_expired_salable_group = 1774, - s_ugcmap_cant_sell_my_home_in_indoor = 1775, - s_ugcmap_blocked_salable_group = 1776, - s_ugcmap_retry_later = 1777, - s_ugcmap_waiting_for_cube_to_be_created = 1778, - s_ugcmap_waiting_for_cube_to_be_replaced = 1779, - s_ugcmap_waiting_for_cube_to_be_removed = 1780, - s_ugcmap_trigger_count = 1781, - s_ugcmap_no_owner_to_commend = 1782, - s_ugcmap_add_commend_home_fail_from_db = 1783, - s_ugcmap_cant_commend_myself = 1784, - s_ugcmap_cant_commend_duplicate = 1785, - s_ugcmap_ban_word_included = 1786, - s_ugcmap_not_my_house = 1787, - s_ugcmap_automatic_removal = 1788, - s_ugcmap_cant_take_interior_gift_more = 1789, - s_ugcmap_take_interior_gift_fail_from_db = 1790, - s_ugcmap_already_taken_interior_grade_gift = 1791, - s_ugcmap_area_level_extended_successfully = 1792, - s_ugcmap_height_level_extended_successfully = 1793, - s_ugcmap_area_level_shrink_successfully = 1794, - s_ugcmap_height_level_shrink_successfully = 1795, - [Description("You can only use a blueprint while in your home.")] - s_ugcmap_not_use_blueprint_item = 1796, - s_ugc_edit_homeless = 1797, - s_ugc_edit_different_indoorsize = 1798, - s_ugcmap_package_name = 1799, - s_ugcmap_package_description = 1800, - s_ugcmap_package_build_condition = 1801, - s_ugcmap_package_build_description = 1802, - s_ugcmap_buy_realestate = 1803, - s_ugcmap_sell_realestate = 1804, - s_ugcmap_use_sale_coupon_to_buy = 1805, - s_construct_up_limit = 1806, - s_construct_down_limit = 1807, - s_construct_only_indoor = 1808, - s_construct_only_outdoor = 1809, - s_construct_category = 1810, - s_construct_buy_price = 1811, - s_construct_require_interior_level = 1812, - s_construct_require_trophy = 1813, - s_construct_usecount_and_hascount = 1814, - s_construct_usecount_and_hascount_arg = 1815, - s_housing_point_interior_level = 1816, - s_tip_whisper = 1817, - s_mainplayinfo_build_count = 1818, - s_mainplayinfo_maid_count = 1819, - s_mainplayinfo_trigger_count = 1820, - s_warehouse_timeleft_day_normal = 1821, - s_warehouse_timeleft_day_red = 1822, - s_warehouse_timeleft_hour_red = 1823, - s_warehouse_timeleft_minute_red = 1824, - s_warehouse_timeleft_second_red = 1825, - s_warehouse_timeleft_timeover_red = 1826, - s_warehouse_accept = 1827, - s_warehouse_send_to_mail_sender = 1828, - s_warehouse_send_to_mail_title = 1829, - s_warehouse_send_to_mail_content = 1830, - s_worldmap_tooltip_channel = 1831, - s_worldmap_tooltip_myname = 1832, - s_worldmap_tooltip_myhome = 1833, - s_worldmap_tooltip_friend = 1834, - s_worldmap_tooltip_friendhome = 1835, - s_worldmap_tooltip_party = 1836, - s_worldmap_tooltip_level = 1837, - s_worldmap_tooltip_level_upper = 1838, - s_worldmap_tooltip_mapname = 1839, - s_worldmap_tooltip_mapname_low = 1840, - s_worldmap_tooltip_mapname_high = 1841, - s_worldmap_tooltip_fishingspot_low = 1842, - s_worldmap_tooltip_fishingspot_high = 1843, - s_worldmap_tooltip_boss = 1844, - s_worldmap_tooltip_channel_enabled = 1845, - s_worldmap_tooltip_channel_disabled = 1846, - s_worldmap_tooltip_channel_extra = 1847, - s_worldmap_tooltip_portal = 1848, - s_worldmap_tooltip_portal_time = 1849, - s_worldmap_tooltip_quest_completable = 1850, - s_worldmap_tooltip_quest_progress = 1851, - s_worldmap_tooltip_quest_ablenow = 1852, - s_worldmap_tooltip_quest_ableupper = 1853, - s_worldmap_tooltip_taxi_title = 1854, - s_worldmap_tooltip_taxi_price = 1855, - s_worldmap_tooltip_taxi_seal = 1856, - s_worldmap_tooltip_taxi_current = 1857, - s_worldmap_tooltip_taxi_other_continent = 1858, - s_worldmap_tooltip_open_now = 1859, - s_worldmap_tooltip_quest_epic = 1860, - s_worldmap_tooltip_quest_world = 1861, - s_worldmap_tooltip_shadowgate = 1862, - s_worldmap_dungeon_rank_level_proper = 1863, - s_worldmap_dungeon_rank_level_upper = 1864, - s_worldmap_dungeon_expire_date = 1865, - s_minimap_icon_quest_completable_epic = 1866, - s_minimap_icon_quest_completable_event = 1867, - s_minimap_icon_quest_completable_world = 1868, - s_minimap_icon_quest_completable_repeat = 1869, - s_minimap_icon_quest_ablenow_epic = 1870, - s_minimap_icon_quest_ablenow_event = 1871, - s_minimap_icon_quest_ablenow_world = 1872, - s_minimap_icon_quest_ablenow_repeat = 1873, - s_minimap_icon_quest_progress = 1874, - s_minimap_icon_quest_ableupper = 1875, - s_minimap_tooltip_function_npc = 1876, - s_minimap_tooltip_function_npc_1row = 1877, - s_minimap_tooltip_function_portal = 1878, - s_minimap_tooltip_portal = 1879, - s_minimap_tooltip_normal = 1880, - s_minimap_tooltip_normal_alignleft = 1881, - s_minimap_tooltip_quest_completable = 1882, - s_minimap_tooltip_quest_progress = 1883, - s_minimap_tooltip_quest_ablenow = 1884, - s_minimap_tooltip_quest_ableupper = 1885, - s_triggereditor_state_change = 1886, - s_triggereditor_delete_state_invalid = 1887, - s_triggereditor_delete_state_confirm = 1888, - s_triggereditor_modifiy_state_invalid = 1889, - s_triggereditor_delete_attribute = 1890, - s_triggereditor_modelsize_too_long = 1891, - s_triggereditor_modify_attribute_invalid = 1892, - s_triggereditor_new_trigger = 1893, - s_triggereditor_exit_trigger = 1894, - s_triggereditor_upload = 1895, - s_triggereditor_download = 1896, - s_msg_rclick_attach_trade = 1897, - s_msg_rclick_dettach_trade = 1898, - s_msg_rclick_puton_equip = 1899, - s_msg_rclick_putoff_equip = 1900, - s_msg_rclick_store_in = 1901, - s_msg_rclick_store_out = 1902, - s_msg_rclick_sell = 1903, - s_msg_rclick_attach_mail = 1904, - s_msg_rclick_dettach_mail = 1905, - s_msg_rclick_buy = 1906, - s_msg_rclick_putoff_qslot = 1907, - s_msg_rclick_puton_qslot = 1908, - s_msg_rclick_puton_doll = 1909, - s_msg_rclick_putoff_doll = 1910, - s_msg_rclick_upgrade = 1911, - s_msg_rclick_warehouse_out = 1912, - s_msg_rclick_item_break = 1913, - s_msg_rclick_blackmarket = 1914, - s_msg_rclick_play_instrument = 1915, - s_msg_dclick_usable = 1916, - s_msg_dclick_readable = 1917, - s_msg_lclick_usable = 1918, - s_msg_tooltip_compare_swap = 1919, - s_fusion_error = 1920, - s_record_error = 1921, - s_record_audio_error = 1922, - s_tooltip_exp = 1923, - s_tooltip_todayword = 1924, - s_tooltip_party_ch = 1925, - s_tooltip_party = 1926, - s_achieve_adventure = 1927, - s_achieve_combat = 1928, - s_achieve_life = 1929, - s_achieve_reward_item = 1930, - s_achieve_reward_meso = 1931, - s_achieve_reward_exp = 1932, - s_achieve_reward_function = 1933, - s_achieve_reward_title = 1934, - s_achieve_reward_merat = 1935, - s_achieve_reward_field_enterance = 1936, - s_achieve_reward_item_buy_auth = 1937, - s_achieve_meter = 1938, - s_achieve_killometer = 1939, - s_achieve_killometer_f = 1940, - s_achieve_get_achieve_daily = 1941, - s_achieve_get_achieve_hero_progress = 1942, - s_achieve_get_achieve_hero_complete = 1943, - s_achieve_grade = 1944, - s_achieve_state_progress = 1945, - s_achieve_state_complete = 1946, - s_achieve_tooltip_comment = 1947, - s_achieve_reset_account = 1948, - s_achieve_reset_char = 1949, - s_achieve_category_reward_count = 1950, - s_maid_change_maid_name = 1951, - s_maid_change_owner_name = 1952, - s_maid_default_owner_name = 1953, - s_maid_label_birth = 1954, - s_maid_label_constel = 1955, - s_maid_label_height = 1956, - s_maid_label_weight = 1957, - s_maid_label_like = 1958, - s_maid_label_hate = 1959, - s_maid_label_hobby = 1960, - s_maid_label_ownername = 1961, - s_maid_label_belongto = 1962, - s_maid_label_name = 1963, - s_maid_label_title = 1964, - s_maid_label_salary = 1965, - s_maid_need_salary = 1966, - s_maid_expire_date = 1967, - s_maid_expire_date_morning = 1968, - s_maid_expire_date_afternoon = 1969, - s_maid_expire_date_reserved_word = 1970, - s_maid_extend_date_reserved_word = 1971, - s_maid_affinity_gauge_level = 1972, - s_maid_affinity_gauge_level_max = 1973, - s_maid_affinity_gauge_affinity = 1974, - s_maid_affinity_gauge_affinity_max = 1975, - s_maid_edit_confirm = 1976, - s_maid_feel_normal = 1977, - s_maid_feel_good = 1978, - s_maid_feel_very_good = 1979, - s_maid_tooltip_maid_desc = 1980, - s_maid_tooltip_manufactured = 1981, - s_maid_tooltip_manufacturing = 1982, - s_maid_reserved_word_passed_days = 1983, - s_maid_reserved_word_pay_type_meso = 1984, - s_maid_reserved_word_pay_type_merat = 1985, - s_maid_recipe_item_option = 1986, - s_maid_recipe_item_option_range = 1987, - s_manufacture_require = 1988, - s_manufacture_require_over_max = 1989, - s_manufacture_detail = 1990, - s_manufacture_ingredient_count_satisfied = 1991, - s_manufacture_ingredient_count_need = 1992, - s_manufacture_title_specialty = 1993, - s_manufacture_title_status = 1994, - s_manufacture_title_status_tooltip = 1995, - s_manufacture_title_status_tooltip_max = 1996, - s_manufacture_progress_hour = 1997, - s_manufacture_progress_min = 1998, - s_manufacture_progress_sec = 1999, - s_manufacture_progress_left = 2000, - s_manufacture_cancel_confirm = 2001, - s_manufacture_complete = 2002, - s_manufacture_complete_jackpot = 2003, - s_manufacture_complete_by_merat = 2004, - s_manufacture_workbench_tooltip_cook_on = 2005, - s_manufacture_workbench_tooltip_cook_off = 2006, - s_manufacture_workbench_tooltip_alchemy_on = 2007, - s_manufacture_workbench_tooltip_alchemy_off = 2008, - s_manufacture_workbench_tooltip_biz_on = 2009, - s_manufacture_workbench_tooltip_biz_off = 2010, - s_manufacture_cinematic_title = 2011, - s_manufacture_cinematic_affinity = 2012, - s_manufacture_cinematic_affinity_grade_up = 2013, - s_manufacture_cinematic_mood_plus = 2014, - s_manufacture_cinematic_mood_minus = 2015, - s_manufacture_error_msg = 2016, - s_maid_recipe_list_title = 2017, - s_reveal_taxi_station = 2018, - s_interact_err_item = 2019, - s_function_reward_itemget = 2020, - s_cinematic_job_change = 2021, - s_interact_err_quest = 2022, - s_myhouse_address_with_room = 2023, - s_myhouse_name_with_room = 2024, - s_realestate_ask_contract = 2025, - s_realestate_expire_data = 2026, - s_realestate_extension_date = 2027, - s_realestate_ask_extention_merat = 2028, - s_realestate_ask_extention_meso = 2029, - s_realestate_ask_extention_item = 2030, - s_realestate_building_type_0 = 2031, - s_realestate_building_type_1 = 2032, - s_realestate_building_type_2 = 2033, - s_realestate_building_type_3 = 2034, - s_realestate_area = 2035, - s_realestate_install_building_count = 2036, - s_realestate_detail_expire_date = 2037, - s_realestate_detail_expire_time = 2038, - s_realestate_morning = 2039, - s_realestate_afternoon = 2040, - s_realestate_waiting_time = 2041, - s_realestate_date_string = 2042, - s_realestate_price_desc_money = 2043, - s_realestate_price_desc_item = 2044, - s_realestate_description = 2045, - s_realestate_sell_meso = 2046, - s_realestate_sell_merat = 2047, - s_realestate_sell_name = 2048, - s_realestate_sell_wrong = 2049, - s_realestate_sell_cant_sell_my_home_in_indoor = 2050, - s_address_popup_sale = 2051, - s_address_popup_waiting = 2052, - s_address_popup_noroom = 2053, - s_address_popup_myhome = 2054, - s_address_popup_privilege = 2055, - s_address_popup_meso_icon = 2056, - s_address_popup_merat_icon = 2057, - s_realestate_broker_default_buy = 2058, - s_realestate_broker_default_no_room = 2059, - s_realestate_broker_default_my_house = 2060, - s_realestate_broker_click_buy = 2061, - s_realestate_broker_click_no_room = 2062, - s_realestate_broker_click_my_house = 2063, - s_realestate_broker_sell_confirm = 2064, - s_realestate_broker_cant_buy_more_than_two_house_for_site = 2065, - s_realestate_broker_need_trophy = 2066, - s_realestate_this_is_my_apartment = 2067, - s_realestate_apartment_sold_out = 2068, - s_realestate_apartment_sold_out_desc = 2069, - s_realestate_contractable = 2070, - s_realestate_terms = 2071, - s_realestate_terms_text_content = 2072, - s_apartment_input_room_number = 2073, - s_apartment_myhome = 2074, - s_apartment_myhome_contract = 2075, - s_apartment_myhome_tooltip = 2076, - s_achieve_reward_tab = 2077, - s_achieve_reward_tab_great = 2078, - s_achieve_renderer_count = 2079, - s_achieve_renderer_count_time = 2080, - s_achieve_renderer_hour = 2081, - s_achieve_renderer_min = 2082, - s_notify_levelup = 2083, - s_notify_learn_skill = 2084, - s_notify_mail_reply = 2085, - s_notify_mail_reply_content = 2086, - s_notify_mail_remainDay = 2087, - s_notify_mail_remainDay_tooltip = 2088, - s_notify_mail_remainDay_today = 2089, - s_notify_mail_remainDay_tooltip_today = 2090, - s_notify_mail_remainDay_expire = 2091, - s_notify_achieve_reward_function = 2092, - s_notify_achieve_reward_deadfail = 2093, - s_notify_groupchat_invite = 2094, - s_notify_groupchat_invited = 2095, - s_notify_groupchat_leave = 2096, - s_notify_groupchat_login = 2097, - s_notify_groupchat_logout = 2098, - s_notify_groupchat_reject = 2099, - s_err_groupchat_maxjoin = 2100, - s_err_groupchat_null_target_user = 2101, - s_err_groupchat_join_exist = 2102, - s_err_groupchat_name_exist = 2103, - s_err_groupchat_maxgroup = 2104, - s_err_groupchat_add_member_target = 2105, - s_groupchat_list = 2106, - s_groupchat_exit = 2107, - s_tooltip_home_buff = 2108, - s_msg_error_realestate_name = 2109, - s_msg_error_realestate_name_ban_all = 2110, - s_msg_error_realestate_name_ban_text = 2111, - s_worldmap_go_myhome = 2112, - s_worldmap_go_myhome_no_station = 2113, - s_trade_request_success = 2114, - s_trade_recieved_request = 2115, - s_trade_decline = 2116, - s_trade_cancel = 2117, - s_trade_success = 2118, - s_trade_error_system = 2119, - s_trade_error_distance = 2120, - s_trade_error_timeout = 2121, - s_trade_error_already_request = 2122, - s_trade_error_trading_now = 2123, - s_trade_error_meso = 2124, - s_trade_error_latched = 2125, - s_trade_error_decline = 2126, - s_trade_error_itemcount = 2127, - s_trade_error_slotcount = 2128, - s_trade_error_itemnone = 2129, - s_trade_error_pvp = 2130, - s_trade_error_mapLimit = 2131, - s_trade_error_invalid_meso = 2132, - s_trade_error_target_property_protection_time = 2133, - s_trade_error_target_fatigue_penalty = 2134, - s_trade_error = 2135, - s_trade_error_meso_transfer_limited = 2136, - s_trade_error_meso_transfer_limited_other = 2137, - s_trade_error_meso_transfer_limited2 = 2138, - s_trade_error_meso_transfer_limited_other2 = 2139, - s_trade_error_meso_transfer_adv_level_limited = 2140, - s_trade_error_meso_transfer_adv_level_limited_other = 2141, - s_trade_meso = 2142, - s_trade_tradelimit = 2143, - s_trade_tradelimit_blackmarket = 2144, - s_trade_restrict = 2145, - s_trade_tradein = 2146, - s_trade_already_requested = 2147, - s_trade_unlatch_me = 2148, - s_trade_unlatch_oppnent = 2149, - s_trade_begin = 2150, - s_gameoption_init = 2151, - s_gameoption_init_all = 2152, - s_gameoption_init_current = 2153, - s_gameoption_quickslot_warning = 2154, - s_gameoption_modified_warning = 2155, - s_gameoption_keysetting_warning = 2156, - s_gameoption_quality_warning = 2157, - s_gameoption_restart_warning = 2158, - s_gameoption_keysetting_shortcutkey_warning1 = 2159, - s_gameoption_keysetting_shortcutkey_warning2 = 2160, - s_gameoption_keysetting_shortcutkey_warning3 = 2161, - s_gameoption_confirm_reset_layout = 2162, - s_gameoption_keysetting_modifiedkey = 2163, - s_gameoption_keysetting_usenot_gamepad = 2164, - s_gameoption_keysetting_usenot_keyboard = 2165, - s_dynamic_action_already_alloc = 2166, - s_dynamic_action_item_invalid = 2167, - s_dynamic_action_already_learn = 2168, - s_dynamic_action_err_shortcutkey = 2169, - s_dynamic_action_learn = 2170, - s_dynamic_action_learn_ok = 2171, - s_fittingdoll_error_invalid_owner = 2172, - s_fittingdoll_InventoryFull = 2173, - s_fittingdoll_InvalidDoll = 2174, - s_fittingdoll_InvalidItemType_Skin = 2175, - s_fittingdoll_InvalidItemType_Equip = 2176, - s_fittingdoll_InvalidItemType_PutonPc = 2177, - s_fittingdoll_InvalidItemType_PutonDoll = 2178, - s_fittingdoll_InvalidItem = 2179, - s_fittingdoll_InvalidItem_rule = 2180, - s_fittingdoll_Invaliditem_limit = 2181, - s_fittingdoll_Invaliditem_male = 2182, - s_fittingdoll_Invaliditem_female = 2183, - s_fittingdoll_Invalid_slot = 2184, - s_fittingdoll_Invalid_binding = 2185, - s_fittingdoll_Invalid_doll = 2186, - s_fittingdoll_Invalid_moveDisable = 2187, - s_fittingdoll_transform_done_all = 2188, - s_fittingdoll_transform_done_partially = 2189, - [Description("You cannnot use that right now.")] - s_home_returnable_invalid_state = 2190, - [Description("You cannot move to the house from here.")] - s_home_returnable_forbidden = 2191, - [Description("You're already there!")] - s_home_returnable_forbidden_to_sameplace = 2192, - s_home_returnable_homeless = 2193, - s_home_returnable_samefield = 2194, - s_home_returnable_cooldown = 2195, - s_home_returnable_fieldname = 2196, - s_home_returnable_newcharacter = 2197, - s_home_returnable_confirm_place = 2198, - s_home_returnable_confirm_last_place = 2199, - s_home_returnable_without_indoor = 2200, - s_home_visit_confirm = 2201, - s_home_visit_failed_dead = 2202, - s_home_visit_failed_common = 2203, - s_home_visit_failed_homeless = 2204, - s_home_visit_failed_disablemap = 2205, - s_home_visit_failed_myhome = 2206, - s_home_invite_confirm = 2207, - s_home_invite_accept = 2208, - s_home_invite_reject = 2209, - s_home_invite_acceptwait = 2210, - s_home_invite_logout = 2211, - s_home_invite_denybyauto = 2212, - s_home_invite_timeout = 2213, - [Description("You cannot invite yourself.")] - s_home_invite_self = 2214, - s_home_invite_cant_invite_now = 2215, - s_tutorial_shortcutkey_limit = 2216, - s_tutorial_itemputonoff_limit = 2217, - s_tutorial_itemdrop_limit = 2218, - s_tutorial_dialog_limit = 2219, - s_tutorial_skip_movie = 2220, - [Description("Invalid character.")] - s_fail_enterfield_invaliduser = 2221, - s_fail_enterfield_userfull = 2222, - s_fail_enterfield_event_already_start = 2224, - s_interact_result_auth = 2225, - s_interact_result_quest = 2226, - s_interact_result_party = 2227, - s_interact_result_privilege = 2228, - s_interact_result_unknown = 2229, - s_interact_result_mastery = 2230, - s_interact_find_new_telescope = 2231, - s_hunting_kill_boss = 2232, - s_hunting_npc_kill_boss = 2233, - s_mode_pvp_status_winner = 2234, - s_mode_pvp_status_challenger = 2235, - s_returnhome_enable = 2236, - s_returnhome_homeless = 2237, - s_returnhome_disable_cooldown = 2238, - s_returnhome_disable = 2239, - s_returnhome_enable_merat = 2240, - s_returnhome_homeless_merat = 2241, - s_returnhome_disable_cooldown_merat = 2242, - s_returnhome_disable_merat = 2243, - s_report_fail_no_context = 2244, - s_report_fail_no_reason = 2245, - s_report_fail_no_blind_profile_check = 2246, - s_report_really_report = 2247, - s_report_really_report_with_ban = 2248, - s_report_really_report_with_blind = 2249, - s_report_really_blind = 2250, - s_report_success = 2251, - s_report_fail = 2252, - s_report_blind_success = 2253, - s_report_cant_report_own_item = 2254, - s_report_ban_reason = 2255, - s_tencent_report_block_warning_user = 2256, - s_tencent_report_block_warning_ugc_field_banner = 2257, - s_tencent_report_block_warning_ugc_item = 2258, - s_ban_check_err_min_length = 2259, - s_ban_check_err_max_length = 2260, - s_ban_check_err_invalid_char = 2261, - s_ban_check_err_invalid_char_space = 2262, - s_ban_check_err_all_word = 2263, - s_ban_check_err_all_name = 2264, - s_ban_check_err_any_word = 2265, - s_ban_check_err_any = 2266, - s_ban_check_tabname_err_any_word = 2267, - s_ban_check_tabname_err_any = 2268, - s_ban_check_title_err_min_length = 2269, - s_ban_check_title_err_max_length = 2270, - s_ban_check_title_err_invalid_char = 2271, - s_ban_check_title_err_invalid_char_space = 2272, - s_ban_check_title_err_any_word = 2273, - s_ban_check_title_err_any = 2274, - s_ban_check_comment_err_any_word = 2275, - s_ban_check_comment_err_any = 2276, - s_nes_adminRoomEnterFailed = 2277, - s_maintenance_title = 2278, - s_maintenance_modify_enable = 2279, - s_chat_restrict_samechat = 2280, - s_chat_restrict_fastchat = 2281, - s_chat_restrict_addtime = 2282, - s_chat_restrict_accumwarning = 2283, - s_chat_unknown_command = 2284, - s_chat_whisper_decline = 2285, - s_portal_invincible_effect_name = 2286, - s_portal_invincible_effect_description = 2287, - s_revival_effect_name = 2288, - s_revival_effect_description = 2289, - s_revival_invincible_effect_name = 2290, - s_revival_invincible_effect_description = 2291, - s_err_system_title = 2292, - s_err_system_check_graphic_driver = 2293, - s_err_system_graphic_error0 = 2294, - s_err_system_graphic_error1 = 2295, - s_err_system_graphic_error2 = 2296, - s_err_system_graphic_error3 = 2297, - s_err_system_graphic_error4 = 2298, - s_err_system_graphic_error5 = 2299, - s_err_system_graphic_error6 = 2300, - s_err_system_graphic_error7 = 2301, - s_err_system_graphic_error8 = 2302, - s_err_system_graphic_error9 = 2303, - s_err_system_graphic_error10 = 2304, - s_err_system_graphic_error11 = 2305, - s_err_system_graphic_error12 = 2306, - s_err_system_graphic_error13 = 2307, - s_err_system_graphic_error14 = 2308, - s_err_system_graphic_error15 = 2309, - s_err_system_graphic_error16 = 2310, - s_err_system_graphic_error17 = 2311, - s_err_system_graphic_error18 = 2312, - s_msg_restriction_special_chatting_need_acc_char_level = 2313, - s_msg_restriction_special_chatting_need_acc_adv_level = 2314, - s_msg_restriction_special_chatting_over_daily_usage_limit = 2315, - s_msg_restriction_mail_send_need_acc_char_level = 2316, - s_msg_restriction_mail_send_need_acc_adv_level = 2317, - s_msg_restriction_mail_send_over_daily_usage_limit = 2318, - s_msg_restriction_whisper_chatting_need_acc_char_level = 2319, - s_msg_restriction_whisper_chatting_need_acc_adv_level = 2320, - s_msg_restriction_group_chatting_need_acc_char_level = 2321, - s_msg_restriction_group_chatting_need_acc_adv_level = 2322, - s_msg_restriction_ugc_banner_need_acc_char_level = 2323, - s_msg_restriction_ugc_banner_need_acc_adv_level = 2324, - s_admin_character_name = 2325, - s_itembreak_invalid_item = 2326, - s_itembreak_invalid_donation_item = 2327, - s_itembreak_unknown = 2328, - s_itembreak_excellent_item = 2329, - s_itembreak_donation_excellent_item = 2330, - s_recall_user_notify_recall = 2331, - s_recall_user_notify_confirm_recall = 2332, - s_recall_user_notify_recalled = 2333, - s_recall_user_notify_not_found = 2334, - s_recall_user_notify_cannot_move = 2335, - s_recall_user_notify_reject = 2336, - s_recall_user_notify_already_recalled = 2337, - s_recall_user_confirm = 2338, - s_blackmarket_mail_to_sender = 2339, - s_blackmarket_mail_to_cancel_direct = 2340, - s_blackmarket_mail_to_cancel_expired = 2341, - s_blackmarket_mail_to_cancel_hide = 2342, - s_blackmarket_mail_to_seller_title = 2343, - s_blackmarket_mail_to_seller_title2 = 2344, - s_blackmarket_mail_to_seller_title_pending = 2345, - s_blackmarket_mail_to_seller_content = 2346, - s_blackmarket_mail_to_seller_content_soldout = 2347, - s_blackmarket_mail_to_vipseller_content = 2348, - s_blackmarket_mail_to_vipseller_content_soldout = 2349, - s_blackmarket_mail_to_seller_content2 = 2350, - s_blackmarket_mail_to_seller_content_pending2 = 2351, - s_blackmarket_mail_to_seller_content_pending3 = 2352, - s_blackmarket_mail_to_seller_content2_itemname = 2353, - s_blackmarket_mail_to_buyer_title = 2354, - s_blackmarket_mail_to_buyer_content = 2355, - s_blackmarket_mail_to_cancel_title = 2356, - s_blackmarket_mail_to_cancel_title_expired = 2357, - s_blackmarket_mail_to_cancel_title_hide = 2358, - s_blackmarket_mail_to_cancel_content = 2359, - s_blackmarket_mail_to_fail_add_title = 2360, - s_blackmarket_mail_to_fail_add_content = 2361, - s_blackmarket_notice_try_later = 2362, - s_blackmarket_price_1_unit = 2363, - s_blackmarket_upper_rank = 2364, - s_blackmarket_period = 2365, - s_blackmarket_openday_1day = 2366, - s_blackmarket_openday_2day = 2367, - s_blackmarket_openday_over_2day = 2368, - s_blackmarket_openday_with_hour = 2369, - s_blackmarket_openday_am = 2370, - s_blackmarket_openday_pm = 2371, - s_blackmarket_notice_close = 2372, - s_blackmarket_notice_sell_complete = 2373, - s_blackmarket_remain_time_hhmmss = 2374, - s_blackmarket_remain_time_mmss = 2375, - s_blackmarket_remain_time_ss = 2376, - s_blackmarket_shutdown = 2377, - s_blackmarket_notice_fee = 2378, - s_blackmarket_error_code = 2379, - s_blackmarket_error_disable_registitem = 2380, - s_blackmarket_error_close = 2381, - s_blackmarket_error_already_remove = 2382, - s_blackmarket_error_invalid_sale_price = 2383, - s_blackmarket_error_invalid_sale_price_range = 2384, - s_blackmarket_error_invalid_sale_count = 2385, - s_blackmarket_error_lack_sale_count = 2386, - s_blackmarket_error_max_register_item = 2387, - s_blackmarket_error_fail_register = 2388, - s_blackmarket_error_register_not_exist_in_inven = 2389, - s_blackmarket_error_already_add = 2390, - s_blackmarket_error_search_invalid_name = 2391, - s_blackmarket_error_search_invalid_count = 2392, - s_blackmarket_error_sell_restricted_by_user_level = 2393, - s_blackmarket_error_buy_restricted_by_user_level = 2394, - s_blackmarket_error_sell_restricted_by_char_cdate = 2395, - s_blackmarket_error_buy_restricted_by_char_cdate = 2396, - s_blackmarket_error_sell_restricted_by_user_level_ex = 2397, - s_blackmarket_error_buy_restricted_by_user_level_ex = 2398, - s_trade_error_send_restricted_by_user_level_ex = 2399, - s_trade_error_recv_restricted_by_user_level_ex = 2400, - s_itemdrop_error_restricted_by_user_level_ex = 2401, - s_itempickup_error_restricted_by_user_level_ex = 2402, - s_msg_blackmarket_complete_register = 2403, - s_msg_blackmarket_complete_remove = 2404, - s_msg_blackmarket_complete_buy = 2405, - s_msg_blackmarket_complete_stopsale = 2406, - s_msg_blackmarket_ask_buy = 2407, - s_msg_blackmarket_ask_stopsale = 2408, - s_msg_blackmarket_notice_discount_fee = 2409, - s_tooltip_blackmarket_sellprice = 2410, - s_talk_reward_title = 2411, - s_itemenchant_unknown_err = 2412, - s_itemenchant_invalid_item = 2413, - s_itemenchant_except_item = 2414, - s_itemenchant_damaged_item = 2415, - s_itemenchant_lack_ingredient = 2416, - s_itemenchant_fail_sec = 2417, - s_itemenchant_fail_min = 2418, - s_itemenchant_fail_hour = 2419, - s_itemenchant_fail_day = 2420, - s_itemenchant_fail_duration = 2421, - s_itemenchant_return_item = 2422, - s_itemenchant_messagebox = 2423, - s_itemenchant_cinematic_btn = 2424, - s_itemenchant_valid_item = 2425, - s_itemenchant_damaged_item_tooltip = 2426, - s_itemenchant_option_prop = 2427, - s_itemenchant_option_damage = 2428, - s_itemenchant_option_low_prop = 2429, - s_itemenchant_option_repair = 2430, - s_itemenchant_damage_warning = 2431, - s_itemenchant_success_notice = 2432, - s_itemenchant_use_protector = 2433, - s_itemremake_chat_maxoption = 2434, - s_item_repacking_limit_count = 2435, - s_item_repacking_item_consume_count = 2436, - s_item_repacking_item_limit_desc = 2437, - s_item_repacking_item_consume_desc = 2438, - s_item_repacking_scroll_success_dialog = 2439, - s_shadowworld_removebuff = 2440, - s_shadowworld_removebuff_before = 2441, - s_shadowworld_player_kill = 2442, - s_shadowworld_player_kill_slayer = 2443, - s_shadowworld_player_kill_ruler = 2444, - s_shadowworld_pkzone_msg = 2445, - s_shadowworld_pkzone_chat_msg = 2446, - s_shadowworld_safezone_msg = 2447, - s_shadowworld_safezone_chat_msg = 2448, - s_shadowworld_pkzone_allert = 2449, - s_shadowworld_kill_log_die = 2450, - s_shadowworld_kill_log_kill = 2451, - s_pvp_start = 2452, - s_pvp_ffa_double_kill = 2453, - s_pvp_ffa_triple_kill = 2454, - s_pvp_ffa_slayer_kill = 2455, - s_pvp_ffa_ruler_kill = 2456, - s_common_error_unknown = 2457, - s_common_day = 2458, - s_common_sec = 2459, - s_common_min = 2460, - s_common_hour = 2461, - s_common_min_sec = 2462, - s_common_hour_min = 2463, - s_common_hour_min_sec = 2464, - s_common_date_week = 2465, - s_common_max_size = 2466, - s_common_min_size = 2467, - s_common_text_count = 2468, - s_common_set = 2469, - s_common_meso = 2470, - s_common_merat = 2471, - s_common_cash = 2472, - s_common_payitem = 2473, - s_common_meratname_cash = 2474, - s_common_meratname_free = 2475, - s_common_meratname_market = 2476, - s_common_meratname_event = 2477, - s_common_percent = 2478, - s_bill_format_sumprice = 2479, - s_bill_format_sumprice_beauty = 2480, - s_bill_coupon_hair = 2481, - s_bill_coupon_face = 2482, - s_bill_coupon_makeup = 2483, - s_bill_coupon_skin = 2484, - s_bill_coupon_equip = 2485, - s_beauty_use_coupon = 2486, - s_bill_warning_coloring = 2487, - s_bill_meratname_total = 2488, - s_bill_meratprice_total = 2489, - s_field_limit_unknown = 2490, - s_field_limit_level_min = 2491, - s_field_limit_achieve = 2492, - s_field_limit_admin = 2493, - s_field_limit_content_chat = 2494, - s_room_increase_time = 2495, - s_room_increase_time_type2 = 2496, - s_room_decrease_time = 2497, - s_room_party_err_cooldown = 2498, - s_room_dungeon_time_up = 2499, - s_room_dungeon_cooldown = 2500, - s_shutdown_notify = 2501, - s_msg_shortcutkey_slot_full = 2502, - s_msg_shortcutkey_error = 2503, - s_bootypopup_buff_duration = 2504, - s_field_enteracne_dungeon_boss_tooltip = 2505, - s_field_enteracne_party_status_unknown = 2506, - s_field_enteracne_party_status_level = 2507, - s_field_enteracne_party_status_etc = 2508, - s_field_enteracne_party_notify_enter_cheif = 2509, - s_field_enteracne_party_notify_reset_dungeon = 2510, - s_field_enterance_solo_status = 2511, - s_field_enterance_party_status = 2512, - s_dungeonRoom_cooldown_nextDay = 2513, - s_dungeonRoom_cooldown_dayOfWeeks = 2514, - s_emergency_maintenance_remain_minute = 2515, - s_emergency_maintenance_remain_seconds = 2516, - s_emergency_maintenance_function_limit = 2517, - s_ugc_unload_banner_tooltip = 2518, - s_msg_tooltip_customize_color = 2519, - s_msg_tooltip_customize_buy_random_color = 2520, - s_msg_tooltip_limit_register_meratmarket = 2521, - s_msg_tooltip_enable_play_music = 2522, - s_msg_tooltip_musicscore_enable_playcount = 2523, - s_msg_tooltip_musicscore_disable_playcount = 2524, - s_msg_tooltip_musicscore_enable_playcount_custom_empty = 2525, - s_msg_tooltip_musicscore_custom_used_item_desc = 2526, - s_msg_tooltip_musicscore_custom_used_item_desc_guide = 2527, - s_msg_tooltip_musicscore_custom_empty_stat_desc = 2528, - s_msg_tooltip_musicscore_stat_desc = 2529, - s_msg_tooltip_musicscore_writer = 2530, - s_msg_tooltip_gemstone_enable_upgrade = 2531, - s_upper_hud_token_pocket = 2532, - s_upper_hud_token_pocket_honor = 2533, - s_upper_hud_token_pocket_karma = 2534, - s_upper_hud_token_pocket_lu = 2535, - s_upper_hud_token_pocket_habi = 2536, - s_upper_hud_merat = 2537, - s_upper_hud_merat_raw = 2538, - s_enter_ugcmap_result_no_room = 2539, - s_enter_ugcmap_result_not_exist_room = 2540, - s_enter_ugcmap_result_expired = 2541, - s_enter_ugcmap_result_not_exist_building = 2542, - s_ugc_edit_terms_text_content = 2543, - s_ugc_edit_terms_agree = 2544, - s_ugc_edit_demolish_agree = 2545, - s_ugc_edit_terms_coupon_consume = 2546, - s_ugc_item_edit_terms = 2547, - s_ugc_banner_terms = 2548, - s_ugc_profile_terms = 2549, - s_ugc_guildmark_terms = 2550, - s_ugc_guildposter_terms = 2551, - s_blackmarket_terms = 2552, - s_blackmarket_terms_text_content = 2553, - s_meratmarket_designers_terms = 2554, - s_meratmarket_designers_terms_text_content = 2555, - s_meratmarket_tooltip_sell_count = 2556, - s_meratmarket_tooltip_sell_date = 2557, - s_meratmarket_tooltip_bookmark = 2558, - s_meratmarket_tooltip_myshop_calculate = 2559, - s_meratmarket_complete_buy_item = 2560, - s_meratmarket_complete_buy_item_all = 2561, - s_meratmarket_complete_buy_item_error = 2562, - s_meratmarket_complete_buy_item_all_error = 2563, - s_meratmarket_complete_buy_item_all_with_error = 2564, - s_meratmarket_complete_check_balance_success = 2565, - s_meratmarket_emptytext_ad = 2566, - s_meratmarket_emptytext_not_exist = 2567, - s_meratmarket_error_result_buy_lack_merat = 2568, - s_meratmarket_error_result_buy_lack_empty_slot = 2569, - s_meratmarket_error_result_buy_own_product = 2570, - s_meratmarket_error_result_buy_price_index = 2571, - s_meratmarket_error_result_buy_sold_out = 2572, - s_meratmarket_error_result_buy_sold_out_premium_buylimit = 2573, - s_meratmarket_error_result_buy_change_price = 2574, - s_meratmarket_error_result_buy_count = 2575, - s_meratmarket_error_result_buy_not_sale = 2576, - s_meratmarket_error_result_already_add = 2577, - s_meratmarket_error_result_buy_error = 2578, - s_meratmarket_error_result_check_balance_delay_time = 2579, - s_meratmarket_buy_format = 2580, - s_meratmarket_buy_title = 2581, - s_meratmarket_buy_success_row = 2582, - s_meratmarket_buy_fail_row = 2583, - s_meratmarket_buy_success_all = 2584, - s_meratmarket_buy_success_with_fail = 2585, - s_meratmarket_buy_fail_all = 2586, - s_meratmarket_gift_title = 2587, - s_meratmarket_gift_success_row = 2588, - s_meratmarket_gift_fail_row = 2589, - s_meratmarket_gift_success_all = 2590, - s_meratmarket_gift_success_with_fail = 2591, - s_meratmarket_gift_fail_all = 2592, - s_meratmarket_error_lack_invenslot = 2593, - s_meratmarket_error_already_add_bookmark = 2594, - s_meratmarket_error_remove_state_sale = 2595, - s_meratmarket_error_calculating = 2596, - s_meratmarket_error_register_price = 2597, - s_meratmarket_error_register_invalid_category = 2598, - s_meratmarket_error_register_disable_ugcitem = 2599, - s_meratmarket_error_register_blocked_ugcitem = 2600, - s_meratmarket_error_bancheck_desc = 2601, - s_meratmarket_error_bancheck_tags = 2602, - s_meratmarket_error_common = 2603, - s_meratmarket_error_already_sale = 2604, - s_meratmarket_error_not_sale = 2605, - s_meratmarket_error_not_sale_expired = 2606, - s_meratmarket_error_search_result_empty = 2607, - s_meratmarket_error_search_result_empty_by_itemlink = 2608, - s_meratmarket_error_search_result_empty_by_banner = 2609, - s_meratmarket_error_search_result_with_ban = 2610, - s_meratmarket_error_buy_owner_product = 2611, - s_meratmarket_error_buy_soldout = 2612, - s_meratmarket_error_add_basket_by_buylimit = 2613, - s_meratmarket_error_buy_soldout_premium_buylimit = 2614, - s_meratmarket_bookmark_add_complete = 2615, - s_meratmarket_bookmark_limit_maxcount = 2616, - s_meratmarket_bookmark_input_search = 2617, - s_meratmarket_bookmark_empty_products = 2618, - s_meratmarket_basket_limit_maxcount = 2619, - s_meratmarket_basket_empty_buyall = 2620, - s_meratmarket_basket_complete_add = 2621, - s_meratmarket_myshop_notice_sell_restrict = 2622, - s_meratmarket_myshop_notice_sell_restrict_title = 2623, - s_meratmarket_myshop_notice_restrict_by_sellingamount = 2624, - s_meratmarket_myshop_sell_count = 2625, - s_meratmarket_myshop_history_buy_date = 2626, - s_meratmarket_myshop_limit_register_count = 2627, - s_meratmarket_myshop_notice_calculate = 2628, - s_meratmarket_myshop_ask_stopsale = 2629, - s_meratmarket_myshop_ask_remove = 2630, - s_meratmarket_myshop_empty_calculate = 2631, - s_meratmarket_myshop_history_row = 2632, - s_meratmarket_myshop_history_enable_collect = 2633, - s_meratmarket_myshop_history_schedule_collect = 2634, - s_meratmarket_myshop_history_title = 2635, - s_meratmarket_myshop_complete_stopsale = 2636, - s_meratmarket_myshop_complete_register = 2637, - s_meratmarket_myshop_complete_remove = 2638, - s_meratmarket_myshop_complete_resale = 2639, - s_meratmarket_myshop_complete_calculate = 2640, - s_meratmarket_free_resale_confirm = 2641, - s_meratmarket_register_limit_desc = 2642, - s_meratmarket_register_sale_day = 2643, - s_meratmarket_register_ad_hour_and_price = 2644, - s_meratmarket_register_royalty = 2645, - s_meratmarket_register_notice_price = 2646, - s_meratmarket_register_fee = 2647, - s_meratmarket_register_error_add_not_find = 2648, - s_meratmarket_register_error_add_already = 2649, - s_meratmarket_bill_total_fee = 2650, - s_meratmarket_priceday = 2651, - s_meratmarket_priceday_limitless = 2652, - s_meratmarket_gift_mail_sender = 2653, - s_meratmarket_gift_mail_title = 2654, - s_meratmarket_gift_mail_content = 2655, - s_meratmarket_notice_default = 2656, - s_meratmarket_error_result_gift_not_exist_user = 2657, - s_meratmarket_error_result_gift_target_admin = 2658, - s_meratmarket_error_result_gift_admin = 2659, - s_meratmarket_already_in_modelhouse = 2660, - s_meratmarket_moveto_modelhouse_forbidden = 2661, - s_mesoMarket_mail_to_sender = 2662, - s_mesoMarket_mail_to_cancel_title = 2663, - s_mesoMarket_mail_to_cancel_content = 2664, - s_mesoMarket_mail_to_seller_title = 2665, - s_mesoMarket_mail_to_seller_content = 2666, - s_mesoMarket_mail_to_buyer_title = 2667, - s_mesoMarket_mail_to_buyer_content = 2668, - s_mentoring_new_mentor_mail_sender = 2669, - s_mentoring_new_mentor_mail_title = 2670, - s_mentoring_new_mentor_mail_content = 2671, - s_cashshop_invenpickup = 2672, - s_cashshop_invenpickup_refund = 2673, - s_cashshop_refund = 2674, - s_cashshop_error_onetime_buy = 2675, - s_cashshop_expand_inven_warning = 2676, - s_cashshop_expand_inven_tab_warning = 2677, - s_cashshop_expand_character_slot_warning = 2678, - s_cashshop_dbclick_info = 2679, - s_cashshop_lack_balance = 2680, - s_cashshop_basket = 2681, - s_cashshop_buy_success = 2682, - s_cashshop_get_coupon_success = 2683, - s_cashshop_buy_success_send_mail = 2684, - s_cashshop_gift_success = 2685, - s_cashshop_refund_success = 2686, - s_cashshop_pickup_success = 2687, - s_cashshop_gift_message = 2688, - s_cashshop_gift_comfirm = 2689, - s_cashshop_err_unknown = 2690, - s_cashshop_err_closed = 2691, - s_cashshop_err_closed_pubtest = 2692, - s_cashshop_err_disconnect = 2693, - s_cashshop_err_not_ready_product = 2694, - s_cashshop_err_invalid_gift_name = 2695, - s_cashshop_err_same_account = 2696, - s_cashshop_err_no_empty_slot = 2697, - s_cashshop_err_no_empty_slot_pickup_jp = 2698, - s_cashshop_err_not_purchase_date = 2699, - s_cashshop_err_cannot_gift_product = 2700, - s_cashshop_err_cannot_refund_product = 2701, - s_cashshop_err_null_cash_product_data = 2702, - s_cashshop_err_null_nisms_product = 2703, - s_cashshop_err_requesting = 2704, - s_cashshop_err_invalid_authorization_token = 2705, - s_cashshop_err_balance_unknown = 2706, - s_cashshop_err_balance_block_user = 2707, - s_cashshop_err_balance_maintenance = 2708, - s_cashshop_err_balance_notfind_user = 2709, - s_cashshop_err_purchase_unknown = 2710, - s_cashshop_err_purchase_total_sale_count_over = 2711, - s_cashshop_err_purchase_sale_count_over = 2712, - s_cashshop_err_purchase_invalid_game = 2713, - s_cashshop_err_purchase_invalid_gift_product = 2714, - s_cashshop_err_purchase_eventcash_protect_gift = 2715, - s_cashshop_err_purchase_eventcash_protect_buy = 2716, - s_cashshop_err_purchase_maintenance = 2717, - s_cashshop_err_purchase_limit_purchase = 2718, - s_cashshop_err_purchase_lock_cashuse = 2719, - s_cashshop_err_purchase_block_user = 2720, - s_cashshop_err_purchase_notfind_user = 2721, - s_cashshop_err_purchase_lack_balance = 2722, - s_cashshop_err_purchase_invalid_cash_date = 2723, - s_cashshop_err_purchase_invalid_sale_product = 2724, - s_cashshop_err_purchase_invalid_cash_user = 2725, - s_cashshop_err_inventory_inquiry_unknown = 2726, - s_cashshop_err_inventory_inquiry_maintenance = 2727, - s_cashshop_err_inventory_pickup_unknown = 2728, - s_cashshop_err_inventory_pickup_no_inventory = 2729, - s_cashshop_err_inventory_pickup_lackcount = 2730, - s_cashshop_err_inventory_pickup_maintenance = 2731, - s_cashshop_err_refund_unknown = 2732, - s_cashshop_err_refund_disable_refund = 2733, - s_cashshop_err_refund_over_date = 2734, - s_cashshop_err_refund_no_inventory = 2735, - s_cashshop_err_refund_gift = 2736, - s_cashshop_err_gift_limit = 2737, - s_cashshop_err_gift_age = 2738, - s_cashshop_err_gift_send_block = 2739, - s_cashshop_err_gift_recv_block = 2740, - s_cashshop_err_gift_unknown = 2741, - s_cashshop_err_gift_sale_count_over = 2742, - s_partydps_notice_history_prefix = 2743, - s_partydps_notice_history_row = 2744, - s_partydps_notice_history_total_time = 2745, - s_msgbox_format_profile_char_detail = 2746, - s_messenger_comfirm_chat_super = 2747, - s_messenger_comfirm_chat_world = 2748, - s_messenger_comfirm_chat_channel = 2749, - s_messenger_no_show_messagebox = 2750, - s_messenger_whisper = 2751, - s_messenger_club = 2752, - s_messenger_party_chatballoon_prefix = 2753, - s_messenger_guild_chatballoon_prefix = 2754, - s_gem_error_inventory_full = 2755, - s_gem_error_InvalidSlot = 2756, - s_gem_error_InvalidItem = 2757, - s_gem_error_Expired = 2758, - s_gem_error_PCBang = 2759, - s_gem_error_unknown = 2760, - s_room_exit_ugc_indoor = 2761, - s_room_exit_bonus = 2762, - s_room_exit_massive_event = 2763, - s_room_exit_pvp_zone = 2764, - s_room_exit_msg_ugc_indoor = 2765, - s_room_exit_msg_boss_dungeon = 2766, - s_room_exit_msg_party_dungeon = 2767, - s_room_exit_msg_shadow_expedition_dungeon = 2768, - s_room_exit_msg_bonus = 2769, - s_room_exit_msg_massive_event = 2770, - s_room_exit_msg_pvp_zone = 2771, - s_room_exit_msg_pvp_field = 2772, - s_room_exit_msg_design = 2773, - s_room_exit_msg_blueprint = 2774, - s_room_exit_msg_blueprint_preview = 2775, - s_itemlink_meratmarket = 2776, - s_itemlink_cashshop = 2777, - s_waitingticket_message = 2778, - s_waitingticket_cancel = 2779, - s_debug_invincible = 2780, - s_debug_god_mode = 2781, - s_debug_invisible_all = 2782, - s_debug_invisible_npc = 2783, - s_debug_instant_death = 2784, - s_superchat_use_coupon = 2785, - s_worldchat_use_coupon = 2786, - s_channelchat_use_coupon = 2787, - s_weddingchat_use_coupon = 2788, - s_revival_use_coupon = 2789, - s_pet_name_change_use_coupon = 2790, - s_alreadyloginuser_request_kick = 2791, - s_alreadyloginuser_response_kick = 2792, - s_lauchingfestival_onetimecharacter = 2793, - s_pcbang_play_time1 = 2794, - s_pcbang_play_time2 = 2795, - s_pcbang_cant_tutorial_field = 2796, - s_pcbang_no_pcbang = 2797, - s_pcbang_get_gift_item = 2798, - s_pcbang_get_play_item = 2799, - s_play_time_warnning = 2800, - s_play_time_warnning_fatigue_half_begin = 2801, - s_play_time_warnning_fatigue_half = 2802, - s_play_time_warnning_fatigue_max = 2803, - s_play_time_warnning_chat = 2804, - s_nametag_symbol_none = 2805, - s_nametag_symbol_enchant = 2806, - s_nametag_symbol_seller = 2807, - s_nametag_symbol_guild = 2808, - s_nametag_symbol_architect = 2809, - s_nametag_symbol_trophy = 2810, - s_nametag_symbol_fisher = 2811, - s_nametag_symbol_musician = 2812, - s_nametag_symbol_balrog = 2813, - s_nametag_symbol_pioneer = 2814, - s_nametag_symbol_oneyear = 2815, - s_nametag_symbol_level = 2816, - s_nametag_symbol_enchant_desc = 2817, - s_nametag_symbol_seller_desc = 2818, - s_nametag_symbol_guild_desc = 2819, - s_nametag_symbol_architect_desc = 2820, - s_nametag_symbol_trophy_desc = 2821, - s_nametag_symbol_fisher_desc = 2822, - s_nametag_symbol_musician_desc = 2823, - s_nametag_symbol_balrog_desc = 2824, - s_nametag_symbol_pioneer_desc = 2825, - s_nametag_symbol_oneyear_desc = 2826, - s_nametag_symbol_level_desc = 2827, - s_fieldboss_reward_assistbonus_notify = 2828, - s_macro_penalty_reward_notify = 2829, - s_user_trigger_over_max_length = 2830, - s_user_trigger_over_max_state = 2831, - s_user_trigger_over_max_enter_action = 2832, - s_user_trigger_over_max_condition = 2833, - s_user_trigger_over_max_condition_action = 2834, - s_user_trigger_item_tooltip_enable_trigger_control = 2835, - s_user_trigger_item_tooltip_state_change_action_permission = 2836, - s_user_trigger_debugging_msg = 2837, - s_user_trigger_error_msg_system_error = 2838, - s_user_trigger_error_msg_cube_position = 2839, - s_user_trigger_ask_msg_rollback = 2840, - s_user_trigger_ask_msg_clear_contents = 2841, - s_user_trigger_ask_msg_save = 2842, - s_user_trigger_ask_msg_close = 2843, - s_user_trigger_msg_show_debug = 2844, - s_user_trigger_msg_rollback = 2845, - s_home_password_on_msg = 2846, - s_home_password_off_msg = 2847, - s_home_password_off_confirm = 2848, - s_home_password_input = 2849, - s_home_password_state_on = 2850, - s_home_password_state_off = 2851, - s_home_password_string_error = 2852, - s_home_password_mismatch = 2853, - s_home_password_block_time = 2854, - s_home_password_on_chat_msg = 2855, - s_home_password_off_chat_msg = 2856, - s_home_password_enter_field_chat_msg = 2857, - s_home_password_user_out_chat_msg = 2858, - s_home_password_expire_date_chat_msg = 2859, - s_home_password_user_out_button = 2860, - s_home_commend_success = 2861, - s_home_commend_send_confirm = 2862, - s_team_pvp_red_team_name = 2863, - s_team_pvp_blue_team_name = 2864, - s_team_pvp_north = 2865, - s_team_pvp_center = 2866, - s_team_pvp_south = 2867, - s_team_pvp_conquered = 2868, - s_team_pvp_red_player_plunder = 2869, - s_team_pvp_blue_player_plunder = 2870, - s_team_pvp_winner_reward = 2871, - s_team_pvp_loser_reward = 2872, - s_render_screen_restoration_failed = 2873, - s_render_graphiccard_error = 2874, - s_window_service_auth_error = 2875, - s_util_mem_depletion_url = 2876, - s_util_mem_depletion_phyx = 2877, - s_util_mem_depletion_default = 2878, - s_partysearch_sort_party_create_hi = 2879, - s_partysearch_sort_party_create_low = 2880, - s_partysearch_sort_party_level_hi = 2881, - s_partysearch_sort_party_level_low = 2882, - s_partysearch_sort_memeber_count_hi = 2883, - s_partysearch_sort_memeber_count_low = 2884, - s_partysearch_sort_memeber_create_hi = 2885, - s_partysearch_sort_memeber_create_low = 2886, - s_partysearch_complete_register_partysearch = 2887, - s_partysearch_complete_register_membersearch = 2888, - s_partysearch_complete_remove_partysearch = 2889, - s_partysearch_complete_remove_membersearch = 2890, - s_partysearch_ask_party_join_already_memebersearch = 2891, - s_partysearch_err_search_result_empty = 2892, - s_partysearch_err_search_result_with_ban = 2893, - s_partysearch_err_whisper_myself = 2894, - s_partysearch_err_party_join_myself = 2895, - s_partysearch_err_party_join_already = 2896, - s_partysearch_err_party_join_lack_gearscore = 2897, - s_partysearch_err_party_invite_myself = 2898, - s_partysearch_err_party_invite_not_chief = 2899, - s_partysearch_err_memebersearch_cant_register_not_chief = 2900, - s_partysearch_err_memebersearch_cant_register_max_memebercount = 2901, - s_partysearch_err_memebersearch_cant_register_limit_condition = 2902, - s_partysearch_err_memebersearch_cant_register_already = 2903, - s_partysearch_err_memebersearch_cant_modify_not_chief = 2904, - s_partysearch_err_memebersearch_cant_modify_no_register = 2905, - s_partysearch_err_partysearch_cant_register_in_party = 2906, - s_partysearch_err_partysearch_cant_register_limit_condition = 2907, - s_partysearch_err_partysearch_cant_register_already = 2908, - s_partysearch_err_partysearch_cant_modify_in_party = 2909, - s_partysearch_err_partysearch_cant_modify_no_register = 2910, - s_partysearch_err_dungeon_cooldown = 2911, - s_partysearch_err_server_lastaction = 2912, - s_partysearch_err_server_not_chief = 2913, - s_partysearch_err_server_max_member = 2914, - s_partysearch_err_server_db = 2915, - s_partysearch_err_server_already_register = 2916, - s_partysearch_err_server_invalid_type = 2917, - s_partysearch_err_server_in_party = 2918, - s_partysearch_err_server_registring = 2919, - s_partysearch_err_server_banword_title = 2920, - s_partysearch_err_server_banword_findword = 2921, - s_partysearch_err_server_party_invited = 2922, - s_partysearch_err_server_blocked = 2923, - s_partysearch_err_server_code = 2924, - s_partysearchregister_err_title = 2925, - s_partysearchregister_invalid_dungeonlevel = 2926, - s_partysearchregister_invalid_dungeondata = 2927, - s_partysearchregister_ask_remove = 2928, - s_err_cash_recall_cannot_now = 2929, - s_err_cash_recall_prohibit_map = 2930, - s_err_cash_recall_cannot_place = 2931, - s_err_cash_recall_not_guild = 2932, - s_err_cash_recall_not_party = 2933, - s_err_cash_recall_no_guildmember = 2934, - s_err_cash_recall_no_partymember = 2935, - s_err_cash_recall_no_weddingmember = 2936, - s_err_cash_recall_overflow = 2937, - s_err_cash_recall_cannot_dead = 2938, - s_err_cash_recall_cannot_battle = 2939, - s_cash_recall_confirm = 2940, - s_cash_recall_party_notice = 2941, - s_cash_recall_guild_notice = 2942, - s_cash_recall_expired_notice = 2943, - s_cash_recall_end_notice = 2944, - s_cash_recall_other_continent = 2945, - s_enchantscroll_openscroll = 2946, - s_enchantscroll_desc_enchant = 2947, - s_enchantscroll_desc_restore = 2948, - s_enchantscroll_desc_repair = 2949, - s_enchantscroll_desc_random_enchant = 2950, - s_enchantscroll_limit = 2951, - s_enchantscroll_itemname = 2952, - s_enchantscroll_breaking = 2953, - s_enchantscroll_successprop = 2954, - s_enchantscroll_ok = 2955, - s_enchantscroll_invalid_scroll = 2956, - s_enchantscroll_invalid_item = 2957, - s_enchantscroll_breaking_item = 2958, - s_enchantscroll_invalid_level = 2959, - s_enchantscroll_invalid_slot = 2960, - s_enchantscroll_invalid_rank = 2961, - s_enchantscroll_invalid_grade = 2962, - s_enchantscroll_not_breaking_item = 2963, - s_systemmail_notify_ontime_confirm = 2964, - s_server_name_scania = 2965, - s_server_name_mardia = 2966, - s_server_name_hasello = 2967, - s_server_name_windia = 2968, - s_server_name_flata = 2969, - s_recovery_mail_title = 2970, - s_recovery_mail_content = 2971, - s_recovery_mail_sender = 2972, - s_trade_recovery_mail_title = 2973, - s_trade_recovery_mail_content = 2974, - s_trade_recovery_mail_sender = 2975, - s_doll_recovery_mail_title = 2976, - s_doll_recovery_mail_content = 2977, - s_doll_recovery_mail_sender = 2978, - s_function_cube_error_invalid_cube = 2979, - s_function_cube_error_invalid_pos = 2980, - s_function_cube_error_invalid_summon_user = 2981, - s_err_ugcmap_package_cant_use = 2982, - s_err_ugcmap_package_cant_use_in_this_map = 2983, - [Description("Can only be used in the indoor space of the house.")] - s_err_ugcmap_package_should_use_in_indoor = 2984, - s_err_ugcmap_package_not_a_valid_package_item = 2985, - s_err_ugcmap_package_cant_use_in_others_home = 2986, - [Description("You must clear your home of furnishings first.")] - s_err_ugcmap_package_clear_indoor_first = 2987, - s_err_ugcmap_package_not_a_valid_indoor = 2988, - s_err_ugcmap_package_not_a_indoor_for_package_item = 2989, - s_err_ugcmap_package_not_a_indoor_for_this_package_item = 2990, - s_err_ugcmap_package_failed_to_consume_package_item = 2991, - s_err_ugcmap_package_automatic_creation_is_in_progress = 2992, - s_err_ugcmap_package_automatic_removal_is_in_progress = 2993, - [Description("'The saved design has been applied.")] - s_ugcmap_package_automatic_creation_completed = 2994, - [Description("The home's interior has been cleared of all furnishings.")] - s_ugcmap_package_automatic_removal_completed = 2995, - s_ugcmap_package_automatic_creation_suspended = 2996, - s_ugcmap_package_automatic_removal_suspended = 2997, - s_ugcmap_package_automatic_creation_skip = 2998, - s_ugcmap_package_open_package_when_indoor_size_is_different = 2999, - s_ugcmap_package_open_package_when_indoor_is_empty = 3000, - s_err_ugcmap_design_home_should_use_in_design_home = 3001, - s_err_ugcmap_design_home_not_my_design_home = 3002, - s_err_ugcmap_design_home_indoor_isnt_empty = 3003, - s_err_ugcmap_design_home_should_use_in_home = 3004, - s_err_ugcmap_design_home_nothing_to_import = 3005, - s_err_ugcmap_design_home_invalid_item = 3006, - s_err_ugcmap_design_home_not_enough_meso = 3007, - s_err_ugcmap_design_home_not_enough_merat = 3008, - s_err_ugcmap_design_home_not_enough_item = 3009, - s_err_ugcmap_design_home_not_enough_honor_token = 3010, - s_err_ugcmap_design_home_not_enough_karma_token = 3011, - s_err_ugcmap_design_home_not_enough_lu_token = 3012, - s_err_ugcmap_design_home_not_enough_habi_token = 3013, - s_err_ugcmap_design_home_not_enough_shard_token = 3014, - s_err_ugcmap_design_home_not_enough_red_merat = 3015, - s_err_ugcmap_design_home_not_enough_reverse_coin = 3016, - s_err_ugcmap_design_home_not_enough_star_point = 3017, - s_err_ugcmap_design_home_invalid_design_index = 3018, - s_err_ugcmap_cant_save_maid = 3019, - s_err_ugcmap_guide_object_cant_go_far = 3020, - s_err_ugcmap_cant_find_delegate_owner = 3021, - s_err_ugcmap_cant_use_clear_ugc_map = 3022, - s_err_ugcmap_not_massive_event_field = 3023, - s_err_ugcmap_only_owner_can_set_balance = 3024, - s_err_ugcmap_meso_balance_should_be_positive_number = 3025, - s_err_ugcmap_merat_balance_should_be_positive_number = 3026, - s_err_ugcmap_meso_merat_balance_description = 3027, - s_err_ugcmap_nothing_to_export = 3028, - s_fishing_grade_Lv1 = 3029, - s_fishing_grade_Lv2 = 3030, - s_fishing_grade_Lv3 = 3031, - s_fishing_grade_Lv4 = 3032, - s_fishing_grade_Lv5 = 3033, - s_fishing_grade_Lv6 = 3034, - s_fishing_grade_Lv7 = 3035, - s_fishing_grade_Lv8 = 3036, - s_fishing_grade_Lv9 = 3037, - s_fishing_grade_Lv10 = 3038, - s_fishing_grade_Lv11 = 3039, - s_fishing_grade_Lv12 = 3040, - s_fishing_grade_Lv13 = 3041, - s_fishing_grade_Lv14 = 3042, - s_fishing_grade_Lv15 = 3043, - s_fishing_grade_Lv16 = 3044, - s_fishing_grade_Lv17 = 3045, - s_fishing_grade_Lv18 = 3046, - s_fishing_grade_Lv19 = 3047, - s_fishing_grade_Lv20 = 3048, - s_fishing_grade_Lv21 = 3049, - s_fishing_habitat_water = 3050, - s_fishing_habitat_seawater = 3051, - s_fishing_habitat_poison = 3052, - s_fishing_habitat_lava = 3053, - s_fishing_habitat_oil = 3054, - s_fishing_habitat_devilwater = 3055, - s_fishing_habitat_emeraldwater = 3056, - s_fishing_habitat_all = 3057, - s_fishing_notify_success = 3058, - s_fishing_notify_fail = 3059, - s_fishing_fish_grade = 3060, - s_fishing_fish_catch_count = 3061, - s_fishing_fish_size = 3062, - s_fishing_error_invalid_cube = 3063, - s_fishing_error_notexist_water = 3064, - s_fishing_error_invalid_item = 3065, - s_fishing_error_lack_mastery = 3066, - s_fishing_error_notexist_fish = 3067, - s_fishing_error_system_error = 3068, - s_fishing_error_fishingrod_mastery = 3069, - s_fishing_error_ride = 3070, - s_fishing_error_inventory_full = 3071, - s_fishing_error_ugcmap = 3072, - s_fishing_offset_mastery = 3073, - s_fishing_offset_mastery_firstcatch = 3074, - s_fishing_offset_mastery_bigsizecatch = 3075, - s_fishing_total_mastery = 3076, - s_fishing_need_mastery = 3077, - s_fishing_size_small = 3078, - s_fishing_size_medium = 3079, - s_fishing_size_large = 3080, - s_fishing_size_bigfish = 3081, - s_remake_itemoption_error = 3082, - s_remake_itemoption_error_impossible = 3083, - s_remake_itemoption_error_limitlevel = 3084, - s_tooltip_itemoption_kinds_constant = 3085, - s_tooltip_itemoption_kinds_static = 3086, - s_tooltip_itemoption_kinds_random = 3087, - s_tooltip_itemoption_kinds_title = 3088, - s_tooltip_itemoption_kinds_space = 3089, - s_tooltip_itemoption_enchant = 3090, - s_tooltip_itemoption_enchant_broken = 3091, - s_tooltip_itemoption_stat_row = 3092, - s_tooltip_itemremake_count = 3093, - s_tooltip_itemremake_enable = 3094, - s_wedding_visit_confirm = 3095, - s_wedding_visit_failed_common = 3096, - s_wedding_visit_failed_dead = 3097, - s_wedding_visit_failed_invalid_time = 3098, - s_wedding_visit_failed_user_count_limit_exceeded = 3099, - s_wedding_visit_failed_user_self_link = 3100, - s_wedding_visit_failed_disablemap = 3101, - s_wedding_visit_failed_solo_instance = 3102, - s_wedding_visit_failed_same_place = 3103, - s_err_wedding_chat_cannot_use_common = 3104, - s_err_wedding_chat_cannot_use_no_reservation = 3105, - s_err_wedding_chat_cannot_use_complete = 3106, - s_anti_addiction_cannot_receive = 3107, - s_auto_itemuse_default = 3108, - s_auto_itemuse_condition = 3109, - s_pet_input_name = 3110, - s_pet_summon_on = 3111, - s_pet_summon_off = 3112, - s_pet_btn_summon_on = 3113, - s_pet_btn_summon_off = 3114, - s_pet_inventory_ask_in = 3115, - s_pet_inventory_ask_out = 3116, - s_pet_inventory_slot_use_count = 3117, - s_pet_inventory_expire = 3118, - s_pet_inventory_end_time = 3119, - s_pet_inventory_not_use = 3120, - s_pet_inventory_not_sendin = 3121, - s_pet_inventory_not_sendin_petitem = 3122, - s_pet_effect_end_time = 3123, - s_pet_additional_desc_name = 3124, - s_pet_change_name_free = 3125, - s_pet_change_name_merat = 3126, - s_pet_change_name_samename = 3127, - s_pet_itemuse_cannot_not_posion = 3128, - s_pet_itemuse_cannot_same_item = 3129, - s_pet_itemuse_cannot_same_condition = 3130, - s_pet_itemuse_cannot_notexist_condition = 3131, - s_pet_itemuse_cannot_drag_user_inventory = 3132, - s_pet_error_summon_potion = 3133, - s_pet_extension_ontarget = 3134, - s_pet_extension_period = 3135, - s_pet_extension_period_limit = 3136, - s_pet_extension_period_remain = 3137, - s_pet_extension_period_hungry = 3138, - s_pet_extension_period_not_extendlife = 3139, - s_pet_nutrient_ontarget = 3140, - s_pet_nutrient_period_limit = 3141, - s_pet_nutrient_confirm_replace_effect = 3142, - s_pet_nutrient_success = 3143, - s_pet_nutrient_success_extension = 3144, - s_pet_nutrient_cannot_hungry = 3145, - s_usercommanddescription_user = 3146, - s_usercommanddescription_tester = 3147, - s_usercommanddescription_admin = 3148, - s_timeevent_common_1 = 3149, - s_timeevent_common_4 = 3150, - s_timeevent_common_7 = 3151, - s_timestring_am = 3152, - s_timestring_pm = 3153, - s_skill_compact_control_add_tab = 3154, - s_skill_compact_control_rename_tab = 3155, - s_skill_compact_control_same_tabname = 3156, - s_skill_compact_control_default_tabname = 3157, - s_skill_level_max = 3158, - s_window_title_name = 3159, - s_crashreporter_title = 3160, - s_crashreporter_text = 3161, - s_gameevent_enterfield_confirm = 3162, - s_card_reverse_game_consume_fail = 3163, - s_card_reverse_game_select_count = 3164, - s_card_reverse_game_close_message = 3165, - s_card_reverse_game_reward_notice = 3166, - s_rank_duel_arena_fail = 3167, - s_invalid_break_skinitem = 3168, - s_itemlock_unknown_err = 3169, - s_itemlock_invalid_item_lock = 3170, - s_itemlock_invalid_item_unlock = 3171, - s_party_search_default_msg_1 = 3172, - s_party_search_default_msg_2 = 3173, - s_party_search_default_msg_3 = 3174, - s_party_search_default_msg_4 = 3175, - s_party_search_default_msg_5 = 3176, - s_character_ability_character_category_name = 3177, - s_character_ability_monster_and_dungeon_category_name = 3178, - s_character_ability_play_and_feature_category_name = 3179, - s_character_ability_quest_and_grow_category_name = 3180, - s_character_ability_content_category_name = 3181, - s_character_ability_character_category_tooltip = 3182, - s_character_ability_monster_and_dungeon_category_tooltip = 3183, - s_character_ability_play_and_feature_category_tooltip = 3184, - s_character_ability_quest_and_grow_category_tooltip = 3185, - s_character_ability_content_category_tooltip = 3186, - s_character_ability_err_no_abilitypoint = 3187, - s_character_ability_err_no_meso = 3188, - s_character_ability_err_no_merat = 3189, - s_character_ability_point_info_text = 3190, - s_character_ability_meso_reset_desc = 3191, - s_character_ability_meso_reset_button = 3192, - s_character_ability_merat_reset_desc = 3193, - s_character_ability_merat_reset_button = 3194, - s_character_ability_err_reset_cooltime = 3195, - s_character_ability_disabled_quest_desc = 3196, - s_character_ability_reset_cooltime = 3197, - s_character_ability_reset_cooltime_request = 3198, - s_character_ability_err_level_reset_cooltime = 3199, - s_party_recall_scroll_buy = 3200, - s_msg_autofishing_extend = 3201, - s_msg_autofishing_extend_desc = 3202, - s_msg_autofishing_extend_showcurrentduration_desc = 3203, - s_system_shop_title_extend_autofishing = 3204, - s_notice_bypass = 3205, - s_notice_unstable_network_state = 3206, - s_ugcmap_fun_host_gravity_change = 3207, - s_ugcmap_fun_card_info_user_open = 3208, - s_ugcmap_fun_card_info_user_private = 3209, - s_ugcmap_fun_card_info_deck_open = 3210, - s_ugcmap_fun_card_info_deck_private = 3211, - s_ugcmap_fun_card_info_mine = 3212, - s_ugcmap_fun_card_open_mine = 3213, - s_ugcmap_fun_card_discard_open = 3214, - s_ugcmap_fun_card_discard_private = 3215, - s_ugcmap_fun_card_discard_mine = 3216, - s_ugcmap_fun_card_verify_owned = 3217, - s_ugcmap_fun_card_verify_not_owned = 3218, - s_ugcmap_fun_card_handover_open = 3219, - s_ugcmap_fun_card_handover_private = 3220, - s_ugcmap_fun_card_handover_sender = 3221, - s_ugcmap_fun_card_handover_receiver = 3222, - s_ugcmap_fun_card_add = 3223, - s_ugcmap_fun_card_receive_open = 3224, - s_ugcmap_fun_card_receive_private = 3225, - s_ugcmap_fun_card_receive_mine = 3226, - s_ugcmap_fun_card_deck_not_exist = 3227, - s_ugcmap_fun_card_deck_reset = 3228, - s_ugcmap_fun_card_deck_reset_empty = 3229, - s_ugcmap_fun_card_not_exist = 3230, - s_ugcmap_fun_card_empty_in_deck = 3231, - s_ugcmap_fun_card_too_many_in_deck = 3232, - s_ugcmap_fun_card_target_not_exist = 3233, - s_ugcmap_fun_card_deck_name_empty = 3234, - s_ugcmap_fun_card_deck_name_invalid = 3235, - s_ugcmap_fun_card_deck_too_many = 3236, - s_ugcmap_fun_card_deck_exist_already = 3237, - s_ugcmap_fun_card_deck_is_permanent = 3238, - s_ugcmap_fun_card_deck_add = 3239, - s_ugcmap_fun_card_deck_discard = 3240, - s_ugcmap_fun_survey_open = 3241, - s_ugcmap_fun_survey_secret = 3242, - s_ugcmap_fun_survey_abstention = 3243, - s_ugcmap_fun_survey_guide = 3244, - s_ugcmap_fun_survey_info_head = 3245, - s_ugcmap_fun_survey_info_footer = 3246, - s_ugcmap_fun_survey_create_success = 3247, - s_ugcmap_fun_survey_create_error = 3248, - s_ugcmap_fun_survey_add_success = 3249, - s_ugcmap_fun_survey_add_error = 3250, - s_ugcmap_fun_survey_start = 3251, - s_ugcmap_fun_survey_vote = 3252, - s_ugcmap_fun_survey_end = 3253, - s_ugcmap_fun_survey_result = 3254, - s_ugcmap_fun_survey_close_without_vote = 3255, - s_ugcmap_fun_portal_name_duplicated = 3256, - s_ugcmap_fun_roll = 3257, - s_ugcmap_fun_roll_error = 3258, - s_ugcmap_fun_random_group = 3259, - s_ugcmap_fun_random_pick = 3260, - s_ugcmap_fun_random_pick_special = 3261, - s_ugcmap_fun_typing_ready = 3262, - s_ugcmap_fun_typing_begin = 3263, - s_ugcmap_fun_typing_submit = 3264, - s_ugcmap_fun_typing_end = 3265, - s_ugcmap_fun_typing_ranking = 3266, - s_ugcmap_fun_pvp_ffa_finished = 3267, - s_ugcmap_fun_pvp_ffa_finished_draw = 3268, - s_ugcmap_fun_pvp_ffa_winnter_name = 3269, - s_ugcmap_cant_extend_area_level_anymore = 3270, - s_ugcmap_cant_extend_height_level_anymore = 3271, - s_ugcmap_cube_lock = 3272, - s_ugcmap_cant_additionalbuy = 3273, - s_ugcmap_admin_only = 3274, - s_ugcmap_not_allowed_item = 3275, - s_err_ugcmap_not_enough_meso_balance = 3276, - s_err_ugcmap_not_enough_merat_balance = 3277, - s_err_ugcmap_cant_find_delegator_in_this_map = 3278, - s_err_ugcmap_cant_full_delegator_user = 3279, - s_err_ugcmap_cant_duplicate_delegator_in_this_map = 3280, - s_err_ugcmap_cant_build_empty_ugc = 3281, - s_err_ugcmap_should_use_in_home = 3282, - s_err_ugcmap_construct_exp_overtime = 3283, - s_ugcmap_add_delegator_user = 3284, - s_ugcmap_remove_delegator_user = 3285, - s_ugcmap_give_delegator_user = 3286, - s_ugcmap_release_delegator_user = 3287, - s_ugcmap_removeall_delegator_user = 3288, - s_err_ugcmap_blueprint_preview_cube_action_disabled = 3289, - s_home_today_reward = 3290, - s_home_bill_description = 3291, - s_home_interior_grade_gift_taken = 3292, - s_platform_common_error_unknown_error_code = 3293, - s_platform_common_error_unknown = 3294, - s_platform_common_error_invalid_param = 3295, - s_platform_common_error_not_initialize = 3296, - s_platform_common_error_unstable_install = 3297, - s_platform_common_error_unstable_install_path = 3298, - s_platform_common_error_unstable_install_data = 3299, - s_gamehelper_game_optimize_apply = 3300, - s_gamehelper_voice_chat_enter = 3301, - s_gamehelper_voice_chat_leave = 3302, - s_vip_coupon_extend_msg = 3303, - s_vip_coupon_new_msg = 3304, - s_word_tab_petinfo = 3305, - s_word_tab_petcompose = 3306, - s_word_tab_petevolution = 3307, - s_word_tab_petcollect = 3308, - s_word_tab_remakeoption = 3309, - s_char_input_itemname = 3310, - s_timeevent_boss_lifetimetext1 = 3311, - s_timeevent_boss_lifetimetext2 = 3312, - s_word_pet = 3313, - s_word_battle_pet = 3314, - s_live_broadcast_system_error = 3315, - s_ugcox_create_portal = 3316, - s_ugcox_host_commission_meso_get = 3317, - s_ugcox_entry_prize_meso_get = 3318, - s_ugcox_entry_fee_refund_meso_get = 3319, - s_ugcox_enter_fail_full = 3320, - s_couple_effect_error_openbox_unknown = 3321, - s_couple_effect_error_openbox_charname = 3322, - s_couple_effect_error_openbox_myself_char = 3323, - s_couple_effect_error_openbox_myself_account = 3324, - s_couple_effect_mail_sender = 3325, - s_couple_effect_mail_title_receiver = 3326, - s_couple_effect_mail_content_receiver = 3327, - s_couple_effect_mail_send_partner = 3328, - s_couple_emotion_request_recv = 3329, - s_couple_emotion_request_success = 3330, - s_couple_emotion_failed = 3331, - s_couple_emotion_response_accept = 3332, - s_couple_emotion_response_decline = 3333, - s_couple_emotion_target_user_wrong_position = 3334, - s_couple_emotion_recv_request_in_progressed = 3335, - s_couple_emotion_cannot_request_wrong_state = 3336, - s_couple_emotion_cannot_request_already_in_recv_state = 3337, - s_couple_emotion_cannot_request_long_distance = 3338, - s_couple_emotion_cannot_request_blocked_target = 3339, - s_couple_emotion_cannot_request_in_this_map = 3340, - s_couple_emotion_cannot_response_not_exist_request_user = 3341, - s_couple_emotion_failed_request_not_exist_skill = 3342, - s_couple_emotion_failed_accept_request_user_wrong_state = 3343, - s_couple_emotion_failed_request_already_recv = 3344, - s_couple_emotion_failed_request_already_in_action = 3345, - s_couple_emotion_failed_requset_auto_decline = 3346, - s_couple_emotion_failed_request_wrong_state_target_user = 3347, - s_couple_emotion_failed_accept_cannot_find_request_user = 3348, - s_couple_emotion_failed_teleport_limit_distance = 3349, - s_couple_emotion_failed_response_wrong_state_target_user = 3350, - s_galleryevent_cannot_open_card = 3351, - s_galleryevent_invalid_event = 3352, - s_microgame_rps_open_banner_failed_not_exist_ticket = 3353, - s_microgame_rps_open_banner_failed_action_key = 3354, - s_microgame_rps_open_banner_failed_wrong_state = 3355, - s_microgame_rps_request_failed_not_exist_ticket = 3356, - s_microgame_rps_request_failed_wrong_distance = 3357, - s_microgame_rps_request_failed_wrong_position = 3358, - s_microgame_rps_request_failed_wrong_state = 3359, - s_microgame_rps_request_cancel = 3360, - s_microgame_rps_peer_game_cancel = 3361, - s_microgame_rps_banner_failed_blocked_in_this_field = 3362, - s_microgame_rps_request_failed_blocked_in_this_field = 3363, - s_microgame_rps_request_failed_blocked_user = 3364, - s_microgame_rps_response_failed_blocked_user = 3365, - s_microgame_rps_request_failed_peer_wrong_state = 3366, - s_microgame_rps_accept_failed_peer_wrong_state = 3367, - s_microgame_rps_close = 3368, - s_microgame_rps_waiting = 3369, - s_microgame_rps_request_recv = 3370, - s_microgame_rps_response_decline = 3371, - s_microgame_rps_response_accept = 3372, - s_microgame_rps_failed = 3373, - s_microgame_rps_result_win = 3374, - s_microgame_rps_result_lose = 3375, - s_microgame_rps_result_draw = 3376, - s_survival_event_reduce_safezone_ready = 3377, - s_survival_event_reduce_safezone_start = 3378, - s_function_item_survival_scan_info = 3379, - s_function_item_survival_scan_announce = 3380, - s_treewatering_watering = 3381, - s_treewatering_emotion = 3382, - s_treewatering_watering_casting = 3383, - s_treewatering_emotion_casting = 3384, - s_treewatering_wateringreward = 3385, - s_treewatering_emotionreward = 3386, - s_treewatering_no_waterpot = 3387, - s_treewatering_nomore_watering_reward = 3388, - s_treewatering_nomore_emotion_reward = 3389, - s_notify_adventure_levelup = 3390, - s_cashshop_err_purchase_steam_restircted_country = 3391, - s_cashshop_err_purchase_product_info_expired = 3392, - s_closet_msg_success = 3393, - s_closet_msg_item_not_exist = 3394, - s_closet_msg_item_failed = 3395, - s_socket_error_common = 3396, - s_socket_error_10004 = 3397, - s_socket_error_10013 = 3398, - s_socket_error_10014 = 3399, - s_socket_error_10022 = 3400, - s_socket_error_10024 = 3401, - s_socket_error_10035 = 3402, - s_socket_error_10036 = 3403, - s_socket_error_10037 = 3404, - s_socket_error_10039 = 3405, - s_socket_error_10040 = 3406, - s_socket_error_10048 = 3407, - s_socket_error_10050 = 3408, - s_socket_error_10051 = 3409, - s_socket_error_10052 = 3410, - s_socket_error_10053 = 3411, - s_socket_error_10054 = 3412, - s_socket_error_10055 = 3413, - s_socket_error_10056 = 3414, - s_socket_error_10058 = 3415, - s_socket_error_10060 = 3416, - s_socket_error_10061 = 3417, - s_socket_error_10064 = 3418, - s_socket_error_10065 = 3419, - s_socket_error_10067 = 3420, - s_socket_error_10101 = 3421, - s_socket_error_11001 = 3422, - s_socket_error_11002 = 3423, - s_steam_purchase_restriction_adventure_level = 3424, - s_steam_purchase_restriction_character_level = 3425, - s_steam_purchase_restriction_account_create_days = 3426, - s_common_block_for_spamer_not_enough_max_level = 3427, - s_common_block_for_spamer_not_enough_adventure_level = 3428, - s_wedding_mail_title_receiver = 3429, - s_wedding_mail_contents_receiver = 3430, - s_wedding_mail_change_title_receiver = 3431, - s_wedding_mail_change_contents_receiver = 3432, - s_wedding_mail_cancel_title_receiver = 3433, - s_wedding_mail_cancel_contents_receiver = 3434, - s_payback_error_reward_time = 3435, - s_hideandseek_remain_user = 3436, - s_itemgacha_dialog_title = 3437, - s_itemgacha_dialog_gacha_type_skin = 3438, - s_itemgacha_dialog_gacha_type_look = 3439, - s_itemgacha_dialog_gacha_type_specup = 3440, - s_itemgacha_dialog_gacha_type_special = 3441, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Enum; + +public enum StringCode { + s_empty_string = 0, + s_char_input_name = 1, + s_char_level_job = 2, + s_char_delete_waiting = 3, + s_char_ask_delete = 4, + s_char_ask_delete_with_wait = 5, + s_char_ask_delete_final = 6, + s_char_ask_delete_confirm_checkbox = 7, + s_char_ask_revival_char = 8, + s_char_err_char_count = 9, + s_char_err_char_count_by_gameevent = 10, + s_char_err_name = 11, + s_char_err_system = 12, + s_char_err_input = 13, + s_char_err_ban_any = 14, + s_char_err_ban_all = 15, + s_char_err_already_taken = 16, + s_char_err_invalid_def_item = 17, + s_char_err_job_forbidden = 18, + s_char_err_creation_restriction = 19, + s_char_err_unknown = 20, + s_char_err_exist_ugc_map = 21, + s_char_err_already_destroy = 22, + s_char_err_destroy = 23, + s_char_err_delete_name = 24, + s_char_err_guild_master = 25, + s_char_err_guild = 26, + s_char_err_ugc_market = 27, + s_char_err_black_market_count = 28, + s_char_err_unread_mail = 29, + s_char_err_no_destroy_wait = 30, + s_char_info_birthday = 31, + s_char_info_level_job = 32, + s_char_info_guild = 33, + s_char_info_ugcmap = 34, + s_char_info_home_name = 35, + s_char_info_guild_name = 36, + s_char_info_home_commend = 37, + s_char_info_err_not_found = 38, + s_change_charname_disconnect = 39, + s_change_charname_confirm = 40, + s_change_charname_err_bad_words = 41, + s_change_charname_err_already_taken = 42, + s_change_charname_err_consume_item = 43, + s_change_charname_err_system = 44, + s_err_gender = 45, + s_err_target = 46, + s_err_job = 47, + s_err_stat = 48, + s_err_lack_coupon = 49, + s_err_lack_super_coupon = 50, + s_err_lack_money = 51, + s_err_lack_meso = 52, + s_err_lack_merat = 53, + s_err_lack_merat_blue = 54, + s_err_lack_merat_red = 55, + s_err_lack_payment_item = 56, + s_err_lack_honor_token = 57, + s_err_lack_karma_token = 58, + s_err_lack_lu_token = 59, + s_err_lack_habi_token = 60, + s_err_lack_reverse_coin = 61, + s_err_lack_mentor_token = 62, + s_err_lack_mentee_token = 63, + s_err_lack_star_point = 64, + s_err_lack_meso_market_token = 65, + s_err_unable = 66, + s_err_inventory = 67, + s_err_inventory_tab_full = 68, + s_err_dropitem_pickfail_ownership = 69, + s_err_lack_hp = 70, + s_err_lack_sp = 71, + s_err_lack_ep = 72, + s_err_lack_shopitem = 73, + s_err_invalid_item = 74, + s_err_input = 75, + s_err_cannot_find_user = 76, + s_err_input_whisper_target = 77, + s_err_cannot_find_club = 78, + s_err_input_club = 79, + s_err_cannot_move = 80, + s_err_cannot_fly = 81, + s_err_cannot_use_here = 82, + s_err_cannot_use_dead = 83, + s_err_cannot_use_cooltime = 84, + s_err_cannot_use_only_shadowworld = 85, + s_err_require_additional_effect = 86, + s_err_got_of_control_unable_potion = 87, + s_err_skill_use_disable = 88, + s_err_skill_use_disable_by_require_condition = 89, + s_err_lack_guild_trophy = 90, + s_err_lack_achieve = 91, + s_err_lack_championship_grade = 92, + s_err_lack_championship_join_count = 93, + s_err_cannot_use_by_function_cube_climb = 94, + s_err_cannot_use_by_function_cube_jump = 95, + s_err_cannot_use_by_function_cube_skill = 96, + s_err_cannot_use_by_function_cube_play_instrument = 97, + s_err_cannot_use_in_design_home = 98, + s_err_cannot_buy_limited_item_more = 99, + s_err_cannot_install_blueprint = 100, + s_err_cannot_install_maid_in_practice = 101, + s_err_cannot_install_nurturing_in_design_home = 102, + s_err_cannot_install_magic_portal = 103, + s_err_cannot_install_trigger_editor = 104, + s_err_cannot_install_trigger_controlobject = 105, + s_err_cannot_install_interior_message = 106, + s_err_cannot_install_event_cube = 107, + s_err_cannot_install_trophy_relative_cube = 108, + s_err_cannot_install_workbench_cube = 109, + s_err_cannot_install_fittingdoll = 110, + s_err_cannot_install_ugcdesign_maidin_other = 111, + s_err_cannot_destroy_petitem_summon = 112, + s_err_cannot_destroy_petitem_hasitem = 113, + s_err_check_survival_with = 114, + s_err_cannot_use_maview_in_testsvr = 115, + s_job_difficulty = 116, + s_err_job_not_enough_meso = 117, + s_err_job_not_enough_level = 118, + s_err_job_bad_job = 119, + s_err_job_no_penalty = 120, + s_err_job_no_home = 121, + s_err_job_not_complete_quest = 122, + s_err_job_privilege = 123, + s_err_job_dayofweek = 124, + s_err_job_debuff = 125, + s_err_job_guild = 126, + s_err_job_inventory_full = 127, + s_err_job_unknown = 128, + s_mirror_err_not_editable_cp = 129, + s_mirror_err_not_editable_cp_by_transp_badge = 130, + s_mirror_err_not_equip_cp = 131, + s_err_empty_tabname = 132, + s_err_duplicate_tabname = 133, + s_err_lack_itemcount = 134, + s_err_null_product = 135, + s_err_near_taxi_station = 136, + s_err_taxi_same_field = 137, + s_taxi_transfer = 138, + s_cash_taxi_transfer = 139, + s_event_free_taxi_transfer = 140, + s_err_cash_taxi_continent = 141, + s_err_cash_taxi_cannot_departure = 142, + s_err_cash_taxi_cannot_destination = 143, + s_err_cash_taxi_cannot_now = 144, + s_err_cash_taxi_cannot_craft_mode = 145, + s_err_cash_taxi_cannot_place = 146, + s_err_cash_call_medic_prohibit_map = 147, + s_err_cash_call_medic_cannot_now = 148, + s_err_cash_call_medic_revival_count = 149, + s_err_cash_call_medic_cannot_place = 150, + s_cash_call_medic_complete = 151, + s_err_cash_call_market_prohibit_map = 152, + s_err_cash_call_market_cannot_now = 153, + s_err_cash_call_market_cannot_place = 154, + s_err_cash_call_market_cannot_already = 155, + s_err_cash_call_bank_cannot_now = 156, + s_err_cash_call_bank_cannot_now_already = 157, + s_err_cash_call_bank_cannot_now_place = 158, + s_err_cash_call_cancel = 159, + s_err_timeevent_move_field = 160, + s_err_timeevent_battle = 161, + s_err_timeevent_samefield = 162, + s_err_timeevent_same_continent = 163, + s_err_itemlink_cannot_other_user = 164, + s_err_worldmap_search_nothing_field = 165, + s_err_worldmap_search_invisible_field = 166, + s_worldmap_timeevent_move = 167, + s_timeevent_notice_chat = 168, + s_revival_panelty = 169, + s_fail_login = 170, + s_fail_connect = 171, + s_fail_char_create = 172, + s_notice_shutdown = 173, + s_npc_normal = 174, + s_npc_leader = 175, + s_npc_named = 176, + s_npc_boss = 177, + s_npc_field_boss = 178, + s_npc_dungeon_boss = 179, + s_npc_friendly = 180, + s_item_require_weapon = 181, + s_item_edit_time = 182, + s_item_end_time = 183, + s_item_end_time_expired = 184, + s_item_end_time_maid = 185, + s_item_end_time_maid_expired = 186, + s_item_end_time_pcbang = 187, + s_item_end_time_pcbang_expired = 188, + s_item_end_time_tooltip = 189, + s_item_end_time_tooltip_expiration = 190, + s_item_end_time_tooltip_pcbang = 191, + s_item_end_time_tooltip_maid = 192, + s_item_end_time_when_item_get_tooltip = 193, + s_item_end_time_when_item_get_tooltip_pcbang = 194, + s_item_end_time_when_item_get_tooltip_maid = 195, + s_item_word_d = 196, + s_item_word_h = 197, + s_item_word_m = 198, + s_item_type_active = 199, + s_item_type_passive = 200, + s_item_type_twohand = 201, + s_item_type_lefthand = 202, + s_item_type_righthand = 203, + s_item_err_code = 204, + s_item_err_puton = 205, + s_item_err_putoff = 206, + s_item_err_destroy_equip = 207, + s_item_err_no_weapon = 208, + s_item_err_invalid_weapon = 209, + s_item_err_Invalid_slot = 210, + s_item_err_cash_slot = 211, + s_item_err_cash_putoff = 212, + s_item_err_twohand = 213, + s_item_err_drop = 214, + s_item_err_cannot_drop = 215, + s_item_err_cannot_drop_if_binding = 216, + s_item_err_binding_destroy = 217, + s_item_err_binding_destroy_at_fittingdoll = 218, + s_item_err_binditem_in_socket_store_out = 219, + s_item_err_binding_in_socket_destroy = 220, + s_item_err_binding_in_socket_destroy_at_fittingdoll = 221, + s_item_err_cubeitem_destroy = 222, + s_item_err_puton_low_level = 223, + s_item_err_puton_expired = 224, + s_item_err_puton_pcbang = 225, + s_item_err_use_low_level = 226, + s_item_invalid_function_item = 227, + s_item_invalid_function_not_use_item = 228, + s_item_invalid_do_not_have = 229, + s_word_item_change_name_samename = 230, + s_item_err_puton_job = 231, + s_item_err_disable_job = 232, + s_item_err_puton_invalid = 233, + s_item_err_invalid_count = 234, + s_item_err_cant_sell = 235, + s_item_err_transfer_item_bound = 236, + s_item_err_range = 237, + s_item_err_skill_level = 238, + s_item_err_puton_skill_samekinds = 239, + s_item_err_invaild_store_type = 240, + s_item_err_store_full = 241, + s_item_tooltip_ugc_removed = 242, + s_item_ugc_name_blocked = 243, + s_item_err_cant_sell_riding_item = 244, + s_item_err_cant_attach_to_mail_riding_item = 245, + s_item_err_cant_drop_riding_item = 246, + s_item_err_cant_trade_riding_item = 247, + s_item_err_donot_preview = 248, + s_item_err_donot_preview_expire = 249, + s_item_err_binditem = 250, + s_item_err_binditem_store_out = 251, + s_item_err_puton_invalid_binding = 252, + s_item_err_use_invalid_binding = 253, + s_item_err_moveDisableitem_store_out = 254, + s_item_opt_error = 255, + s_item_opt_sa_improve_acquire_exp = 256, + s_item_opt_sa_improve_acquire_exp_v = 257, + s_item_opt_sa_improve_acquire_exp_r = 258, + s_item_opt_sa_improve_acquire_meso = 259, + s_item_opt_sa_improve_acquire_meso_v = 260, + s_item_opt_sa_improve_acquire_meso_r = 261, + s_item_opt_sa_improve_speed_swim = 262, + s_item_opt_sa_improve_speed_swim_v = 263, + s_item_opt_sa_improve_speed_swim_r = 264, + s_item_opt_sa_improve_speed_dash = 265, + s_item_opt_sa_improve_speed_dash_v = 266, + s_item_opt_sa_improve_speed_dash_r = 267, + s_item_opt_sa_improve_acquire_potion = 268, + s_item_opt_sa_improve_acquire_potion_v = 269, + s_item_opt_sa_improve_acquire_potion_r = 270, + s_item_opt_sa_improve_acquire_equipment = 271, + s_item_opt_sa_improve_acquire_equipment_v = 272, + s_item_opt_sa_improve_acquire_equipment_r = 273, + s_item_opt_sa_improve_damage_critical = 274, + s_item_opt_sa_improve_damage_critical_v = 275, + s_item_opt_sa_improve_damage_critical_r = 276, + s_item_opt_sa_improve_damage_normalNpc = 277, + s_item_opt_sa_improve_damage_normalNpc_v = 278, + s_item_opt_sa_improve_damage_normalNpc_r = 279, + s_item_opt_sa_improve_damage_leaderNpc = 280, + s_item_opt_sa_improve_damage_leaderNpc_v = 281, + s_item_opt_sa_improve_damage_leaderNpc_r = 282, + s_item_opt_sa_improve_damage_namedNpc = 283, + s_item_opt_sa_improve_damage_namedNpc_v = 284, + s_item_opt_sa_improve_damage_namedNpc_r = 285, + s_item_opt_sa_improve_damage_bossNpc = 286, + s_item_opt_sa_improve_damage_bossNpc_v = 287, + s_item_opt_sa_improve_damage_bossNpc_r = 288, + s_item_opt_sa_improve_recovery_regen_doheal = 289, + s_item_opt_sa_improve_recovery_regen_doheal_v = 290, + s_item_opt_sa_improve_recovery_regen_doheal_r = 291, + s_item_opt_sa_improve_recovery_regen_receiveheal = 292, + s_item_opt_sa_improve_recovery_regen_receiveheal_v = 293, + s_item_opt_sa_improve_recovery_regen_receiveheal_r = 294, + s_item_opt_sa_reduce_time_stun = 295, + s_item_opt_sa_reduce_time_stun_v = 296, + s_item_opt_sa_reduce_time_stun_r = 297, + s_item_opt_sa_improve_recovery_hp_dokill = 298, + s_item_opt_sa_improve_recovery_hp_dokill_v = 299, + s_item_opt_sa_improve_recovery_hp_dokill_r = 300, + s_item_opt_sa_improve_recovery_sp_dokill = 301, + s_item_opt_sa_improve_recovery_sp_dokill_v = 302, + s_item_opt_sa_improve_recovery_sp_dokill_r = 303, + s_item_opt_sa_improve_recovery_ep_dokill = 304, + s_item_opt_sa_improve_recovery_ep_dokill_v = 305, + s_item_opt_sa_improve_recovery_ep_dokill_r = 306, + s_item_opt_sa_reduce_time_cooldown = 307, + s_item_opt_sa_reduce_time_cooldown_v = 308, + s_item_opt_sa_reduce_time_cooldown_r = 309, + s_item_opt_sa_stat_attackpoint = 310, + s_item_opt_sa_stat_attackpoint_v = 311, + s_item_opt_sa_stat_attackpoint_r = 312, + s_item_opt_sa_improve_elements_ice = 313, + s_item_opt_sa_improve_elements_ice_v = 314, + s_item_opt_sa_improve_elements_ice_r = 315, + s_item_opt_sa_improve_elements_fire = 316, + s_item_opt_sa_improve_elements_fire_v = 317, + s_item_opt_sa_improve_elements_fire_r = 318, + s_item_opt_sa_improve_elements_dark = 319, + s_item_opt_sa_improve_elements_dark_v = 320, + s_item_opt_sa_improve_elements_dark_r = 321, + s_item_opt_sa_improve_elements_light = 322, + s_item_opt_sa_improve_elements_light_v = 323, + s_item_opt_sa_improve_elements_light_r = 324, + s_item_opt_sa_improve_elements_poison = 325, + s_item_opt_sa_improve_elements_poison_v = 326, + s_item_opt_sa_improve_elements_poison_r = 327, + s_item_opt_sa_improve_elements_thunder = 328, + s_item_opt_sa_improve_elements_thunder_v = 329, + s_item_opt_sa_improve_elements_thunder_r = 330, + s_item_opt_sa_improve_damage_nearrange = 331, + s_item_opt_sa_improve_damage_nearrange_r = 332, + s_item_opt_sa_improve_damage_nearrange_v = 333, + s_item_opt_sa_improve_damage_longrange = 334, + s_item_opt_sa_improve_damage_longrange_r = 335, + s_item_opt_sa_improve_damage_longrange_v = 336, + s_item_opt_sa_improve_piercing_par = 337, + s_item_opt_sa_improve_piercing_par_r = 338, + s_item_opt_sa_improve_piercing_par_v = 339, + s_item_opt_sa_improve_piercing_mar = 340, + s_item_opt_sa_improve_piercing_mar_r = 341, + s_item_opt_sa_improve_piercing_mar_v = 342, + s_item_opt_sa_reduce_elements_ice = 343, + s_item_opt_sa_reduce_elements_ice_v = 344, + s_item_opt_sa_reduce_elements_ice_r = 345, + s_item_opt_sa_reduce_elements_fire = 346, + s_item_opt_sa_reduce_elements_fire_v = 347, + s_item_opt_sa_reduce_elements_fire_r = 348, + s_item_opt_sa_reduce_elements_dark = 349, + s_item_opt_sa_reduce_elements_dark_v = 350, + s_item_opt_sa_reduce_elements_dark_r = 351, + s_item_opt_sa_reduce_elements_light = 352, + s_item_opt_sa_reduce_elements_light_v = 353, + s_item_opt_sa_reduce_elements_light_r = 354, + s_item_opt_sa_reduce_elements_poison = 355, + s_item_opt_sa_reduce_elements_poison_v = 356, + s_item_opt_sa_reduce_elements_poison_r = 357, + s_item_opt_sa_reduce_elements_thunder = 358, + s_item_opt_sa_reduce_elements_thunder_v = 359, + s_item_opt_sa_reduce_elements_thunder_r = 360, + s_item_opt_sa_reduce_time_condition = 361, + s_item_opt_sa_reduce_time_condition_v = 362, + s_item_opt_sa_reduce_time_condition_r = 363, + s_item_opt_sa_reduce_distance_knockBack = 364, + s_item_opt_sa_reduce_distance_knockBack_v = 365, + s_item_opt_sa_reduce_distance_knockBack_r = 366, + s_item_opt_sa_reduce_damage_nearrange = 367, + s_item_opt_sa_reduce_damage_nearrange_r = 368, + s_item_opt_sa_reduce_damage_nearrange_v = 369, + s_item_opt_sa_reduce_damage_longrange = 370, + s_item_opt_sa_reduce_damage_longrange_r = 371, + s_item_opt_sa_reduce_damage_longrange_v = 372, + s_item_opt_sa_improve_damage_final = 373, + s_item_opt_sa_improve_damage_final_r = 374, + s_item_opt_sa_improve_damage_final_v = 375, + s_item_opt_sa_probability_stun_nearrange = 376, + s_item_opt_sa_probability_stun_nearrange_r = 377, + s_item_opt_sa_probability_stun_nearrange_v = 378, + s_item_opt_sa_probability_stun_longrange = 379, + s_item_opt_sa_probability_stun_longrange_r = 380, + s_item_opt_sa_probability_stun_longrange_v = 381, + s_item_opt_sa_probability_knockback_nearrange = 382, + s_item_opt_sa_probability_knockback_nearrange_r = 383, + s_item_opt_sa_probability_knockback_nearrange_v = 384, + s_item_opt_sa_probability_knockback_longrange = 385, + s_item_opt_sa_probability_knockback_longrange_r = 386, + s_item_opt_sa_probability_knockback_longrange_v = 387, + s_item_opt_sa_probability_cannotmove_nearrange = 388, + s_item_opt_sa_probability_cannotmove_nearrange_r = 389, + s_item_opt_sa_probability_cannotmove_nearrange_v = 390, + s_item_opt_sa_probability_cannotmove_longrange = 391, + s_item_opt_sa_probability_cannotmove_longrange_r = 392, + s_item_opt_sa_probability_cannotmove_longrange_v = 393, + s_item_opt_sa_probability_splashdamage_nearrange = 394, + s_item_opt_sa_probability_splashdamage_nearrange_r = 395, + s_item_opt_sa_probability_splashdamage_nearrange_v = 396, + s_item_opt_sa_probability_splashdamage_longrange = 397, + s_item_opt_sa_probability_splashdamage_longrange_r = 398, + s_item_opt_sa_probability_splashdamage_longrange_v = 399, + s_item_opt_sa_improve_npckill_dropitem_incrate = 400, + s_item_opt_sa_improve_npckill_dropitem_incrate_r = 401, + s_item_opt_sa_improve_npckill_dropitem_incrate_v = 402, + s_item_opt_sa_improve_acquire_questreward_exp = 403, + s_item_opt_sa_improve_acquire_questreward_exp_v = 404, + s_item_opt_sa_improve_acquire_questreward_exp_r = 405, + s_item_opt_sa_improve_acquire_questreward_meso = 406, + s_item_opt_sa_improve_acquire_questreward_meso_v = 407, + s_item_opt_sa_improve_acquire_questreward_meso_r = 408, + s_item_opt_sa_improve_acquire_fishing_exp = 409, + s_item_opt_sa_improve_acquire_fishing_exp_v = 410, + s_item_opt_sa_improve_acquire_fishing_exp_r = 411, + s_item_opt_sa_improve_acquire_arcade_exp = 412, + s_item_opt_sa_improve_acquire_arcade_exp_v = 413, + s_item_opt_sa_improve_acquire_arcade_exp_r = 414, + s_item_opt_sa_improve_acquire_playinstrument_exp = 415, + s_item_opt_sa_improve_acquire_playinstrument_exp_v = 416, + s_item_opt_sa_improve_acquire_playinstrument_exp_r = 417, + s_item_opt_sa_invoke_effect = 418, + s_item_opt_sa_invoke_skill_decrease_cooldowntime_v = 419, + s_item_opt_sa_invoke_skill_increase_cooldowntime_v = 420, + s_item_opt_sa_invoke_skill_damage_v = 421, + s_item_opt_sa_invoke_effect_duration_v = 422, + s_item_opt_sa_invoke_effect_conditioneffect_probability_v = 423, + s_item_opt_sa_invoke_effect_dotdamage_v = 424, + s_item_opt_sa_invoke_effect_hprecovery_v = 425, + s_item_opt_sa_invoke_effect_hp_v = 426, + s_item_opt_sa_invoke_effect_hp_rgp_v = 427, + s_item_opt_sa_invoke_effect_hp_inv_v = 428, + s_item_opt_sa_invoke_effect_sp_v = 429, + s_item_opt_sa_invoke_effect_sp_rgp_v = 430, + s_item_opt_sa_invoke_effect_sp_inv_v = 431, + s_item_opt_sa_invoke_effect_ep_v = 432, + s_item_opt_sa_invoke_effect_ep_rgp_v = 433, + s_item_opt_sa_invoke_effect_ep_inv_v = 434, + s_item_opt_sa_invoke_effect_str_v = 435, + s_item_opt_sa_invoke_effect_atp_v = 436, + s_item_opt_sa_invoke_effect_pap_v = 437, + s_item_opt_sa_invoke_effect_dex_v = 438, + s_item_opt_sa_invoke_effect_evp_v = 439, + s_item_opt_sa_invoke_effect_map_v = 440, + s_item_opt_sa_invoke_effect_int_v = 441, + s_item_opt_sa_invoke_effect_cap_v = 442, + s_item_opt_sa_invoke_effect_par_v = 443, + s_item_opt_sa_invoke_effect_luk_v = 444, + s_item_opt_sa_invoke_effect_cad_v = 445, + s_item_opt_sa_invoke_effect_mar_v = 446, + s_item_opt_sa_invoke_effect_car_v = 447, + s_item_opt_sa_invoke_effect_pen_v = 448, + s_item_opt_sa_invoke_effect_asp_v = 449, + s_item_opt_sa_invoke_effect_ndd_v = 450, + s_item_opt_sa_invoke_effect_msp_v = 451, + s_item_opt_sa_invoke_effect_abp_v = 452, + s_item_opt_sa_invoke_effect_rmsp_v = 453, + s_item_opt_sa_invoke_effect_jmp_v = 454, + s_item_opt_sa_invoke_effect_wap_min_v = 455, + s_item_opt_sa_invoke_effect_wap_max_v = 456, + s_item_opt_sa_invoke_effect_offensive_physical_damage_v = 457, + s_item_opt_sa_invoke_effect_defensive_physical_damage_v = 458, + s_item_opt_sa_invoke_effect_offensive_magical_damage_v = 459, + s_item_opt_sa_invoke_effect_defensive_magical_damage_v = 460, + s_item_opt_sa_invoke_effect_defensive_neardistance_damage_v = 461, + s_item_opt_sa_invoke_effect_defensive_longdistance_damage_v = 462, + s_item_opt_sa_invoke_effect_improve_elements_fire_v = 463, + s_item_opt_sa_invoke_effect_improve_elements_ice_v = 464, + s_item_opt_sa_invoke_effect_improve_elements_thunder_v = 465, + s_item_opt_sa_invoke_effect_improve_elements_poison_v = 466, + s_item_opt_sa_invoke_effect_improve_elements_holy_v = 467, + s_item_opt_sa_invoke_effect_improve_elements_dark_v = 468, + s_item_opt_sa_invoke_effect_reduce_elements_fire_v = 469, + s_item_opt_sa_invoke_effect_reduce_elements_ice_v = 470, + s_item_opt_sa_invoke_effect_reduce_elements_thunder_v = 471, + s_item_opt_sa_invoke_effect_reduce_elements_poison_v = 472, + s_item_opt_sa_invoke_effect_reduce_elements_holy_v = 473, + s_item_opt_sa_invoke_effect_reduce_elements_dark_v = 474, + s_item_opt_sa_invoke_skill_decrease_cooldowntime_r = 475, + s_item_opt_sa_invoke_skill_increase_cooldowntime_r = 476, + s_item_opt_sa_invoke_skill_damage_r = 477, + s_item_opt_sa_invoke_effect_duration_r = 478, + s_item_opt_sa_invoke_effect_conditioneffect_probability_r = 479, + s_item_opt_sa_invoke_effect_dotdamage_r = 480, + s_item_opt_sa_invoke_effect_hprecovery_r = 481, + s_item_opt_sa_invoke_effect_hp_r = 482, + s_item_opt_sa_invoke_effect_hp_rgp_r = 483, + s_item_opt_sa_invoke_effect_hp_inv_r = 484, + s_item_opt_sa_invoke_effect_sp_r = 485, + s_item_opt_sa_invoke_effect_sp_rgp_r = 486, + s_item_opt_sa_invoke_effect_sp_inv_r = 487, + s_item_opt_sa_invoke_effect_ep_r = 488, + s_item_opt_sa_invoke_effect_ep_rgp_r = 489, + s_item_opt_sa_invoke_effect_ep_inv_r = 490, + s_item_opt_sa_invoke_effect_str_r = 491, + s_item_opt_sa_invoke_effect_atp_r = 492, + s_item_opt_sa_invoke_effect_pap_r = 493, + s_item_opt_sa_invoke_effect_dex_r = 494, + s_item_opt_sa_invoke_effect_evp_r = 495, + s_item_opt_sa_invoke_effect_map_r = 496, + s_item_opt_sa_invoke_effect_int_r = 497, + s_item_opt_sa_invoke_effect_cap_r = 498, + s_item_opt_sa_invoke_effect_par_r = 499, + s_item_opt_sa_invoke_effect_luk_r = 500, + s_item_opt_sa_invoke_effect_cad_r = 501, + s_item_opt_sa_invoke_effect_mar_r = 502, + s_item_opt_sa_invoke_effect_car_r = 503, + s_item_opt_sa_invoke_effect_pen_r = 504, + s_item_opt_sa_invoke_effect_asp_r = 505, + s_item_opt_sa_invoke_effect_ndd_r = 506, + s_item_opt_sa_invoke_effect_msp_r = 507, + s_item_opt_sa_invoke_effect_abp_r = 508, + s_item_opt_sa_invoke_effect_rmsp_r = 509, + s_item_opt_sa_invoke_effect_jmp_r = 510, + s_item_opt_sa_invoke_effect_wap_min_r = 511, + s_item_opt_sa_invoke_effect_wap_max_r = 512, + s_item_opt_sa_invoke_effect_offensive_physical_damage_r = 513, + s_item_opt_sa_invoke_effect_defensive_physical_damage_r = 514, + s_item_opt_sa_invoke_effect_offensive_magical_damage_r = 515, + s_item_opt_sa_invoke_effect_defensive_magical_damage_r = 516, + s_item_opt_sa_invoke_effect_defensive_neardistance_damage_r = 517, + s_item_opt_sa_invoke_effect_defensive_longdistance_damage_r = 518, + s_item_opt_sa_invoke_effect_improve_elements_fire_r = 519, + s_item_opt_sa_invoke_effect_improve_elements_ice_r = 520, + s_item_opt_sa_invoke_effect_improve_elements_thunder_r = 521, + s_item_opt_sa_invoke_effect_improve_elements_poison_r = 522, + s_item_opt_sa_invoke_effect_improve_elements_holy_r = 523, + s_item_opt_sa_invoke_effect_improve_elements_dark_r = 524, + s_item_opt_sa_invoke_effect_reduce_elements_fire_r = 525, + s_item_opt_sa_invoke_effect_reduce_elements_ice_r = 526, + s_item_opt_sa_invoke_effect_reduce_elements_thunder_r = 527, + s_item_opt_sa_invoke_effect_reduce_elements_poison_r = 528, + s_item_opt_sa_invoke_effect_reduce_elements_holy_r = 529, + s_item_opt_sa_invoke_effect_reduce_elements_dark_r = 530, + s_item_opt_has_additional_effect = 531, + s_item_opt_sa_improve_damage_pvp = 532, + s_item_opt_sa_improve_damage_pvp_r = 533, + s_item_opt_sa_improve_damage_pvp_v = 534, + s_item_opt_sa_reduce_damage_pvp = 535, + s_item_opt_sa_reduce_damage_pvp_v = 536, + s_item_opt_sa_reduce_damage_pvp_r = 537, + s_login_err_connect = 538, + s_login_err_disconnected = 539, + s_login_err_id = 540, + s_login_err_pwd = 541, + s_login_err_access = 542, + s_login_err_full_server = 543, + s_login_err_version = 544, + s_login_err_full_ch = 545, + s_login_err_db = 546, + s_login_err_unknown = 547, + s_login_err_check_passport = 548, + s_login_err_restrict_title = 549, + s_login_err_restrict = 550, + s_login_err_alphatester = 551, + s_login_err_block_new_account = 552, + s_login_err_guest_nopcbang = 553, + s_login_err_session_error = 554, + s_login_err_external_block_nsn = 555, + s_login_err_external_block_ip = 556, + s_login_err_admin_ip = 557, + s_login_err_main_atl = 558, + s_login_err_auto_external_block = 559, + s_login_err_tencent_signature = 560, + s_ah_err_close = 561, + s_ngs_err_login_error = 562, + s_relocate_world_err = 563, + s_mail_send = 564, + s_mail_return = 565, + s_mail_delete = 566, + s_notify_mail_recieve = 567, + s_mail_err_cannot_attach_item = 568, + s_mail_read_list = 569, + s_mail_delete_list = 570, + s_mail_receive_list = 571, + s_mail_delete_list_received_mail = 572, + s_mail_delete_list_attached_mail = 573, + s_mail_delete_list_attached_mail_checkbox = 574, + s_mail_send_date = 575, + s_mail_attach_item = 576, + s_mail_delete_confirm = 577, + s_mail_delete_exist_ad = 578, + s_mail_delete_exist_attach = 579, + s_mail_send_really = 580, + s_mail_read_fail = 581, + s_mail_error = 582, + s_mail_error_already_attach_meso = 583, + s_mail_error_range = 584, + s_mail_error_not_select = 585, + s_mail_error_username = 586, + s_mail_error_cannot_attach_item = 587, + s_mail_error_attachcount = 588, + s_mail_error_already_receive = 589, + s_mail_error_recipient_equal_sender = 590, + s_mail_error_createmail = 591, + s_mail_error_sendmail = 592, + s_mail_error_empty_cleaning_user = 593, + s_mail_error_empty_cleaning_system = 594, + s_mail_error_empty_title = 595, + s_mail_error_empty_content = 596, + s_mail_error_alreadyread = 597, + s_mail_error_receiveitem_to_inven = 598, + s_mail_error_receive_expired = 599, + s_mail_error_ad_expired = 600, + s_mail_error_block_from_me = 601, + s_mail_error_block_from_other = 602, + s_mail_error_admin_character = 603, + s_mail_error_from_admin_to_user = 604, + s_mail_error_bancheck = 605, + s_mail_error_admin_block = 606, + s_mail_cleaning_title = 607, + s_mail_cleaning_usermail = 608, + s_mail_cleaning_systemmail = 609, + s_mail_error_limit_input = 610, + s_mail_period_item_include = 611, + s_mail_period_item_include_chat = 612, + s_inventory_tab_equip = 613, + s_inventory_tab_life = 614, + s_inventory_tab_etc = 615, + s_inventory_tab_summon = 616, + s_inventory_tab_petequip = 617, + s_inventory_tab_skin = 618, + s_inventory_tab_gem = 619, + s_inventory_tab_quest = 620, + s_inventory_tab_material = 621, + s_inventory_tab_mastery = 622, + s_inventory_tab_pet = 623, + s_inventory_tab_activeskill = 624, + s_inventory_tab_coin = 625, + s_inventory_tab_badge = 626, + s_inventory_tab_survival = 627, + s_inventory_ask_expand = 628, + s_inventory_err_expand_max = 629, + s_move_err_no_server = 630, + s_move_err_over_user = 631, + s_move_err_dungeon_not_exist = 632, + s_move_err_member_limit = 634, + s_move_err_time_out = 635, + s_move_err_field_limit = 636, + s_beauty_skin_name = 637, + s_beauty_notice_hide_cap_by_transp_badge = 638, + s_beauty_msg_back_game_no_save = 639, + s_beauty_msg_error_code = 640, + s_beauty_tooltip_price = 641, + s_beauty_tooltip_style = 642, + s_beauty_tooltip_style_save_date = 643, + s_beauty_msg_random = 644, + s_beauty_msg_coloring_confirm = 645, + s_beauty_goto_map_invalid_dead = 646, + s_beauty_goto_map_invalid_battle = 647, + s_beauty_goto_map_invalid_samefield = 648, + s_msg_die_beginner = 649, + s_msg_die_warring_different_map_revive = 650, + s_msg_revival_btn = 651, + s_msg_revival_mapleworld_btn = 652, + s_msg_revival_merat_btn = 653, + s_msg_revival_merat_cannot_debuff = 654, + s_msg_revival_merat_cannot_here = 655, + s_msg_revival_merat_msg_box = 656, + s_msg_revival_merat_not_dead = 657, + s_msg_revival_meso_btn = 658, + s_msg_revival_meso_event_btn = 659, + s_msg_revival_dungeon_warning = 660, + s_msg_game_over = 661, + s_msg_disconnect_kickuser = 662, + s_msg_item_sell_request = 663, + s_msg_item_sell_count_request = 664, + s_msg_item_sell_confirm = 665, + s_msg_item_buy_confirm = 666, + s_msg_revial_meso = 667, + s_msg_not_visit = 668, + s_msg_not_visitable = 669, + s_msg_admin_input_accountsn = 670, + s_msg_tombstone = 671, + s_msg_meso_drop = 672, + s_msg_item_drop = 673, + s_msg_item_buy = 674, + s_msg_item_sell = 675, + s_msg_item_repurchase = 676, + s_msg_item_upgrade_level = 677, + s_msg_item_upgrade_failed = 678, + s_msg_item_upgrade_disabled = 679, + s_msg_item_upgrade_complete = 680, + s_msg_item_default = 681, + s_msg_item_open_item_box = 682, + s_msg_item_open_item_dont_ask_check = 683, + s_msg_item_open_item_dont_ask_check_buycube = 684, + s_msg_item_use_name = 685, + s_msg_item_use_only_shadowcontinent = 686, + s_msg_item_remove_expired_item_in_inventory = 687, + s_msg_expand_inven_complete = 688, + s_msg_expand_inven_already_maximum = 689, + s_msg_expand_character_slot_complete = 690, + s_msg_expand_character_slot_already_maximum = 691, + s_msg_expand_inven_forced = 692, + s_msg_currency_overflow = 693, + s_store_ask_expand = 694, + s_store_ask_deposit = 695, + s_store_ask_withdraw = 696, + s_store_ask_in = 697, + s_store_ask_out = 698, + s_store_ask_close = 699, + s_store_ask_homebank_close = 700, + s_store_err_code = 701, + s_store_err_expand_max = 702, + s_store_err_deposit_disable_type = 703, + s_store_err_deposit_invalid_money = 704, + s_store_err_deposit_max_money = 705, + s_store_err_withdraw_invalid_money = 706, + s_store_err_withdraw_not_enough_balance = 707, + s_store_err_deposit_not_enough_balance = 708, + s_msg_party_invite = 709, + s_msg_popup_time = 710, + s_msg_take_item = 711, + s_msg_take_item_count = 712, + s_msg_take_item_ugc_cube = 713, + s_msg_take_item_count_ugc_cube = 714, + s_msg_take_exp = 715, + s_msg_take_assist_bonus_exp = 716, + s_msg_take_assist_bonus_exp_system = 717, + s_msg_take_map_exp = 718, + s_msg_take_taxi_exp = 719, + s_msg_take_telescope_exp = 720, + s_msg_take_meso = 721, + s_msg_consume_meso = 722, + s_msg_take_pcbang = 723, + s_msg_take_merat = 724, + s_msg_take_merat_blue = 725, + s_msg_take_merat_red = 726, + s_msg_take_honor_token = 727, + s_msg_take_karma_token = 728, + s_msg_take_lu_token = 729, + s_msg_take_habi_token = 730, + s_msg_take_star_point = 731, + s_msg_take_meso_market_token = 732, + s_msg_cant_sell_trade_drop = 733, + s_msg_cant_sell_trade = 734, + s_msg_cant_sell_drop = 735, + s_msg_cant_sell = 736, + s_msg_cant_trade_drop = 737, + s_msg_cant_trade = 738, + s_msg_cant_drop = 739, + s_msg_chatting_welcome = 740, + s_msg_chatting_changechannel = 741, + s_msg_chatting_consume_merat = 742, + s_msg_chatting_consume_merat_blue = 743, + s_msg_chatting_consume_merat_red = 744, + s_msg_chatting_consume_merat_message = 745, + s_msg_skillbook_learn_messagebox = 746, + s_msg_skillbook_learn_notexistslot = 747, + s_msg_skillbook_error_job = 748, + s_msg_skillbook_error_level = 749, + s_msg_skillbook_error_master = 750, + s_msg_skillbook_error_expired = 751, + s_msg_skillbook_reset_all = 752, + s_mas_move_connect = 753, + s_msg_ugc_cannot_edit = 754, + s_msg_ugc_cannot_find = 755, + s_msg_ugc_not_select = 756, + s_msg_ugc_not_file_preview = 757, + s_msg_ugc_not_file_noexist = 758, + s_msg_ugc_too_many_files = 759, + s_msg_ugc_not_select_reserve = 760, + s_msg_ugc_error_item_name = 761, + s_msg_ugc_admin_banner = 762, + s_msg_ugc_shutdown = 763, + s_msg_ugc_cant_register_shortcut = 764, + s_msg_ugc_fail_file_exist = 765, + s_msg_ugc_fail_file_size = 766, + s_msg_ugc_item_upload_confirm = 767, + s_msg_ugc_item_edit_thumbnail_tooltip = 768, + s_msg_ugc_item_edit_text_title = 769, + s_msg_ugc_item_edit_complete = 770, + s_msg_ugc_expired_item = 771, + s_msg_cant_movechannel = 772, + s_change_gender_confirm = 773, + s_change_gender_err_equip_items = 774, + s_change_gender_result_success = 775, + s_change_gender_result_failed = 776, + s_notify_currency_overflow = 777, + s_content_shutdown_notice = 778, + s_banner_billboard_shutdown = 779, + s_msg_warehouse_container_out = 780, + s_msg_warehouse_container_loading = 781, + s_msg_warehouse_container_count = 782, + s_msg_warehouse_container_count_empty = 783, + s_msg_skill_upgrade = 784, + s_msg_crystal_upgrade_empty = 785, + s_msg_crystal_upgrade_disabled = 786, + s_msg_crystal_upgrade_complete = 787, + s_msg_memo_today = 788, + s_msg_preparing = 789, + s_msg_transfer_bind_when_transform = 790, + s_shop_reset_time_ddhhmmss = 791, + s_shop_reset_time_hhmmss = 792, + s_shop_reset_time_mmss = 793, + s_shop_reset_time_ss = 794, + s_shop_require_achieve = 795, + s_shop_require_guild_trophy = 796, + s_shop_require_championship_grade = 797, + s_shop_err_item_restricted = 798, + s_option_fullscreen = 799, + s_option_windowmode = 800, + s_option_windowmode_full = 801, + s_option_resolution = 802, + s_option_resolution_wide = 803, + s_option_perf_very_low = 804, + s_option_perf_low = 805, + s_option_perf_normal = 806, + s_option_perf_high = 807, + s_option_perf_very_high = 808, + s_option_category_action = 809, + s_option_category_menu = 810, + s_option_category_emotion = 811, + s_option_category_etc = 812, + s_option_category_debug = 813, + s_party_join_me = 814, + s_party_join_someone = 815, + s_party_leave_me = 816, + s_party_leave_someone = 817, + s_party_expel_me = 818, + s_party_expel_someone = 819, + s_party_break = 820, + s_party_member_login = 821, + s_party_member_logout = 822, + s_party_chief_me = 823, + s_party_chief_someone = 824, + s_party_member_dead_tomb = 825, + s_party_member_dead_dark_tomb = 826, + s_party_err_code = 827, + s_party_err_not_exist = 828, + s_party_err_already = 829, + s_party_err_alreadyInvite = 830, + s_party_err_not_chief = 831, + s_party_err_full = 832, + s_party_err_myself = 833, + s_party_err_cannot_invite = 834, + s_party_err_deny = 835, + s_party_err_deny_by_auto = 836, + s_party_err_deny_by_system = 837, + s_party_err_deny_by_timeout = 838, + s_party_err_leave_no_party = 839, + s_party_err_no_party = 840, + s_party_err_fail_enterable_result = 841, + s_party_err_lack_level = 842, + s_party_err_lack_gear_score = 843, + s_party_err_full_limit_player = 844, + s_party_err_invalid_recruit = 845, + s_party_err_invalid_party = 846, + s_party_err_invalid_chief = 847, + s_party_err_wrong_party = 848, + s_party_err_wrong_recruit = 849, + s_party_check_change_chief = 850, + s_party_check_expel_member = 851, + s_party_expel_boss_room = 852, + s_party_someone_get_high_item = 853, + s_party_auto_join_confirm = 854, + s_club_create = 855, + s_club_create_ask = 856, + s_club_break = 857, + s_club_invite_someone = 858, + s_club_invite_me = 859, + s_club_invite_cant_me = 860, + s_club_invite_invalid_charname = 861, + s_club_join = 862, + s_club_join_reject = 863, + s_club_join_reject_invite = 864, + s_club_join_reject_timeout = 865, + s_club_join_reject_logout = 866, + s_club_leave = 867, + s_club_notify_leave = 868, + s_club_notify_accept_invite = 869, + s_club_notify_login_member = 870, + s_club_notify_logout_member = 871, + s_club_notify_change_master = 872, + s_club_notify_change_master_me = 873, + s_club_notify_change_buff = 874, + s_club_notify_change_name = 875, + s_club_ui_offline_time_day = 876, + s_club_ui_offline_time_hour = 877, + s_club_ui_offline_time_min = 878, + s_club_ui_offline_time_sec = 879, + s_club_ui_offline_unknown = 880, + s_club_ui_offline = 881, + s_club_ui_member_location = 882, + s_club_ui_member_detail = 883, + s_club_ui_invite_member = 884, + s_club_ui_create = 885, + s_club_ui_leave = 886, + s_club_ui_change_master = 887, + s_club_ui_create_time = 888, + s_club_ui_select_target_member = 889, + s_club_ui_current_member = 890, + s_club_err_unknown = 891, + s_club_err_create = 892, + s_club_err_create_reject = 893, + s_club_err_null_club = 894, + s_club_err_create_no_party = 895, + s_club_err_already_exist = 896, + s_club_err_wait_inviting = 897, + s_club_err_blocked = 898, + s_club_err_has_guild = 899, + s_club_err_invalid_guild = 900, + s_club_err_null_user = 901, + s_club_err_name_exist = 902, + s_club_err_name_value = 903, + s_club_err_null_member = 904, + s_club_err_exist_member = 905, + s_club_err_full_member = 906, + s_club_err_not_join_member = 907, + s_club_err_cannot_leave_master = 908, + s_club_err_expel_target_master = 909, + s_club_err_no_master = 910, + s_club_err_fail_addmember = 911, + s_club_err_null_invite_member = 912, + s_club_err_none = 913, + s_club_err_block = 914, + s_club_err_fail_this_field = 915, + s_club_err_full_club = 916, + s_club_err_full_club_member = 917, + s_club_err_notparty_alllogin = 918, + s_club_err_remain_time = 919, + s_club_err_same_club_name = 920, + s_club_err_clubname_has_blank = 921, + s_guild_err_no_guild = 923, + s_guild_create = 924, + s_guild_break = 925, + s_guild_invite_someone = 926, + s_guild_invite_me = 927, + s_guild_invite_cant_me = 928, + s_guild_invite_invalid_charname = 929, + s_guild_join = 930, + s_guild_join_reject = 931, + s_guild_join_accecpt_invite = 932, + s_guild_join_reject_invite = 933, + s_guild_join_reject_logout = 934, + s_guild_join_reject_timeout = 935, + s_guild_leave = 936, + s_guild_leave_master_cant = 937, + s_guild_change_notify = 938, + s_guild_change_grade_sucess = 939, + s_guild_grade_default_master = 940, + s_guild_grade_default_group1 = 941, + s_guild_grade_default_group2 = 942, + s_guild_grade_default_group3 = 943, + s_guild_grade_default_group4 = 944, + s_guild_grade_default_group5 = 945, + s_guild_extend_capacity_success = 946, + s_guild_extend_capacity_err_cannot = 947, + s_guild_extend_capacity_err_current = 948, + s_guild_search_same_propensity = 949, + s_guild_search_max_join_request = 950, + s_guild_search_last_request = 951, + s_guild_search_null_join_guild_request = 952, + s_guild_notify_leave = 953, + s_guild_notify_change_grade = 954, + s_guild_notify_accept_invite = 955, + s_guild_notify_expel_member = 956, + s_guild_notify_expeled = 957, + s_guild_notify_expeled_from = 958, + s_guild_notify_login_member = 959, + s_guild_notify_logout_member = 960, + s_guild_notify_change_member_grade = 961, + s_guild_notify_change_member_grade_me = 962, + s_guild_notify_change_master = 963, + s_guild_notify_change_master_me = 964, + s_guild_notify_change_notify = 965, + s_guild_notify_change_mark = 966, + s_guild_notify_change_name = 967, + s_guild_notify_change_capacity = 968, + s_guild_notify_achieve_progress = 969, + s_guild_notify_achieve_complete = 970, + s_guild_notify_pvp_get_grade = 971, + s_guild_notify_pvp_regist = 972, + s_guild_notify_pvp_unregist = 973, + s_guild_notify_pvp_result_win = 974, + s_guild_notify_pvp_result_lose = 975, + s_guild_notify_search_join_accept = 976, + s_guild_notify_search_join_reject = 977, + s_guild_pvp_matching_complete_success = 978, + s_guild_pvp_matching_complete_fail = 979, + s_guild_pvp_matching_complete_popup = 980, + s_guild_pvp_already_pvp_field = 981, + s_guild_pvp_can_regist_when_login = 982, + s_guild_pvp_can_regist_when_playing = 983, + s_guild_pvp_join_only_matched = 984, + s_guild_pvp_join_not_equal_championship_guild = 986, + s_guild_pvp_done_choose_championship_guild = 987, + s_guild_search_choose_propensity = 988, + s_guild_search_guild_search_no_result = 989, + s_guild_search_join_reject_mail_sender = 990, + s_guild_search_join_reject_mail_title = 991, + s_guild_search_join_reject_mail_content = 992, + s_guild_search_request_join_guild = 993, + s_guild_search_cancel_request_join_guild = 994, + s_guild_search_accept_requested_member = 995, + s_guild_search_reject_requested_member = 996, + s_guild_ui_offline_time_day = 997, + s_guild_ui_offline_time_hour = 998, + s_guild_ui_offline_time_min = 999, + s_guild_ui_offline_time_sec = 1000, + s_guild_ui_offline_unknown = 1001, + s_guild_ui_offline = 1002, + s_guild_ui_member_location = 1003, + s_guild_ui_member_detail = 1004, + s_guild_ui_invite_member = 1005, + s_guild_ui_create = 1006, + s_guild_ui_change_notify = 1007, + s_guild_ui_extend_member = 1008, + s_guild_ui_extend_max = 1009, + s_guild_ui_break = 1010, + s_guild_ui_leave = 1011, + s_guild_ui_not_to_master = 1012, + s_guild_ui_change_master = 1013, + s_guild_ui_expel = 1014, + s_guild_ui_guild_create_time = 1015, + s_guild_ui_must_input_group_name = 1016, + s_guild_ui_already_member_have_group = 1017, + s_guild_ui_select_target_member = 1018, + s_guild_ui_current_member = 1019, + s_guild_ui_championship_history_score_normal = 1020, + s_guild_ui_championship_history_no_fight_win = 1021, + s_guild_ui_championship_history_no_fight_lose = 1022, + s_guild_ui_championship_condition = 1023, + s_guild_ui_championship_rating = 1024, + s_guild_ui_championship_combat = 1025, + s_guild_ui_championship_rank = 1026, + s_guild_ui_championship_grade_tooltip = 1027, + s_guild_ui_championship_really_unregist = 1028, + s_guild_ui_championship_no_regist_auth = 1029, + s_guild_ui_championship_no_period_day = 1030, + s_guild_ui_championship_no_period_time = 1031, + s_guild_ui_championship_my_participate = 1032, + s_guild_ui_championship_member_participate = 1033, + s_guild_ui_championship_member_grade_score = 1034, + s_guild_ui_championship_reward_player = 1035, + s_guild_ui_championship_reward_supporter = 1036, + s_guild_ui_championship_choose_championship_guild = 1037, + s_guild_ui_championship_info_championship_guild = 1038, + s_guild_ui_pvp_draw = 1039, + s_guild_ui_pvp_win = 1040, + s_guild_ui_pvp_lose = 1041, + s_guild_ui_pvp_result_winner_rating = 1042, + s_guild_ui_pvp_result_loser_rating_add = 1043, + s_guild_ui_pvp_result_reward = 1044, + s_guild_search_join_requester_info = 1045, + s_guild_err_unknown = 1046, + s_guild_err_null_guild = 1047, + s_guild_err_already_exist = 1048, + s_guild_err_wait_inviting = 1049, + s_guild_err_blocked = 1050, + s_guild_err_has_guild = 1051, + s_guild_err_invalid_guild = 1052, + s_guild_err_null_user = 1053, + s_guild_err_name_exist = 1054, + s_guild_err_name_value = 1055, + s_guild_err_null_member = 1056, + s_guild_err_exist_member = 1057, + s_guild_err_full_member = 1058, + s_guild_err_not_join_member = 1059, + s_guild_err_cannot_leave_master = 1060, + s_guild_err_expel_target_master = 1061, + s_guild_err_not_enough_level = 1062, + s_guild_err_no_money = 1063, + s_guild_err_no_authority = 1064, + s_guild_err_no_master = 1065, + s_guild_err_invalid_grade_range = 1066, + s_guild_err_invalid_capacity_range = 1067, + s_guild_err_invalid_grade_data = 1068, + s_guild_err_invalid_grade_index = 1069, + s_guild_err_exist_empty_grade_index = 1070, + s_guild_err_set_grade_failed = 1071, + s_guild_err_fail_addmember = 1072, + s_guild_err_null_invite_member = 1073, + s_guild_err_cant_during_pvp = 1074, + s_guild_err_none = 1075, + s_guild_err_block = 1076, + s_guild_err_fail_change_gradename = 1077, + s_guild_err_fail_change_gradename_row = 1078, + s_guild_err_fail_this_field = 1079, + s_individual_waiting_unknown = 1080, + s_individual_waiting_min = 1081, + s_individual_waiting_hour = 1082, + s_individual_register_other_arena = 1083, + s_individual_register_call_of_arena = 1084, + s_individual_register_low_level = 1085, + s_individual_register_done = 1086, + s_individual_unregister_done = 1087, + s_individual_matching_done = 1088, + s_individual_join_error_dead = 1089, + s_individual_join_error_field = 1090, + s_whisper_err_myself = 1091, + s_whisper_err_target = 1092, + s_admin_block_velma_notice = 1093, + s_admin_block_velma_notice_chat = 1094, + s_admin_block_velma_add = 1095, + s_admin_block_velma_endtime_dec = 1096, + s_admin_block_velma_msgbox_title = 1097, + s_admin_block_velma_msgbox_content = 1098, + s_admin_block_velma_ugc_notice = 1099, + s_admin_block_velma_ugc_add = 1100, + s_admin_block_velma_ugc_endtime_dec = 1101, + s_admin_block_velma_ugc_msgbox_title = 1102, + s_admin_block_velma_ugc_msgbox_content = 1103, + s_admin_block_transfer_msgbox_title = 1104, + s_admin_block_transfer_msgbox_content = 1105, + s_admin_block_transfer_msgbox_title_kr = 1106, + s_admin_block_transfer_msgbox_content_kr = 1107, + s_admin_block_login = 1108, + s_admin_block_chat = 1109, + s_admin_block_banner_reg = 1110, + s_admin_block_ugcmarket_reg = 1111, + s_admin_block_ugc_equip = 1112, + s_admin_block_ugc_create = 1113, + s_admin_block_guild_create = 1114, + s_admin_block_guild_mark_change = 1115, + s_admin_block_profile_change = 1116, + s_admin_block_char_name_change = 1117, + s_admin_block_mail_send = 1118, + s_admin_block_ugcmap_create = 1119, + s_admin_block_party_search = 1120, + s_admin_block_play_score = 1121, + s_admin_block_write_music = 1122, + s_admin_block_period = 1123, + s_admin_block_transfer = 1124, + s_admin_block_profit = 1125, + s_admin_block_default = 1126, + s_admin_block_create_guild_msgbox_title = 1127, + s_admin_block_create_guild_msgbox_content = 1128, + s_quest_clear_time = 1129, + s_quest_clear_morning = 1130, + s_quest_clear_afternoon = 1131, + s_quest_standard_level = 1132, + s_skill_err_male = 1133, + s_skill_err_female = 1134, + s_skill_err_low_level = 1135, + s_skill_err_weapon = 1136, + s_title_shop = 1137, + s_title_buyitem = 1138, + s_title_sellitem = 1139, + s_ugc_err_code = 1140, + s_ugc_err_url = 1141, + s_ugc_regist_ok = 1142, + s_ugc_upload_ok_item = 1143, + s_ugc_upload_ok_item_screenshot = 1144, + s_ugc_upload_ok_home_screenshot = 1145, + s_ugc_upload_ok_profile = 1146, + s_ugc_upload_ok_banner_sch = 1147, + s_ugc_upload_ok_banner = 1148, + s_ugc_upload_must_profile = 1149, + s_ugc_camera_profile_upload = 1150, + s_ugc_sign_buy = 1151, + s_ugc_sign_sell = 1152, + s_ugc_err_craft_mode = 1153, + s_ugc_confirm_destroy_bank = 1154, + s_ugc_confirm_destroy_fittingdoll = 1155, + s_ugc_confirm_destroy_fittingdoll_v2 = 1156, + s_ugc_confirm_destroy_password = 1157, + s_ugc_confirm_destroy_trigger_editor = 1158, + s_ugc_confirm_destroy_field_affected = 1159, + s_word_ok = 1160, + s_word_cancel = 1161, + s_word_yes = 1162, + s_word_no = 1163, + s_word_accept = 1164, + s_word_deny = 1165, + s_word_complete = 1166, + s_word_next = 1167, + s_word_buy = 1168, + s_word_sell = 1169, + s_word_release = 1170, + s_word_store_unlock = 1171, + s_word_system = 1172, + s_word_entire = 1173, + s_word_friend = 1174, + s_word_party = 1175, + s_word_guild = 1176, + s_word_increase = 1177, + s_word_man = 1178, + s_word_woman = 1179, + s_word_man_or_woman = 1180, + s_word_usercount = 1181, + s_word_stat_increase = 1182, + s_word_stat_increase_near_zero = 1183, + s_word_stat_max = 1184, + s_word_exp = 1185, + s_word_stat_hp = 1186, + s_word_stat_sp = 1187, + s_word_stat_ep = 1188, + s_word_stat_msp = 1189, + s_word_stat_str = 1190, + s_word_stat_dex = 1191, + s_word_stat_int = 1192, + s_word_stat_luk = 1193, + s_word_stat_pap = 1194, + s_word_stat_map = 1195, + s_word_stat_par = 1196, + s_word_stat_mar = 1197, + s_word_stat_asp = 1198, + s_word_stat_atp = 1199, + s_word_stat_cap = 1200, + s_word_stat_cad = 1201, + s_word_stat_ndd = 1202, + s_word_stat_evp = 1203, + s_word_stat_car = 1204, + s_word_stat_abp = 1205, + s_word_stat_jmp = 1206, + s_word_stat_hp_rgp = 1207, + s_word_stat_hp_inv = 1208, + s_word_stat_sp_rgp = 1209, + s_word_stat_sp_inv = 1210, + s_word_stat_ep_rgp = 1211, + s_word_stat_ep_inv = 1212, + s_word_stat_wap = 1213, + s_word_stat_dmg = 1214, + s_word_stat_pen = 1215, + s_word_stat_rmsp = 1216, + s_word_stat_bap = 1217, + s_word_stat_bap_pet = 1218, + s_word_stat_hp_v = 1219, + s_word_stat_sp_v = 1220, + s_word_stat_ep_v = 1221, + s_word_stat_msp_v = 1222, + s_word_stat_str_v = 1223, + s_word_stat_dex_v = 1224, + s_word_stat_int_v = 1225, + s_word_stat_luk_v = 1226, + s_word_stat_pap_v = 1227, + s_word_stat_map_v = 1228, + s_word_stat_par_v = 1229, + s_word_stat_mar_v = 1230, + s_word_stat_asp_v = 1231, + s_word_stat_atp_v = 1232, + s_word_stat_cap_v = 1233, + s_word_stat_cad_v = 1234, + s_word_stat_ndd_v = 1235, + s_word_stat_evp_v = 1236, + s_word_stat_car_v = 1237, + s_word_stat_abp_v = 1238, + s_word_stat_jmp_v = 1239, + s_word_stat_hp_rgp_v = 1240, + s_word_stat_hp_inv_v = 1241, + s_word_stat_sp_rgp_v = 1242, + s_word_stat_sp_inv_v = 1243, + s_word_stat_ep_rgp_v = 1244, + s_word_stat_ep_inv_v = 1245, + s_word_stat_wap_d_v = 1246, + s_word_stat_wap_u_v = 1247, + s_word_stat_wap_common_v = 1248, + s_word_stat_dmg_v = 1249, + s_word_stat_pen_v = 1250, + s_word_stat_rmsp_v = 1251, + s_word_stat_bap_v = 1252, + s_word_stat_bap_pet_v = 1253, + s_word_stat_hp_r = 1254, + s_word_stat_sp_r = 1255, + s_word_stat_ep_r = 1256, + s_word_stat_msp_r = 1257, + s_word_stat_str_r = 1258, + s_word_stat_dex_r = 1259, + s_word_stat_int_r = 1260, + s_word_stat_luk_r = 1261, + s_word_stat_pap_r = 1262, + s_word_stat_map_r = 1263, + s_word_stat_par_r = 1264, + s_word_stat_mar_r = 1265, + s_word_stat_asp_r = 1266, + s_word_stat_atp_r = 1267, + s_word_stat_cap_r = 1268, + s_word_stat_cad_r = 1269, + s_word_stat_ndd_r = 1270, + s_word_stat_evp_r = 1271, + s_word_stat_car_r = 1272, + s_word_stat_abp_r = 1273, + s_word_stat_jmp_r = 1274, + s_word_stat_hp_rgp_r = 1275, + s_word_stat_hp_inv_r = 1276, + s_word_stat_sp_rgp_r = 1277, + s_word_stat_sp_inv_r = 1278, + s_word_stat_ep_rgp_r = 1279, + s_word_stat_ep_inv_r = 1280, + s_word_stat_wap_d_r = 1281, + s_word_stat_wap_u_r = 1282, + s_word_stat_wap_common_r = 1283, + s_word_stat_dmg_r = 1284, + s_word_stat_pen_r = 1285, + s_word_stat_rmsp_r = 1286, + s_word_stat_bap_r = 1287, + s_word_stat_bap_pet_r = 1288, + s_word_stat_tnap = 1289, + s_word_stat_twap = 1290, + s_word_stat_weapon_damage = 1291, + s_word_rank_no = 1292, + s_word_rank_01 = 1293, + s_word_rank_02 = 1294, + s_word_rank_03 = 1295, + s_word_rank_04 = 1296, + s_word_rank_05 = 1297, + s_word_rank_06 = 1298, + s_word_rank_bonus = 1299, + s_word_item_sk = 1300, + s_word_item_hr = 1301, + s_word_item_fa = 1302, + s_word_item_fd = 1303, + s_word_item_lh = 1304, + s_word_item_rh = 1305, + s_word_item_cp = 1306, + s_word_item_mt = 1307, + s_word_item_cl = 1308, + s_word_item_pa = 1309, + s_word_item_gl = 1310, + s_word_item_sh = 1311, + s_word_item_fh = 1312, + s_word_item_ey = 1313, + s_word_item_ea = 1314, + s_word_item_pd = 1315, + s_word_item_ri = 1316, + s_word_item_be = 1317, + s_word_item_er = 1318, + s_word_item_bu = 1319, + s_word_item_de = 1320, + s_word_item_oh = 1321, + s_word_item_look = 1322, + s_word_item_equip = 1323, + s_word_item_category_clothes = 1324, + s_word_item_category_construction = 1325, + s_word_item_unlimited_period = 1326, + s_word_item_soldout = 1327, + s_word_item_soldout_html = 1328, + s_word_item_limit_sell = 1329, + s_word_item_limit_trade = 1330, + s_word_item_limit_sell_trade = 1331, + s_word_item_limit_blackmarket = 1332, + s_word_item_limit_blackmarket_sell = 1333, + s_word_item_limit_trade_count = 1334, + s_word_item_limit_trade_count_without_sell = 1335, + s_word_item_limit_trade_count_without_sell_for_itemlock = 1336, + s_word_item_limit_bind = 1337, + s_word_item_limit_bind_format = 1338, + s_word_item_notuse = 1339, + s_word_item_notuse_hpfull = 1340, + s_word_item_notuse_spfull = 1341, + s_word_item_notuse_epfull = 1342, + s_word_iconcode_none = 1343, + s_word_iconcode_weapon = 1344, + s_word_iconcode_armor = 1345, + s_word_iconcode_accessory = 1346, + s_word_iconcode_active = 1347, + s_word_iconcode_passive = 1348, + s_word_iconcode_potion = 1349, + s_word_iconcode_scroll = 1350, + s_word_iconcode_action = 1351, + s_word_iconcode_etc = 1352, + s_word_iconcode_land = 1353, + s_word_iconcode_building = 1354, + s_word_iconcode_interior = 1355, + s_word_iconcode_souvenir = 1356, + s_word_iconcode_pet = 1357, + s_word_iconcode_riding = 1358, + s_word_iconcode_mannequin = 1359, + s_word_iconcode_store = 1360, + s_word_iconcode_electronics = 1361, + s_word_iconcode_coupon = 1362, + s_word_iconcode_action_skillbook = 1363, + s_word_iconcode_gem = 1364, + s_word_iconcode_storybook = 1365, + s_word_iconcode_maid = 1366, + s_word_iconcode_package = 1367, + s_word_iconcode_random = 1368, + s_word_iconcode_crystal = 1369, + s_word_iconcode_airtaxi = 1370, + s_word_iconcode_dungeonkey = 1371, + s_word_iconcode_workbench_cook = 1372, + s_word_iconcode_workbench_alchemy = 1373, + s_word_iconcode_workbench_biz = 1374, + s_word_iconcode_buffportion = 1375, + s_word_iconcode_trigger_controller = 1376, + s_word_iconcode_interior_pack = 1377, + s_word_iconcode_fishing_rod = 1378, + s_word_iconcode_music_note = 1379, + s_word_iconcode_music_instrument = 1380, + s_word_iconcode_petfood = 1381, + s_word_iconcode_bait = 1382, + s_word_iconcode_gemstone = 1383, + s_word_iconcode_jeweldust = 1384, + s_word_iconcode_coin = 1385, + s_word_iconcode_quest = 1386, + s_word_iconcode_glide_item = 1387, + s_word_iconcode_petequip = 1388, + s_word_iconcode_blueprint = 1389, + s_word_iconcode_capsule = 1390, + s_word_week_sun = 1391, + s_word_week_mon = 1392, + s_word_week_tue = 1393, + s_word_week_wed = 1394, + s_word_week_thu = 1395, + s_word_week_fri = 1396, + s_word_week_sat = 1397, + s_word_week_sun2 = 1398, + s_word_week_mon2 = 1399, + s_word_week_tue2 = 1400, + s_word_week_wed2 = 1401, + s_word_week_thu2 = 1402, + s_word_week_fri2 = 1403, + s_word_week_sat2 = 1404, + s_word_date_week = 1405, + s_word_customize_length = 1406, + s_word_customize_size = 1407, + s_word_customize_location = 1408, + s_word_customize_angle = 1409, + s_word_customize_change = 1410, + s_word_customize_no_edit = 1411, + s_word_customize_comma = 1412, + s_word_game_over = 1413, + s_word_unknown = 1414, + s_word_welcome = 1415, + s_word_item_title_none = 1416, + s_word_item_title_kill = 1417, + s_word_item_title_master = 1418, + s_word_item_title_create = 1419, + s_word_item_title_status = 1420, + s_word_item_title_hidden = 1421, + s_word_item_damage = 1422, + s_word_item_stat_zero = 1423, + s_word_unuse = 1424, + s_word_screenshot = 1425, + s_cannot_move_to_npc = 1426, + s_word_ch = 1427, + s_word_online = 1428, + s_word_offline = 1429, + s_word_dungeon = 1430, + s_word_newTab = 1431, + s_word_level = 1432, + s_word_level_max = 1433, + s_word_learn_level = 1434, + s_word_learn_skillbook = 1435, + s_word_acquire_level = 1436, + s_word_cooltime = 1437, + s_word_needs = 1438, + s_word_fitness = 1439, + s_word_disable_job = 1440, + s_word_sellprice = 1441, + s_word_sellremind_format = 1442, + s_word_require_level = 1443, + s_word_equiped = 1444, + s_word_skillbook = 1445, + s_word_designed = 1446, + s_word_indoor_area = 1447, + s_word_indoor_only = 1448, + s_word_outdoor_only = 1449, + s_word_outdoor_indoor = 1450, + s_word_stackable = 1451, + s_word_do_not_stack = 1452, + s_word_cash = 1453, + s_word_skin = 1454, + s_word_count = 1455, + s_word_magic = 1456, + s_word_nearrange = 1457, + s_word_longrange = 1458, + s_word_taxistation = 1459, + s_word_besttaxistation = 1460, + s_word_telescope = 1461, + s_word_myhome = 1462, + s_word_myhome_property = 1463, + s_word_minimap_alpha = 1464, + s_word_mapbg_alpha = 1465, + s_word_exit_yes = 1466, + s_word_exit_no = 1467, + s_word_item = 1468, + s_word_trade = 1469, + s_word_cashshop = 1470, + s_word_blackmarket = 1471, + s_word_mesomarket = 1472, + s_word_meratmarket = 1473, + s_word_ugc = 1474, + s_word_ugc_banner = 1475, + s_word_homepage = 1476, + s_word_sunday = 1477, + s_word_monday = 1478, + s_word_tuesday = 1479, + s_word_wednesday = 1480, + s_word_thursday = 1481, + s_word_friday = 1482, + s_word_saturday = 1483, + s_word_chatting = 1484, + s_word_mail = 1485, + s_word_smart_push = 1486, + s_word_fishing = 1487, + s_word_safe_riding = 1488, + s_word_amphibious = 1489, + s_word_auto_play_instrument = 1490, + s_word_auto_mining = 1491, + s_word_auto_gathering = 1492, + s_word_auto_breeding = 1493, + s_word_auto_farming = 1494, + s_word_revival = 1496, + s_word_quest = 1497, + s_word_skill = 1498, + s_word_skill_active = 1499, + s_word_skill_passive = 1500, + s_word_skill_move = 1501, + s_word_element_physics = 1502, + s_word_element_fire = 1503, + s_word_element_ice = 1504, + s_word_element_lightning = 1505, + s_word_element_holy = 1506, + s_word_element_darkness = 1507, + s_word_element_poison = 1508, + s_word_min = 1509, + s_word_sec = 1510, + s_word_hour = 1511, + s_word_hour2 = 1512, + s_word_day = 1513, + s_word_remain = 1514, + s_word_group = 1515, + s_word_newgroup = 1516, + s_quest_chapter = 1517, + s_quest_talk_accept = 1518, + s_quest_talk_complete = 1519, + s_quest_talk_progress = 1520, + s_quest_talk_end = 1521, + s_quest_talk_reward_title = 1522, + s_quest_state_init = 1523, + s_quest_state_beginAble = 1524, + s_quest_state_progress = 1525, + s_quest_state_completeAble = 1526, + s_quest_state_complete = 1527, + s_quest_main_quest = 1528, + s_quest_sub_quest = 1529, + s_quest_event_quest = 1530, + s_quest_repeat_infinite = 1531, + s_quest_repeat_daily = 1532, + s_quest_replace_npc = 1533, + s_quest_replace_talk = 1534, + s_quest_replace_quest_object = 1535, + s_quest_replace_field = 1536, + s_quest_replace_item = 1537, + s_quest_replace_satisfied = 1538, + s_mission_replace_npc_meso = 1539, + s_quest_replace_item_move = 1540, + s_quest_not_selected_reward = 1541, + s_quest_boss_notify = 1542, + s_quest_boss_notify_chat = 1543, + s_quest_boss_notify_quest = 1544, + s_quest_error_inventory_full = 1545, + s_quest_error_consume_fail = 1546, + s_quest_error_accept_fail = 1547, + s_quest_error_count_limit = 1548, + s_quest_error_invalid_date = 1549, + s_quest_scroll_progress_quest = 1550, + s_quest_scroll_invalid_begin_quest = 1551, + s_quest_scroll_inventory_full = 1552, + s_quest_scroll_use_item = 1553, + s_title_scroll_duplicate_err = 1554, + s_buddy_confirm_del_somebody_from_list = 1555, + s_buddy_confirm_del_somebody_from_banlist = 1556, + s_buddy_confirm_ban_somebody = 1557, + s_buddy_add_somebody = 1558, + s_buddy_ban_somebody = 1559, + s_buddy_ban_memo_complete = 1560, + s_buddy_request_to_somebody = 1561, + s_buddy_decline_request_from_somebody = 1562, + s_buddy_refused_request_from_somebody = 1563, + s_buddy_cancel_request_from_somebody = 1564, + s_buddy_cancel_request = 1565, + s_buddy_del_somebody_from_list = 1566, + s_buddy_del_somebody_from_banlist = 1567, + s_buddy_alert_receive_request = 1568, + s_buddy_alert_online_somebody = 1569, + s_buddy_alert_offline_somebody = 1570, + s_buddy_request_default_msg = 1571, + s_buddy_waiting = 1572, + s_buddy_format_ch_map = 1573, + s_buddy_format_offline = 1574, + s_buddy_format_mapuser_count = 1575, + s_buddy_format_recv_count = 1576, + s_buddy_format_ban_count = 1577, + s_buddy_format_buddy_count = 1578, + s_buddy_err_unknown = 1579, + s_buddy_err_miss_id = 1580, + s_buddy_err_empty_id = 1581, + s_buddy_err_my_id = 1582, + s_buddy_err_my_id_ex = 1583, + s_buddy_err_not_exist_id = 1584, + s_buddy_err_ban_buddy_add = 1585, + s_buddy_err_already_receive = 1586, + s_buddy_err_max_block = 1587, + s_buddy_err_request_somebody = 1588, + s_buddy_err_max_buddy = 1589, + s_buddy_err_target_full = 1590, + s_buddy_err_already_request = 1591, + s_buddy_err_already_ban = 1592, + s_buddy_err_already_buddy = 1593, + s_buddy_err_already_request_somebody = 1594, + s_buddy_err_already_receive_from_this_char = 1595, + s_format_meso = 1596, + s_format_exp = 1597, + s_format_item = 1598, + s_format_reward_exp = 1599, + s_format_reward_meso = 1600, + s_msg_worldmap_title = 1601, + s_change_ch_err_field = 1602, + s_change_ch_err_battle = 1603, + s_change_ch_err_dead = 1604, + s_action_talk_normal = 1605, + s_action_talk_normal_cinematic = 1606, + s_action_talk_shop = 1607, + s_action_talk_store = 1608, + s_action_talk_mail = 1609, + s_action_enter_cube = 1610, + s_action_portal = 1611, + s_action_interact = 1612, + s_action_buy_site = 1613, + s_action_sale_apartment = 1614, + s_action_sell_cube = 1615, + s_action_regist_ugc = 1616, + s_action_construct = 1617, + s_action_changejob = 1618, + s_action_lift_cube = 1619, + s_action_ride = 1620, + s_action_pull = 1621, + s_action_putdown_liftable = 1622, + s_action_switchcube = 1623, + s_action_breedingcube = 1624, + s_action_farmingcube = 1625, + s_action_harvest = 1626, + s_action_harvesting = 1627, + s_action_remain_time = 1628, + s_action_breeding_growing = 1629, + s_action_farming_growing = 1630, + s_action_fittingdoll = 1631, + s_action_use_telescope = 1632, + s_action_view_cube_profile = 1633, + s_action_owner = 1634, + s_action_taxi_call = 1635, + s_action_taxi_call_progress = 1636, + s_action_cash_taxi_call_progress = 1637, + s_action_regist_banner = 1638, + s_action_fusion = 1639, + s_action_summon_pet = 1640, + s_action_summon_ridee = 1641, + s_action_goto_home = 1642, + s_action_hold = 1643, + s_action_hold_end = 1644, + s_action_cant_pet = 1645, + s_action_cant_ride = 1646, + s_action_privilege_portal = 1647, + s_action_recall_otheruser = 1648, + s_action_fishing = 1649, + s_action_fishing_try = 1650, + s_action_bank_call_progress = 1651, + s_action_webopen = 1652, + s_action_banner = 1653, + s_action_banner_rps = 1654, + s_action_reactor = 1655, + s_action_pickup = 1656, + s_action_homebank_call_progress = 1657, + s_err_homebank_cannot_now = 1658, + s_err_homebank_cannot_now_place = 1659, + s_err_homebank_cannot_now_already = 1660, + s_action_homedoctor_call_progress = 1661, + s_err_homedoctor_cannot_now = 1662, + s_err_homedoctor_cannot_now_place = 1663, + s_cutscene_telescope_keycap = 1664, + s_cutscene_telescope_keydesc = 1665, + s_changejob_accept = 1666, + s_take_boat_accept = 1667, + s_resolve_panelty_accept = 1668, + s_sell_ugc_map_accept = 1669, + s_roulette_accept = 1670, + s_roulette_talk_skip = 1671, + s_html_chat_super = 1672, + s_html_chat_world = 1673, + s_html_chat_channel = 1674, + s_html_chat_normal = 1675, + s_html_chat_party = 1676, + s_html_chat_guild = 1677, + s_html_chat_whisper_to = 1678, + s_html_chat_whisper_from = 1679, + s_html_chat_notice = 1680, + s_html_chat_img_notice = 1681, + s_html_chat_linkable_title = 1682, + s_html_chat_system_notice = 1683, + s_html_chat_club = 1684, + s_html_chat_ugc_event = 1685, + s_html_chat_super_custom = 1686, + s_html_chat_guild_mega_phone = 1687, + s_html_chat_system = 1688, + s_html_chat_wedding = 1689, + s_html_chat_wedding_custom = 1690, + s_system_quest_reward_exp = 1691, + s_system_quest_reward_exp_with_hottimebonus = 1692, + s_system_quest_reward_meso = 1693, + s_system_quest_reward_meso_with_hottimebonus = 1694, + s_system_quest_reward_merat = 1695, + s_system_quest_condition = 1696, + s_system_get_item = 1697, + s_system_property_protection_time = 1698, + s_system_achieve_reward_title = 1699, + s_system_achieve_reward_field_enterance = 1700, + s_system_achieve_reward_shop_unlock = 1701, + s_system_achieve_reward_honor = 1702, + s_system_achieve_reward_karma = 1703, + s_system_achieve_reward_lu = 1704, + s_system_achieve_reward_habi = 1705, + s_html_quest_reward = 1706, + s_ugcmap_ok = 1707, + s_ugcmap_create_on_non_empty_area = 1708, + s_ugcmap_not_exist_craft_item = 1709, + s_ugcmap_not_owned_item = 1710, + s_ugcmap_cant_be_created = 1711, + s_ugcmap_cant_create_on_place = 1712, + s_ugcmap_no_base_cube = 1713, + s_ugcmap_dont_have_ownership = 1714, + s_ugcmap_cant_create_ground_on_ground = 1715, + s_ugcmap_cant_create_on_ground = 1716, + s_ugcmap_only_be_created_on_ground = 1717, + s_ugcmap_cant_stack_on = 1718, + s_ugcmap_db = 1719, + s_ugcmap_center = 1720, + s_ugcmap_no_wall_to_attach = 1721, + s_ugcmap_not_wall_attachable = 1722, + s_ugcmap_only_be_created_on_wall = 1723, + s_ugcmap_cant_attached_to_this_wall = 1724, + s_ugcmap_have_already_attached = 1725, + s_ugcmap_no_cube_to_remove = 1726, + s_ugcmap_cant_be_removed = 1727, + s_ugcmap_cant_remove_before_remove_all_stacked = 1728, + s_ugcmap_cant_remove_building_with_indoor_items = 1729, + s_ugcmap_can_be_remove_from_wall = 1730, + s_ugcmap_no_attached_object = 1731, + s_ugcmap_no_cube_to_rotate = 1732, + s_ugcmap_cant_rotate_default_cube = 1733, + s_ugcmap_no_cube_to_replace = 1734, + s_ugcmap_cant_be_replaced = 1735, + s_ugcmap_attached_cube_exist = 1736, + s_ugcmap_cant_replace_stackable_with_not_stackable = 1737, + s_ugcmap_not_a_buyable = 1738, + s_ugcmap_not_enough_money = 1739, + s_ugcmap_already_owned = 1740, + s_ugcmap_salable = 1741, + s_ugcmap_no_cube_to_lift = 1742, + s_ugcmap_cant_lift_ugc_cube = 1743, + s_ugcmap_cant_lift_salable = 1744, + s_ugcmap_cant_remove_default_cube = 1745, + s_ugcmap_cant_remove_cube_with_attached = 1746, + s_ugcmap_null_cube_item_info = 1747, + s_ugcmap_height_limit = 1748, + s_ugcmap_area_limit = 1749, + s_ugcmap_building_count = 1750, + s_ugcmap_not_for_sale = 1751, + s_ugcmap_no_more_room = 1752, + s_ugcmap_no_home = 1753, + s_ugcmap_my_house = 1754, + s_ugcmap_already_expired = 1755, + s_ugcmap_system_error = 1756, + s_ugcmap_have_equipitems = 1757, + s_ugcmap_cant_replace_same_cube = 1758, + s_ugcmap_cant_buy_more_than_two_house = 1759, + s_ugcmap_need_trophy = 1760, + s_ugcmap_cant_guide_build = 1761, + s_ugcmap_wall_direction_error = 1762, + s_ugcmap_lift_error_msg = 1763, + s_ugcmap_try_place_empty = 1764, + s_ugcmap_cant_replace_type = 1765, + s_ugcmap_cant_rotate_attached = 1766, + s_ugcmap_cant_create_under_attach = 1767, + s_ugcmap_cant_attach_upper_cube = 1768, + s_ugcmap_cant_replace_under_attach = 1769, + s_ugcmap_cant_place_maid = 1770, + s_ugcmap_only_place_on_the_floor = 1771, + s_ugcmap_not_extension_date = 1772, + s_ugcmap_need_extansion_pay = 1773, + s_ugcmap_expired_salable_group = 1774, + s_ugcmap_cant_sell_my_home_in_indoor = 1775, + s_ugcmap_blocked_salable_group = 1776, + s_ugcmap_retry_later = 1777, + s_ugcmap_waiting_for_cube_to_be_created = 1778, + s_ugcmap_waiting_for_cube_to_be_replaced = 1779, + s_ugcmap_waiting_for_cube_to_be_removed = 1780, + s_ugcmap_trigger_count = 1781, + s_ugcmap_no_owner_to_commend = 1782, + s_ugcmap_add_commend_home_fail_from_db = 1783, + s_ugcmap_cant_commend_myself = 1784, + s_ugcmap_cant_commend_duplicate = 1785, + s_ugcmap_ban_word_included = 1786, + s_ugcmap_not_my_house = 1787, + s_ugcmap_automatic_removal = 1788, + s_ugcmap_cant_take_interior_gift_more = 1789, + s_ugcmap_take_interior_gift_fail_from_db = 1790, + s_ugcmap_already_taken_interior_grade_gift = 1791, + s_ugcmap_area_level_extended_successfully = 1792, + s_ugcmap_height_level_extended_successfully = 1793, + s_ugcmap_area_level_shrink_successfully = 1794, + s_ugcmap_height_level_shrink_successfully = 1795, + [Description("You can only use a blueprint while in your home.")] + s_ugcmap_not_use_blueprint_item = 1796, + s_ugc_edit_homeless = 1797, + s_ugc_edit_different_indoorsize = 1798, + s_ugcmap_package_name = 1799, + s_ugcmap_package_description = 1800, + s_ugcmap_package_build_condition = 1801, + s_ugcmap_package_build_description = 1802, + s_ugcmap_buy_realestate = 1803, + s_ugcmap_sell_realestate = 1804, + s_ugcmap_use_sale_coupon_to_buy = 1805, + s_construct_up_limit = 1806, + s_construct_down_limit = 1807, + s_construct_only_indoor = 1808, + s_construct_only_outdoor = 1809, + s_construct_category = 1810, + s_construct_buy_price = 1811, + s_construct_require_interior_level = 1812, + s_construct_require_trophy = 1813, + s_construct_usecount_and_hascount = 1814, + s_construct_usecount_and_hascount_arg = 1815, + s_housing_point_interior_level = 1816, + s_tip_whisper = 1817, + s_mainplayinfo_build_count = 1818, + s_mainplayinfo_maid_count = 1819, + s_mainplayinfo_trigger_count = 1820, + s_warehouse_timeleft_day_normal = 1821, + s_warehouse_timeleft_day_red = 1822, + s_warehouse_timeleft_hour_red = 1823, + s_warehouse_timeleft_minute_red = 1824, + s_warehouse_timeleft_second_red = 1825, + s_warehouse_timeleft_timeover_red = 1826, + s_warehouse_accept = 1827, + s_warehouse_send_to_mail_sender = 1828, + s_warehouse_send_to_mail_title = 1829, + s_warehouse_send_to_mail_content = 1830, + s_worldmap_tooltip_channel = 1831, + s_worldmap_tooltip_myname = 1832, + s_worldmap_tooltip_myhome = 1833, + s_worldmap_tooltip_friend = 1834, + s_worldmap_tooltip_friendhome = 1835, + s_worldmap_tooltip_party = 1836, + s_worldmap_tooltip_level = 1837, + s_worldmap_tooltip_level_upper = 1838, + s_worldmap_tooltip_mapname = 1839, + s_worldmap_tooltip_mapname_low = 1840, + s_worldmap_tooltip_mapname_high = 1841, + s_worldmap_tooltip_fishingspot_low = 1842, + s_worldmap_tooltip_fishingspot_high = 1843, + s_worldmap_tooltip_boss = 1844, + s_worldmap_tooltip_channel_enabled = 1845, + s_worldmap_tooltip_channel_disabled = 1846, + s_worldmap_tooltip_channel_extra = 1847, + s_worldmap_tooltip_portal = 1848, + s_worldmap_tooltip_portal_time = 1849, + s_worldmap_tooltip_quest_completable = 1850, + s_worldmap_tooltip_quest_progress = 1851, + s_worldmap_tooltip_quest_ablenow = 1852, + s_worldmap_tooltip_quest_ableupper = 1853, + s_worldmap_tooltip_taxi_title = 1854, + s_worldmap_tooltip_taxi_price = 1855, + s_worldmap_tooltip_taxi_seal = 1856, + s_worldmap_tooltip_taxi_current = 1857, + s_worldmap_tooltip_taxi_other_continent = 1858, + s_worldmap_tooltip_open_now = 1859, + s_worldmap_tooltip_quest_epic = 1860, + s_worldmap_tooltip_quest_world = 1861, + s_worldmap_tooltip_shadowgate = 1862, + s_worldmap_dungeon_rank_level_proper = 1863, + s_worldmap_dungeon_rank_level_upper = 1864, + s_worldmap_dungeon_expire_date = 1865, + s_minimap_icon_quest_completable_epic = 1866, + s_minimap_icon_quest_completable_event = 1867, + s_minimap_icon_quest_completable_world = 1868, + s_minimap_icon_quest_completable_repeat = 1869, + s_minimap_icon_quest_ablenow_epic = 1870, + s_minimap_icon_quest_ablenow_event = 1871, + s_minimap_icon_quest_ablenow_world = 1872, + s_minimap_icon_quest_ablenow_repeat = 1873, + s_minimap_icon_quest_progress = 1874, + s_minimap_icon_quest_ableupper = 1875, + s_minimap_tooltip_function_npc = 1876, + s_minimap_tooltip_function_npc_1row = 1877, + s_minimap_tooltip_function_portal = 1878, + s_minimap_tooltip_portal = 1879, + s_minimap_tooltip_normal = 1880, + s_minimap_tooltip_normal_alignleft = 1881, + s_minimap_tooltip_quest_completable = 1882, + s_minimap_tooltip_quest_progress = 1883, + s_minimap_tooltip_quest_ablenow = 1884, + s_minimap_tooltip_quest_ableupper = 1885, + s_triggereditor_state_change = 1886, + s_triggereditor_delete_state_invalid = 1887, + s_triggereditor_delete_state_confirm = 1888, + s_triggereditor_modifiy_state_invalid = 1889, + s_triggereditor_delete_attribute = 1890, + s_triggereditor_modelsize_too_long = 1891, + s_triggereditor_modify_attribute_invalid = 1892, + s_triggereditor_new_trigger = 1893, + s_triggereditor_exit_trigger = 1894, + s_triggereditor_upload = 1895, + s_triggereditor_download = 1896, + s_msg_rclick_attach_trade = 1897, + s_msg_rclick_dettach_trade = 1898, + s_msg_rclick_puton_equip = 1899, + s_msg_rclick_putoff_equip = 1900, + s_msg_rclick_store_in = 1901, + s_msg_rclick_store_out = 1902, + s_msg_rclick_sell = 1903, + s_msg_rclick_attach_mail = 1904, + s_msg_rclick_dettach_mail = 1905, + s_msg_rclick_buy = 1906, + s_msg_rclick_putoff_qslot = 1907, + s_msg_rclick_puton_qslot = 1908, + s_msg_rclick_puton_doll = 1909, + s_msg_rclick_putoff_doll = 1910, + s_msg_rclick_upgrade = 1911, + s_msg_rclick_warehouse_out = 1912, + s_msg_rclick_item_break = 1913, + s_msg_rclick_blackmarket = 1914, + s_msg_rclick_play_instrument = 1915, + s_msg_dclick_usable = 1916, + s_msg_dclick_readable = 1917, + s_msg_lclick_usable = 1918, + s_msg_tooltip_compare_swap = 1919, + s_fusion_error = 1920, + s_record_error = 1921, + s_record_audio_error = 1922, + s_tooltip_exp = 1923, + s_tooltip_todayword = 1924, + s_tooltip_party_ch = 1925, + s_tooltip_party = 1926, + s_achieve_adventure = 1927, + s_achieve_combat = 1928, + s_achieve_life = 1929, + s_achieve_reward_item = 1930, + s_achieve_reward_meso = 1931, + s_achieve_reward_exp = 1932, + s_achieve_reward_function = 1933, + s_achieve_reward_title = 1934, + s_achieve_reward_merat = 1935, + s_achieve_reward_field_enterance = 1936, + s_achieve_reward_item_buy_auth = 1937, + s_achieve_meter = 1938, + s_achieve_killometer = 1939, + s_achieve_killometer_f = 1940, + s_achieve_get_achieve_daily = 1941, + s_achieve_get_achieve_hero_progress = 1942, + s_achieve_get_achieve_hero_complete = 1943, + s_achieve_grade = 1944, + s_achieve_state_progress = 1945, + s_achieve_state_complete = 1946, + s_achieve_tooltip_comment = 1947, + s_achieve_reset_account = 1948, + s_achieve_reset_char = 1949, + s_achieve_category_reward_count = 1950, + s_maid_change_maid_name = 1951, + s_maid_change_owner_name = 1952, + s_maid_default_owner_name = 1953, + s_maid_label_birth = 1954, + s_maid_label_constel = 1955, + s_maid_label_height = 1956, + s_maid_label_weight = 1957, + s_maid_label_like = 1958, + s_maid_label_hate = 1959, + s_maid_label_hobby = 1960, + s_maid_label_ownername = 1961, + s_maid_label_belongto = 1962, + s_maid_label_name = 1963, + s_maid_label_title = 1964, + s_maid_label_salary = 1965, + s_maid_need_salary = 1966, + s_maid_expire_date = 1967, + s_maid_expire_date_morning = 1968, + s_maid_expire_date_afternoon = 1969, + s_maid_expire_date_reserved_word = 1970, + s_maid_extend_date_reserved_word = 1971, + s_maid_affinity_gauge_level = 1972, + s_maid_affinity_gauge_level_max = 1973, + s_maid_affinity_gauge_affinity = 1974, + s_maid_affinity_gauge_affinity_max = 1975, + s_maid_edit_confirm = 1976, + s_maid_feel_normal = 1977, + s_maid_feel_good = 1978, + s_maid_feel_very_good = 1979, + s_maid_tooltip_maid_desc = 1980, + s_maid_tooltip_manufactured = 1981, + s_maid_tooltip_manufacturing = 1982, + s_maid_reserved_word_passed_days = 1983, + s_maid_reserved_word_pay_type_meso = 1984, + s_maid_reserved_word_pay_type_merat = 1985, + s_maid_recipe_item_option = 1986, + s_maid_recipe_item_option_range = 1987, + s_manufacture_require = 1988, + s_manufacture_require_over_max = 1989, + s_manufacture_detail = 1990, + s_manufacture_ingredient_count_satisfied = 1991, + s_manufacture_ingredient_count_need = 1992, + s_manufacture_title_specialty = 1993, + s_manufacture_title_status = 1994, + s_manufacture_title_status_tooltip = 1995, + s_manufacture_title_status_tooltip_max = 1996, + s_manufacture_progress_hour = 1997, + s_manufacture_progress_min = 1998, + s_manufacture_progress_sec = 1999, + s_manufacture_progress_left = 2000, + s_manufacture_cancel_confirm = 2001, + s_manufacture_complete = 2002, + s_manufacture_complete_jackpot = 2003, + s_manufacture_complete_by_merat = 2004, + s_manufacture_workbench_tooltip_cook_on = 2005, + s_manufacture_workbench_tooltip_cook_off = 2006, + s_manufacture_workbench_tooltip_alchemy_on = 2007, + s_manufacture_workbench_tooltip_alchemy_off = 2008, + s_manufacture_workbench_tooltip_biz_on = 2009, + s_manufacture_workbench_tooltip_biz_off = 2010, + s_manufacture_cinematic_title = 2011, + s_manufacture_cinematic_affinity = 2012, + s_manufacture_cinematic_affinity_grade_up = 2013, + s_manufacture_cinematic_mood_plus = 2014, + s_manufacture_cinematic_mood_minus = 2015, + s_manufacture_error_msg = 2016, + s_maid_recipe_list_title = 2017, + s_reveal_taxi_station = 2018, + s_interact_err_item = 2019, + s_function_reward_itemget = 2020, + s_cinematic_job_change = 2021, + s_interact_err_quest = 2022, + s_myhouse_address_with_room = 2023, + s_myhouse_name_with_room = 2024, + s_realestate_ask_contract = 2025, + s_realestate_expire_data = 2026, + s_realestate_extension_date = 2027, + s_realestate_ask_extention_merat = 2028, + s_realestate_ask_extention_meso = 2029, + s_realestate_ask_extention_item = 2030, + s_realestate_building_type_0 = 2031, + s_realestate_building_type_1 = 2032, + s_realestate_building_type_2 = 2033, + s_realestate_building_type_3 = 2034, + s_realestate_area = 2035, + s_realestate_install_building_count = 2036, + s_realestate_detail_expire_date = 2037, + s_realestate_detail_expire_time = 2038, + s_realestate_morning = 2039, + s_realestate_afternoon = 2040, + s_realestate_waiting_time = 2041, + s_realestate_date_string = 2042, + s_realestate_price_desc_money = 2043, + s_realestate_price_desc_item = 2044, + s_realestate_description = 2045, + s_realestate_sell_meso = 2046, + s_realestate_sell_merat = 2047, + s_realestate_sell_name = 2048, + s_realestate_sell_wrong = 2049, + s_realestate_sell_cant_sell_my_home_in_indoor = 2050, + s_address_popup_sale = 2051, + s_address_popup_waiting = 2052, + s_address_popup_noroom = 2053, + s_address_popup_myhome = 2054, + s_address_popup_privilege = 2055, + s_address_popup_meso_icon = 2056, + s_address_popup_merat_icon = 2057, + s_realestate_broker_default_buy = 2058, + s_realestate_broker_default_no_room = 2059, + s_realestate_broker_default_my_house = 2060, + s_realestate_broker_click_buy = 2061, + s_realestate_broker_click_no_room = 2062, + s_realestate_broker_click_my_house = 2063, + s_realestate_broker_sell_confirm = 2064, + s_realestate_broker_cant_buy_more_than_two_house_for_site = 2065, + s_realestate_broker_need_trophy = 2066, + s_realestate_this_is_my_apartment = 2067, + s_realestate_apartment_sold_out = 2068, + s_realestate_apartment_sold_out_desc = 2069, + s_realestate_contractable = 2070, + s_realestate_terms = 2071, + s_realestate_terms_text_content = 2072, + s_apartment_input_room_number = 2073, + s_apartment_myhome = 2074, + s_apartment_myhome_contract = 2075, + s_apartment_myhome_tooltip = 2076, + s_achieve_reward_tab = 2077, + s_achieve_reward_tab_great = 2078, + s_achieve_renderer_count = 2079, + s_achieve_renderer_count_time = 2080, + s_achieve_renderer_hour = 2081, + s_achieve_renderer_min = 2082, + s_notify_levelup = 2083, + s_notify_learn_skill = 2084, + s_notify_mail_reply = 2085, + s_notify_mail_reply_content = 2086, + s_notify_mail_remainDay = 2087, + s_notify_mail_remainDay_tooltip = 2088, + s_notify_mail_remainDay_today = 2089, + s_notify_mail_remainDay_tooltip_today = 2090, + s_notify_mail_remainDay_expire = 2091, + s_notify_achieve_reward_function = 2092, + s_notify_achieve_reward_deadfail = 2093, + s_notify_groupchat_invite = 2094, + s_notify_groupchat_invited = 2095, + s_notify_groupchat_leave = 2096, + s_notify_groupchat_login = 2097, + s_notify_groupchat_logout = 2098, + s_notify_groupchat_reject = 2099, + s_err_groupchat_maxjoin = 2100, + s_err_groupchat_null_target_user = 2101, + s_err_groupchat_join_exist = 2102, + s_err_groupchat_name_exist = 2103, + s_err_groupchat_maxgroup = 2104, + s_err_groupchat_add_member_target = 2105, + s_groupchat_list = 2106, + s_groupchat_exit = 2107, + s_tooltip_home_buff = 2108, + s_msg_error_realestate_name = 2109, + s_msg_error_realestate_name_ban_all = 2110, + s_msg_error_realestate_name_ban_text = 2111, + s_worldmap_go_myhome = 2112, + s_worldmap_go_myhome_no_station = 2113, + s_trade_request_success = 2114, + s_trade_recieved_request = 2115, + s_trade_decline = 2116, + s_trade_cancel = 2117, + s_trade_success = 2118, + s_trade_error_system = 2119, + s_trade_error_distance = 2120, + s_trade_error_timeout = 2121, + s_trade_error_already_request = 2122, + s_trade_error_trading_now = 2123, + s_trade_error_meso = 2124, + s_trade_error_latched = 2125, + s_trade_error_decline = 2126, + s_trade_error_itemcount = 2127, + s_trade_error_slotcount = 2128, + s_trade_error_itemnone = 2129, + s_trade_error_pvp = 2130, + s_trade_error_mapLimit = 2131, + s_trade_error_invalid_meso = 2132, + s_trade_error_target_property_protection_time = 2133, + s_trade_error_target_fatigue_penalty = 2134, + s_trade_error = 2135, + s_trade_error_meso_transfer_limited = 2136, + s_trade_error_meso_transfer_limited_other = 2137, + s_trade_error_meso_transfer_limited2 = 2138, + s_trade_error_meso_transfer_limited_other2 = 2139, + s_trade_error_meso_transfer_adv_level_limited = 2140, + s_trade_error_meso_transfer_adv_level_limited_other = 2141, + s_trade_meso = 2142, + s_trade_tradelimit = 2143, + s_trade_tradelimit_blackmarket = 2144, + s_trade_restrict = 2145, + s_trade_tradein = 2146, + s_trade_already_requested = 2147, + s_trade_unlatch_me = 2148, + s_trade_unlatch_oppnent = 2149, + s_trade_begin = 2150, + s_gameoption_init = 2151, + s_gameoption_init_all = 2152, + s_gameoption_init_current = 2153, + s_gameoption_quickslot_warning = 2154, + s_gameoption_modified_warning = 2155, + s_gameoption_keysetting_warning = 2156, + s_gameoption_quality_warning = 2157, + s_gameoption_restart_warning = 2158, + s_gameoption_keysetting_shortcutkey_warning1 = 2159, + s_gameoption_keysetting_shortcutkey_warning2 = 2160, + s_gameoption_keysetting_shortcutkey_warning3 = 2161, + s_gameoption_confirm_reset_layout = 2162, + s_gameoption_keysetting_modifiedkey = 2163, + s_gameoption_keysetting_usenot_gamepad = 2164, + s_gameoption_keysetting_usenot_keyboard = 2165, + s_dynamic_action_already_alloc = 2166, + s_dynamic_action_item_invalid = 2167, + s_dynamic_action_already_learn = 2168, + s_dynamic_action_err_shortcutkey = 2169, + s_dynamic_action_learn = 2170, + s_dynamic_action_learn_ok = 2171, + s_fittingdoll_error_invalid_owner = 2172, + s_fittingdoll_InventoryFull = 2173, + s_fittingdoll_InvalidDoll = 2174, + s_fittingdoll_InvalidItemType_Skin = 2175, + s_fittingdoll_InvalidItemType_Equip = 2176, + s_fittingdoll_InvalidItemType_PutonPc = 2177, + s_fittingdoll_InvalidItemType_PutonDoll = 2178, + s_fittingdoll_InvalidItem = 2179, + s_fittingdoll_InvalidItem_rule = 2180, + s_fittingdoll_Invaliditem_limit = 2181, + s_fittingdoll_Invaliditem_male = 2182, + s_fittingdoll_Invaliditem_female = 2183, + s_fittingdoll_Invalid_slot = 2184, + s_fittingdoll_Invalid_binding = 2185, + s_fittingdoll_Invalid_doll = 2186, + s_fittingdoll_Invalid_moveDisable = 2187, + s_fittingdoll_transform_done_all = 2188, + s_fittingdoll_transform_done_partially = 2189, + [Description("You cannnot use that right now.")] + s_home_returnable_invalid_state = 2190, + [Description("You cannot move to the house from here.")] + s_home_returnable_forbidden = 2191, + [Description("You're already there!")] + s_home_returnable_forbidden_to_sameplace = 2192, + s_home_returnable_homeless = 2193, + s_home_returnable_samefield = 2194, + s_home_returnable_cooldown = 2195, + s_home_returnable_fieldname = 2196, + s_home_returnable_newcharacter = 2197, + s_home_returnable_confirm_place = 2198, + s_home_returnable_confirm_last_place = 2199, + s_home_returnable_without_indoor = 2200, + s_home_visit_confirm = 2201, + s_home_visit_failed_dead = 2202, + s_home_visit_failed_common = 2203, + s_home_visit_failed_homeless = 2204, + s_home_visit_failed_disablemap = 2205, + s_home_visit_failed_myhome = 2206, + s_home_invite_confirm = 2207, + s_home_invite_accept = 2208, + s_home_invite_reject = 2209, + s_home_invite_acceptwait = 2210, + s_home_invite_logout = 2211, + s_home_invite_denybyauto = 2212, + s_home_invite_timeout = 2213, + [Description("You cannot invite yourself.")] + s_home_invite_self = 2214, + s_home_invite_cant_invite_now = 2215, + s_tutorial_shortcutkey_limit = 2216, + s_tutorial_itemputonoff_limit = 2217, + s_tutorial_itemdrop_limit = 2218, + s_tutorial_dialog_limit = 2219, + s_tutorial_skip_movie = 2220, + [Description("Invalid character.")] + s_fail_enterfield_invaliduser = 2221, + s_fail_enterfield_userfull = 2222, + s_fail_enterfield_event_already_start = 2224, + s_interact_result_auth = 2225, + s_interact_result_quest = 2226, + s_interact_result_party = 2227, + s_interact_result_privilege = 2228, + s_interact_result_unknown = 2229, + s_interact_result_mastery = 2230, + s_interact_find_new_telescope = 2231, + s_hunting_kill_boss = 2232, + s_hunting_npc_kill_boss = 2233, + s_mode_pvp_status_winner = 2234, + s_mode_pvp_status_challenger = 2235, + s_returnhome_enable = 2236, + s_returnhome_homeless = 2237, + s_returnhome_disable_cooldown = 2238, + s_returnhome_disable = 2239, + s_returnhome_enable_merat = 2240, + s_returnhome_homeless_merat = 2241, + s_returnhome_disable_cooldown_merat = 2242, + s_returnhome_disable_merat = 2243, + s_report_fail_no_context = 2244, + s_report_fail_no_reason = 2245, + s_report_fail_no_blind_profile_check = 2246, + s_report_really_report = 2247, + s_report_really_report_with_ban = 2248, + s_report_really_report_with_blind = 2249, + s_report_really_blind = 2250, + s_report_success = 2251, + s_report_fail = 2252, + s_report_blind_success = 2253, + s_report_cant_report_own_item = 2254, + s_report_ban_reason = 2255, + s_tencent_report_block_warning_user = 2256, + s_tencent_report_block_warning_ugc_field_banner = 2257, + s_tencent_report_block_warning_ugc_item = 2258, + s_ban_check_err_min_length = 2259, + s_ban_check_err_max_length = 2260, + s_ban_check_err_invalid_char = 2261, + s_ban_check_err_invalid_char_space = 2262, + s_ban_check_err_all_word = 2263, + s_ban_check_err_all_name = 2264, + s_ban_check_err_any_word = 2265, + s_ban_check_err_any = 2266, + s_ban_check_tabname_err_any_word = 2267, + s_ban_check_tabname_err_any = 2268, + s_ban_check_title_err_min_length = 2269, + s_ban_check_title_err_max_length = 2270, + s_ban_check_title_err_invalid_char = 2271, + s_ban_check_title_err_invalid_char_space = 2272, + s_ban_check_title_err_any_word = 2273, + s_ban_check_title_err_any = 2274, + s_ban_check_comment_err_any_word = 2275, + s_ban_check_comment_err_any = 2276, + s_nes_adminRoomEnterFailed = 2277, + s_maintenance_title = 2278, + s_maintenance_modify_enable = 2279, + s_chat_restrict_samechat = 2280, + s_chat_restrict_fastchat = 2281, + s_chat_restrict_addtime = 2282, + s_chat_restrict_accumwarning = 2283, + s_chat_unknown_command = 2284, + s_chat_whisper_decline = 2285, + s_portal_invincible_effect_name = 2286, + s_portal_invincible_effect_description = 2287, + s_revival_effect_name = 2288, + s_revival_effect_description = 2289, + s_revival_invincible_effect_name = 2290, + s_revival_invincible_effect_description = 2291, + s_err_system_title = 2292, + s_err_system_check_graphic_driver = 2293, + s_err_system_graphic_error0 = 2294, + s_err_system_graphic_error1 = 2295, + s_err_system_graphic_error2 = 2296, + s_err_system_graphic_error3 = 2297, + s_err_system_graphic_error4 = 2298, + s_err_system_graphic_error5 = 2299, + s_err_system_graphic_error6 = 2300, + s_err_system_graphic_error7 = 2301, + s_err_system_graphic_error8 = 2302, + s_err_system_graphic_error9 = 2303, + s_err_system_graphic_error10 = 2304, + s_err_system_graphic_error11 = 2305, + s_err_system_graphic_error12 = 2306, + s_err_system_graphic_error13 = 2307, + s_err_system_graphic_error14 = 2308, + s_err_system_graphic_error15 = 2309, + s_err_system_graphic_error16 = 2310, + s_err_system_graphic_error17 = 2311, + s_err_system_graphic_error18 = 2312, + s_msg_restriction_special_chatting_need_acc_char_level = 2313, + s_msg_restriction_special_chatting_need_acc_adv_level = 2314, + s_msg_restriction_special_chatting_over_daily_usage_limit = 2315, + s_msg_restriction_mail_send_need_acc_char_level = 2316, + s_msg_restriction_mail_send_need_acc_adv_level = 2317, + s_msg_restriction_mail_send_over_daily_usage_limit = 2318, + s_msg_restriction_whisper_chatting_need_acc_char_level = 2319, + s_msg_restriction_whisper_chatting_need_acc_adv_level = 2320, + s_msg_restriction_group_chatting_need_acc_char_level = 2321, + s_msg_restriction_group_chatting_need_acc_adv_level = 2322, + s_msg_restriction_ugc_banner_need_acc_char_level = 2323, + s_msg_restriction_ugc_banner_need_acc_adv_level = 2324, + s_admin_character_name = 2325, + s_itembreak_invalid_item = 2326, + s_itembreak_invalid_donation_item = 2327, + s_itembreak_unknown = 2328, + s_itembreak_excellent_item = 2329, + s_itembreak_donation_excellent_item = 2330, + s_recall_user_notify_recall = 2331, + s_recall_user_notify_confirm_recall = 2332, + s_recall_user_notify_recalled = 2333, + s_recall_user_notify_not_found = 2334, + s_recall_user_notify_cannot_move = 2335, + s_recall_user_notify_reject = 2336, + s_recall_user_notify_already_recalled = 2337, + s_recall_user_confirm = 2338, + s_blackmarket_mail_to_sender = 2339, + s_blackmarket_mail_to_cancel_direct = 2340, + s_blackmarket_mail_to_cancel_expired = 2341, + s_blackmarket_mail_to_cancel_hide = 2342, + s_blackmarket_mail_to_seller_title = 2343, + s_blackmarket_mail_to_seller_title2 = 2344, + s_blackmarket_mail_to_seller_title_pending = 2345, + s_blackmarket_mail_to_seller_content = 2346, + s_blackmarket_mail_to_seller_content_soldout = 2347, + s_blackmarket_mail_to_vipseller_content = 2348, + s_blackmarket_mail_to_vipseller_content_soldout = 2349, + s_blackmarket_mail_to_seller_content2 = 2350, + s_blackmarket_mail_to_seller_content_pending2 = 2351, + s_blackmarket_mail_to_seller_content_pending3 = 2352, + s_blackmarket_mail_to_seller_content2_itemname = 2353, + s_blackmarket_mail_to_buyer_title = 2354, + s_blackmarket_mail_to_buyer_content = 2355, + s_blackmarket_mail_to_cancel_title = 2356, + s_blackmarket_mail_to_cancel_title_expired = 2357, + s_blackmarket_mail_to_cancel_title_hide = 2358, + s_blackmarket_mail_to_cancel_content = 2359, + s_blackmarket_mail_to_fail_add_title = 2360, + s_blackmarket_mail_to_fail_add_content = 2361, + s_blackmarket_notice_try_later = 2362, + s_blackmarket_price_1_unit = 2363, + s_blackmarket_upper_rank = 2364, + s_blackmarket_period = 2365, + s_blackmarket_openday_1day = 2366, + s_blackmarket_openday_2day = 2367, + s_blackmarket_openday_over_2day = 2368, + s_blackmarket_openday_with_hour = 2369, + s_blackmarket_openday_am = 2370, + s_blackmarket_openday_pm = 2371, + s_blackmarket_notice_close = 2372, + s_blackmarket_notice_sell_complete = 2373, + s_blackmarket_remain_time_hhmmss = 2374, + s_blackmarket_remain_time_mmss = 2375, + s_blackmarket_remain_time_ss = 2376, + s_blackmarket_shutdown = 2377, + s_blackmarket_notice_fee = 2378, + s_blackmarket_error_code = 2379, + s_blackmarket_error_disable_registitem = 2380, + s_blackmarket_error_close = 2381, + s_blackmarket_error_already_remove = 2382, + s_blackmarket_error_invalid_sale_price = 2383, + s_blackmarket_error_invalid_sale_price_range = 2384, + s_blackmarket_error_invalid_sale_count = 2385, + s_blackmarket_error_lack_sale_count = 2386, + s_blackmarket_error_max_register_item = 2387, + s_blackmarket_error_fail_register = 2388, + s_blackmarket_error_register_not_exist_in_inven = 2389, + s_blackmarket_error_already_add = 2390, + s_blackmarket_error_search_invalid_name = 2391, + s_blackmarket_error_search_invalid_count = 2392, + s_blackmarket_error_sell_restricted_by_user_level = 2393, + s_blackmarket_error_buy_restricted_by_user_level = 2394, + s_blackmarket_error_sell_restricted_by_char_cdate = 2395, + s_blackmarket_error_buy_restricted_by_char_cdate = 2396, + s_blackmarket_error_sell_restricted_by_user_level_ex = 2397, + s_blackmarket_error_buy_restricted_by_user_level_ex = 2398, + s_trade_error_send_restricted_by_user_level_ex = 2399, + s_trade_error_recv_restricted_by_user_level_ex = 2400, + s_itemdrop_error_restricted_by_user_level_ex = 2401, + s_itempickup_error_restricted_by_user_level_ex = 2402, + s_msg_blackmarket_complete_register = 2403, + s_msg_blackmarket_complete_remove = 2404, + s_msg_blackmarket_complete_buy = 2405, + s_msg_blackmarket_complete_stopsale = 2406, + s_msg_blackmarket_ask_buy = 2407, + s_msg_blackmarket_ask_stopsale = 2408, + s_msg_blackmarket_notice_discount_fee = 2409, + s_tooltip_blackmarket_sellprice = 2410, + s_talk_reward_title = 2411, + s_itemenchant_unknown_err = 2412, + s_itemenchant_invalid_item = 2413, + s_itemenchant_except_item = 2414, + s_itemenchant_damaged_item = 2415, + s_itemenchant_lack_ingredient = 2416, + s_itemenchant_fail_sec = 2417, + s_itemenchant_fail_min = 2418, + s_itemenchant_fail_hour = 2419, + s_itemenchant_fail_day = 2420, + s_itemenchant_fail_duration = 2421, + s_itemenchant_return_item = 2422, + s_itemenchant_messagebox = 2423, + s_itemenchant_cinematic_btn = 2424, + s_itemenchant_valid_item = 2425, + s_itemenchant_damaged_item_tooltip = 2426, + s_itemenchant_option_prop = 2427, + s_itemenchant_option_damage = 2428, + s_itemenchant_option_low_prop = 2429, + s_itemenchant_option_repair = 2430, + s_itemenchant_damage_warning = 2431, + s_itemenchant_success_notice = 2432, + s_itemenchant_use_protector = 2433, + s_itemremake_chat_maxoption = 2434, + s_item_repacking_limit_count = 2435, + s_item_repacking_item_consume_count = 2436, + s_item_repacking_item_limit_desc = 2437, + s_item_repacking_item_consume_desc = 2438, + s_item_repacking_scroll_success_dialog = 2439, + s_shadowworld_removebuff = 2440, + s_shadowworld_removebuff_before = 2441, + s_shadowworld_player_kill = 2442, + s_shadowworld_player_kill_slayer = 2443, + s_shadowworld_player_kill_ruler = 2444, + s_shadowworld_pkzone_msg = 2445, + s_shadowworld_pkzone_chat_msg = 2446, + s_shadowworld_safezone_msg = 2447, + s_shadowworld_safezone_chat_msg = 2448, + s_shadowworld_pkzone_allert = 2449, + s_shadowworld_kill_log_die = 2450, + s_shadowworld_kill_log_kill = 2451, + s_pvp_start = 2452, + s_pvp_ffa_double_kill = 2453, + s_pvp_ffa_triple_kill = 2454, + s_pvp_ffa_slayer_kill = 2455, + s_pvp_ffa_ruler_kill = 2456, + s_common_error_unknown = 2457, + s_common_day = 2458, + s_common_sec = 2459, + s_common_min = 2460, + s_common_hour = 2461, + s_common_min_sec = 2462, + s_common_hour_min = 2463, + s_common_hour_min_sec = 2464, + s_common_date_week = 2465, + s_common_max_size = 2466, + s_common_min_size = 2467, + s_common_text_count = 2468, + s_common_set = 2469, + s_common_meso = 2470, + s_common_merat = 2471, + s_common_cash = 2472, + s_common_payitem = 2473, + s_common_meratname_cash = 2474, + s_common_meratname_free = 2475, + s_common_meratname_market = 2476, + s_common_meratname_event = 2477, + s_common_percent = 2478, + s_bill_format_sumprice = 2479, + s_bill_format_sumprice_beauty = 2480, + s_bill_coupon_hair = 2481, + s_bill_coupon_face = 2482, + s_bill_coupon_makeup = 2483, + s_bill_coupon_skin = 2484, + s_bill_coupon_equip = 2485, + s_beauty_use_coupon = 2486, + s_bill_warning_coloring = 2487, + s_bill_meratname_total = 2488, + s_bill_meratprice_total = 2489, + s_field_limit_unknown = 2490, + s_field_limit_level_min = 2491, + s_field_limit_achieve = 2492, + s_field_limit_admin = 2493, + s_field_limit_content_chat = 2494, + s_room_increase_time = 2495, + s_room_increase_time_type2 = 2496, + s_room_decrease_time = 2497, + s_room_party_err_cooldown = 2498, + s_room_dungeon_time_up = 2499, + s_room_dungeon_cooldown = 2500, + s_shutdown_notify = 2501, + s_msg_shortcutkey_slot_full = 2502, + s_msg_shortcutkey_error = 2503, + s_bootypopup_buff_duration = 2504, + s_field_enteracne_dungeon_boss_tooltip = 2505, + s_field_enteracne_party_status_unknown = 2506, + s_field_enteracne_party_status_level = 2507, + s_field_enteracne_party_status_etc = 2508, + s_field_enteracne_party_notify_enter_cheif = 2509, + s_field_enteracne_party_notify_reset_dungeon = 2510, + s_field_enterance_solo_status = 2511, + s_field_enterance_party_status = 2512, + s_dungeonRoom_cooldown_nextDay = 2513, + s_dungeonRoom_cooldown_dayOfWeeks = 2514, + s_emergency_maintenance_remain_minute = 2515, + s_emergency_maintenance_remain_seconds = 2516, + s_emergency_maintenance_function_limit = 2517, + s_ugc_unload_banner_tooltip = 2518, + s_msg_tooltip_customize_color = 2519, + s_msg_tooltip_customize_buy_random_color = 2520, + s_msg_tooltip_limit_register_meratmarket = 2521, + s_msg_tooltip_enable_play_music = 2522, + s_msg_tooltip_musicscore_enable_playcount = 2523, + s_msg_tooltip_musicscore_disable_playcount = 2524, + s_msg_tooltip_musicscore_enable_playcount_custom_empty = 2525, + s_msg_tooltip_musicscore_custom_used_item_desc = 2526, + s_msg_tooltip_musicscore_custom_used_item_desc_guide = 2527, + s_msg_tooltip_musicscore_custom_empty_stat_desc = 2528, + s_msg_tooltip_musicscore_stat_desc = 2529, + s_msg_tooltip_musicscore_writer = 2530, + s_msg_tooltip_gemstone_enable_upgrade = 2531, + s_upper_hud_token_pocket = 2532, + s_upper_hud_token_pocket_honor = 2533, + s_upper_hud_token_pocket_karma = 2534, + s_upper_hud_token_pocket_lu = 2535, + s_upper_hud_token_pocket_habi = 2536, + s_upper_hud_merat = 2537, + s_upper_hud_merat_raw = 2538, + s_enter_ugcmap_result_no_room = 2539, + s_enter_ugcmap_result_not_exist_room = 2540, + s_enter_ugcmap_result_expired = 2541, + s_enter_ugcmap_result_not_exist_building = 2542, + s_ugc_edit_terms_text_content = 2543, + s_ugc_edit_terms_agree = 2544, + s_ugc_edit_demolish_agree = 2545, + s_ugc_edit_terms_coupon_consume = 2546, + s_ugc_item_edit_terms = 2547, + s_ugc_banner_terms = 2548, + s_ugc_profile_terms = 2549, + s_ugc_guildmark_terms = 2550, + s_ugc_guildposter_terms = 2551, + s_blackmarket_terms = 2552, + s_blackmarket_terms_text_content = 2553, + s_meratmarket_designers_terms = 2554, + s_meratmarket_designers_terms_text_content = 2555, + s_meratmarket_tooltip_sell_count = 2556, + s_meratmarket_tooltip_sell_date = 2557, + s_meratmarket_tooltip_bookmark = 2558, + s_meratmarket_tooltip_myshop_calculate = 2559, + s_meratmarket_complete_buy_item = 2560, + s_meratmarket_complete_buy_item_all = 2561, + s_meratmarket_complete_buy_item_error = 2562, + s_meratmarket_complete_buy_item_all_error = 2563, + s_meratmarket_complete_buy_item_all_with_error = 2564, + s_meratmarket_complete_check_balance_success = 2565, + s_meratmarket_emptytext_ad = 2566, + s_meratmarket_emptytext_not_exist = 2567, + s_meratmarket_error_result_buy_lack_merat = 2568, + s_meratmarket_error_result_buy_lack_empty_slot = 2569, + s_meratmarket_error_result_buy_own_product = 2570, + s_meratmarket_error_result_buy_price_index = 2571, + s_meratmarket_error_result_buy_sold_out = 2572, + s_meratmarket_error_result_buy_sold_out_premium_buylimit = 2573, + s_meratmarket_error_result_buy_change_price = 2574, + s_meratmarket_error_result_buy_count = 2575, + s_meratmarket_error_result_buy_not_sale = 2576, + s_meratmarket_error_result_already_add = 2577, + s_meratmarket_error_result_buy_error = 2578, + s_meratmarket_error_result_check_balance_delay_time = 2579, + s_meratmarket_buy_format = 2580, + s_meratmarket_buy_title = 2581, + s_meratmarket_buy_success_row = 2582, + s_meratmarket_buy_fail_row = 2583, + s_meratmarket_buy_success_all = 2584, + s_meratmarket_buy_success_with_fail = 2585, + s_meratmarket_buy_fail_all = 2586, + s_meratmarket_gift_title = 2587, + s_meratmarket_gift_success_row = 2588, + s_meratmarket_gift_fail_row = 2589, + s_meratmarket_gift_success_all = 2590, + s_meratmarket_gift_success_with_fail = 2591, + s_meratmarket_gift_fail_all = 2592, + s_meratmarket_error_lack_invenslot = 2593, + s_meratmarket_error_already_add_bookmark = 2594, + s_meratmarket_error_remove_state_sale = 2595, + s_meratmarket_error_calculating = 2596, + s_meratmarket_error_register_price = 2597, + s_meratmarket_error_register_invalid_category = 2598, + s_meratmarket_error_register_disable_ugcitem = 2599, + s_meratmarket_error_register_blocked_ugcitem = 2600, + s_meratmarket_error_bancheck_desc = 2601, + s_meratmarket_error_bancheck_tags = 2602, + s_meratmarket_error_common = 2603, + s_meratmarket_error_already_sale = 2604, + s_meratmarket_error_not_sale = 2605, + s_meratmarket_error_not_sale_expired = 2606, + s_meratmarket_error_search_result_empty = 2607, + s_meratmarket_error_search_result_empty_by_itemlink = 2608, + s_meratmarket_error_search_result_empty_by_banner = 2609, + s_meratmarket_error_search_result_with_ban = 2610, + s_meratmarket_error_buy_owner_product = 2611, + s_meratmarket_error_buy_soldout = 2612, + s_meratmarket_error_add_basket_by_buylimit = 2613, + s_meratmarket_error_buy_soldout_premium_buylimit = 2614, + s_meratmarket_bookmark_add_complete = 2615, + s_meratmarket_bookmark_limit_maxcount = 2616, + s_meratmarket_bookmark_input_search = 2617, + s_meratmarket_bookmark_empty_products = 2618, + s_meratmarket_basket_limit_maxcount = 2619, + s_meratmarket_basket_empty_buyall = 2620, + s_meratmarket_basket_complete_add = 2621, + s_meratmarket_myshop_notice_sell_restrict = 2622, + s_meratmarket_myshop_notice_sell_restrict_title = 2623, + s_meratmarket_myshop_notice_restrict_by_sellingamount = 2624, + s_meratmarket_myshop_sell_count = 2625, + s_meratmarket_myshop_history_buy_date = 2626, + s_meratmarket_myshop_limit_register_count = 2627, + s_meratmarket_myshop_notice_calculate = 2628, + s_meratmarket_myshop_ask_stopsale = 2629, + s_meratmarket_myshop_ask_remove = 2630, + s_meratmarket_myshop_empty_calculate = 2631, + s_meratmarket_myshop_history_row = 2632, + s_meratmarket_myshop_history_enable_collect = 2633, + s_meratmarket_myshop_history_schedule_collect = 2634, + s_meratmarket_myshop_history_title = 2635, + s_meratmarket_myshop_complete_stopsale = 2636, + s_meratmarket_myshop_complete_register = 2637, + s_meratmarket_myshop_complete_remove = 2638, + s_meratmarket_myshop_complete_resale = 2639, + s_meratmarket_myshop_complete_calculate = 2640, + s_meratmarket_free_resale_confirm = 2641, + s_meratmarket_register_limit_desc = 2642, + s_meratmarket_register_sale_day = 2643, + s_meratmarket_register_ad_hour_and_price = 2644, + s_meratmarket_register_royalty = 2645, + s_meratmarket_register_notice_price = 2646, + s_meratmarket_register_fee = 2647, + s_meratmarket_register_error_add_not_find = 2648, + s_meratmarket_register_error_add_already = 2649, + s_meratmarket_bill_total_fee = 2650, + s_meratmarket_priceday = 2651, + s_meratmarket_priceday_limitless = 2652, + s_meratmarket_gift_mail_sender = 2653, + s_meratmarket_gift_mail_title = 2654, + s_meratmarket_gift_mail_content = 2655, + s_meratmarket_notice_default = 2656, + s_meratmarket_error_result_gift_not_exist_user = 2657, + s_meratmarket_error_result_gift_target_admin = 2658, + s_meratmarket_error_result_gift_admin = 2659, + s_meratmarket_already_in_modelhouse = 2660, + s_meratmarket_moveto_modelhouse_forbidden = 2661, + s_mesoMarket_mail_to_sender = 2662, + s_mesoMarket_mail_to_cancel_title = 2663, + s_mesoMarket_mail_to_cancel_content = 2664, + s_mesoMarket_mail_to_seller_title = 2665, + s_mesoMarket_mail_to_seller_content = 2666, + s_mesoMarket_mail_to_buyer_title = 2667, + s_mesoMarket_mail_to_buyer_content = 2668, + s_mentoring_new_mentor_mail_sender = 2669, + s_mentoring_new_mentor_mail_title = 2670, + s_mentoring_new_mentor_mail_content = 2671, + s_cashshop_invenpickup = 2672, + s_cashshop_invenpickup_refund = 2673, + s_cashshop_refund = 2674, + s_cashshop_error_onetime_buy = 2675, + s_cashshop_expand_inven_warning = 2676, + s_cashshop_expand_inven_tab_warning = 2677, + s_cashshop_expand_character_slot_warning = 2678, + s_cashshop_dbclick_info = 2679, + s_cashshop_lack_balance = 2680, + s_cashshop_basket = 2681, + s_cashshop_buy_success = 2682, + s_cashshop_get_coupon_success = 2683, + s_cashshop_buy_success_send_mail = 2684, + s_cashshop_gift_success = 2685, + s_cashshop_refund_success = 2686, + s_cashshop_pickup_success = 2687, + s_cashshop_gift_message = 2688, + s_cashshop_gift_comfirm = 2689, + s_cashshop_err_unknown = 2690, + s_cashshop_err_closed = 2691, + s_cashshop_err_closed_pubtest = 2692, + s_cashshop_err_disconnect = 2693, + s_cashshop_err_not_ready_product = 2694, + s_cashshop_err_invalid_gift_name = 2695, + s_cashshop_err_same_account = 2696, + s_cashshop_err_no_empty_slot = 2697, + s_cashshop_err_no_empty_slot_pickup_jp = 2698, + s_cashshop_err_not_purchase_date = 2699, + s_cashshop_err_cannot_gift_product = 2700, + s_cashshop_err_cannot_refund_product = 2701, + s_cashshop_err_null_cash_product_data = 2702, + s_cashshop_err_null_nisms_product = 2703, + s_cashshop_err_requesting = 2704, + s_cashshop_err_invalid_authorization_token = 2705, + s_cashshop_err_balance_unknown = 2706, + s_cashshop_err_balance_block_user = 2707, + s_cashshop_err_balance_maintenance = 2708, + s_cashshop_err_balance_notfind_user = 2709, + s_cashshop_err_purchase_unknown = 2710, + s_cashshop_err_purchase_total_sale_count_over = 2711, + s_cashshop_err_purchase_sale_count_over = 2712, + s_cashshop_err_purchase_invalid_game = 2713, + s_cashshop_err_purchase_invalid_gift_product = 2714, + s_cashshop_err_purchase_eventcash_protect_gift = 2715, + s_cashshop_err_purchase_eventcash_protect_buy = 2716, + s_cashshop_err_purchase_maintenance = 2717, + s_cashshop_err_purchase_limit_purchase = 2718, + s_cashshop_err_purchase_lock_cashuse = 2719, + s_cashshop_err_purchase_block_user = 2720, + s_cashshop_err_purchase_notfind_user = 2721, + s_cashshop_err_purchase_lack_balance = 2722, + s_cashshop_err_purchase_invalid_cash_date = 2723, + s_cashshop_err_purchase_invalid_sale_product = 2724, + s_cashshop_err_purchase_invalid_cash_user = 2725, + s_cashshop_err_inventory_inquiry_unknown = 2726, + s_cashshop_err_inventory_inquiry_maintenance = 2727, + s_cashshop_err_inventory_pickup_unknown = 2728, + s_cashshop_err_inventory_pickup_no_inventory = 2729, + s_cashshop_err_inventory_pickup_lackcount = 2730, + s_cashshop_err_inventory_pickup_maintenance = 2731, + s_cashshop_err_refund_unknown = 2732, + s_cashshop_err_refund_disable_refund = 2733, + s_cashshop_err_refund_over_date = 2734, + s_cashshop_err_refund_no_inventory = 2735, + s_cashshop_err_refund_gift = 2736, + s_cashshop_err_gift_limit = 2737, + s_cashshop_err_gift_age = 2738, + s_cashshop_err_gift_send_block = 2739, + s_cashshop_err_gift_recv_block = 2740, + s_cashshop_err_gift_unknown = 2741, + s_cashshop_err_gift_sale_count_over = 2742, + s_partydps_notice_history_prefix = 2743, + s_partydps_notice_history_row = 2744, + s_partydps_notice_history_total_time = 2745, + s_msgbox_format_profile_char_detail = 2746, + s_messenger_comfirm_chat_super = 2747, + s_messenger_comfirm_chat_world = 2748, + s_messenger_comfirm_chat_channel = 2749, + s_messenger_no_show_messagebox = 2750, + s_messenger_whisper = 2751, + s_messenger_club = 2752, + s_messenger_party_chatballoon_prefix = 2753, + s_messenger_guild_chatballoon_prefix = 2754, + s_gem_error_inventory_full = 2755, + s_gem_error_InvalidSlot = 2756, + s_gem_error_InvalidItem = 2757, + s_gem_error_Expired = 2758, + s_gem_error_PCBang = 2759, + s_gem_error_unknown = 2760, + s_room_exit_ugc_indoor = 2761, + s_room_exit_bonus = 2762, + s_room_exit_massive_event = 2763, + s_room_exit_pvp_zone = 2764, + s_room_exit_msg_ugc_indoor = 2765, + s_room_exit_msg_boss_dungeon = 2766, + s_room_exit_msg_party_dungeon = 2767, + s_room_exit_msg_shadow_expedition_dungeon = 2768, + s_room_exit_msg_bonus = 2769, + s_room_exit_msg_massive_event = 2770, + s_room_exit_msg_pvp_zone = 2771, + s_room_exit_msg_pvp_field = 2772, + s_room_exit_msg_design = 2773, + s_room_exit_msg_blueprint = 2774, + s_room_exit_msg_blueprint_preview = 2775, + s_itemlink_meratmarket = 2776, + s_itemlink_cashshop = 2777, + s_waitingticket_message = 2778, + s_waitingticket_cancel = 2779, + s_debug_invincible = 2780, + s_debug_god_mode = 2781, + s_debug_invisible_all = 2782, + s_debug_invisible_npc = 2783, + s_debug_instant_death = 2784, + s_superchat_use_coupon = 2785, + s_worldchat_use_coupon = 2786, + s_channelchat_use_coupon = 2787, + s_weddingchat_use_coupon = 2788, + s_revival_use_coupon = 2789, + s_pet_name_change_use_coupon = 2790, + s_alreadyloginuser_request_kick = 2791, + s_alreadyloginuser_response_kick = 2792, + s_lauchingfestival_onetimecharacter = 2793, + s_pcbang_play_time1 = 2794, + s_pcbang_play_time2 = 2795, + s_pcbang_cant_tutorial_field = 2796, + s_pcbang_no_pcbang = 2797, + s_pcbang_get_gift_item = 2798, + s_pcbang_get_play_item = 2799, + s_play_time_warnning = 2800, + s_play_time_warnning_fatigue_half_begin = 2801, + s_play_time_warnning_fatigue_half = 2802, + s_play_time_warnning_fatigue_max = 2803, + s_play_time_warnning_chat = 2804, + s_nametag_symbol_none = 2805, + s_nametag_symbol_enchant = 2806, + s_nametag_symbol_seller = 2807, + s_nametag_symbol_guild = 2808, + s_nametag_symbol_architect = 2809, + s_nametag_symbol_trophy = 2810, + s_nametag_symbol_fisher = 2811, + s_nametag_symbol_musician = 2812, + s_nametag_symbol_balrog = 2813, + s_nametag_symbol_pioneer = 2814, + s_nametag_symbol_oneyear = 2815, + s_nametag_symbol_level = 2816, + s_nametag_symbol_enchant_desc = 2817, + s_nametag_symbol_seller_desc = 2818, + s_nametag_symbol_guild_desc = 2819, + s_nametag_symbol_architect_desc = 2820, + s_nametag_symbol_trophy_desc = 2821, + s_nametag_symbol_fisher_desc = 2822, + s_nametag_symbol_musician_desc = 2823, + s_nametag_symbol_balrog_desc = 2824, + s_nametag_symbol_pioneer_desc = 2825, + s_nametag_symbol_oneyear_desc = 2826, + s_nametag_symbol_level_desc = 2827, + s_fieldboss_reward_assistbonus_notify = 2828, + s_macro_penalty_reward_notify = 2829, + s_user_trigger_over_max_length = 2830, + s_user_trigger_over_max_state = 2831, + s_user_trigger_over_max_enter_action = 2832, + s_user_trigger_over_max_condition = 2833, + s_user_trigger_over_max_condition_action = 2834, + s_user_trigger_item_tooltip_enable_trigger_control = 2835, + s_user_trigger_item_tooltip_state_change_action_permission = 2836, + s_user_trigger_debugging_msg = 2837, + s_user_trigger_error_msg_system_error = 2838, + s_user_trigger_error_msg_cube_position = 2839, + s_user_trigger_ask_msg_rollback = 2840, + s_user_trigger_ask_msg_clear_contents = 2841, + s_user_trigger_ask_msg_save = 2842, + s_user_trigger_ask_msg_close = 2843, + s_user_trigger_msg_show_debug = 2844, + s_user_trigger_msg_rollback = 2845, + s_home_password_on_msg = 2846, + s_home_password_off_msg = 2847, + s_home_password_off_confirm = 2848, + s_home_password_input = 2849, + s_home_password_state_on = 2850, + s_home_password_state_off = 2851, + s_home_password_string_error = 2852, + s_home_password_mismatch = 2853, + s_home_password_block_time = 2854, + s_home_password_on_chat_msg = 2855, + s_home_password_off_chat_msg = 2856, + s_home_password_enter_field_chat_msg = 2857, + s_home_password_user_out_chat_msg = 2858, + s_home_password_expire_date_chat_msg = 2859, + s_home_password_user_out_button = 2860, + s_home_commend_success = 2861, + s_home_commend_send_confirm = 2862, + s_team_pvp_red_team_name = 2863, + s_team_pvp_blue_team_name = 2864, + s_team_pvp_north = 2865, + s_team_pvp_center = 2866, + s_team_pvp_south = 2867, + s_team_pvp_conquered = 2868, + s_team_pvp_red_player_plunder = 2869, + s_team_pvp_blue_player_plunder = 2870, + s_team_pvp_winner_reward = 2871, + s_team_pvp_loser_reward = 2872, + s_render_screen_restoration_failed = 2873, + s_render_graphiccard_error = 2874, + s_window_service_auth_error = 2875, + s_util_mem_depletion_url = 2876, + s_util_mem_depletion_phyx = 2877, + s_util_mem_depletion_default = 2878, + s_partysearch_sort_party_create_hi = 2879, + s_partysearch_sort_party_create_low = 2880, + s_partysearch_sort_party_level_hi = 2881, + s_partysearch_sort_party_level_low = 2882, + s_partysearch_sort_memeber_count_hi = 2883, + s_partysearch_sort_memeber_count_low = 2884, + s_partysearch_sort_memeber_create_hi = 2885, + s_partysearch_sort_memeber_create_low = 2886, + s_partysearch_complete_register_partysearch = 2887, + s_partysearch_complete_register_membersearch = 2888, + s_partysearch_complete_remove_partysearch = 2889, + s_partysearch_complete_remove_membersearch = 2890, + s_partysearch_ask_party_join_already_memebersearch = 2891, + s_partysearch_err_search_result_empty = 2892, + s_partysearch_err_search_result_with_ban = 2893, + s_partysearch_err_whisper_myself = 2894, + s_partysearch_err_party_join_myself = 2895, + s_partysearch_err_party_join_already = 2896, + s_partysearch_err_party_join_lack_gearscore = 2897, + s_partysearch_err_party_invite_myself = 2898, + s_partysearch_err_party_invite_not_chief = 2899, + s_partysearch_err_memebersearch_cant_register_not_chief = 2900, + s_partysearch_err_memebersearch_cant_register_max_memebercount = 2901, + s_partysearch_err_memebersearch_cant_register_limit_condition = 2902, + s_partysearch_err_memebersearch_cant_register_already = 2903, + s_partysearch_err_memebersearch_cant_modify_not_chief = 2904, + s_partysearch_err_memebersearch_cant_modify_no_register = 2905, + s_partysearch_err_partysearch_cant_register_in_party = 2906, + s_partysearch_err_partysearch_cant_register_limit_condition = 2907, + s_partysearch_err_partysearch_cant_register_already = 2908, + s_partysearch_err_partysearch_cant_modify_in_party = 2909, + s_partysearch_err_partysearch_cant_modify_no_register = 2910, + s_partysearch_err_dungeon_cooldown = 2911, + s_partysearch_err_server_lastaction = 2912, + s_partysearch_err_server_not_chief = 2913, + s_partysearch_err_server_max_member = 2914, + s_partysearch_err_server_db = 2915, + s_partysearch_err_server_already_register = 2916, + s_partysearch_err_server_invalid_type = 2917, + s_partysearch_err_server_in_party = 2918, + s_partysearch_err_server_registring = 2919, + s_partysearch_err_server_banword_title = 2920, + s_partysearch_err_server_banword_findword = 2921, + s_partysearch_err_server_party_invited = 2922, + s_partysearch_err_server_blocked = 2923, + s_partysearch_err_server_code = 2924, + s_partysearchregister_err_title = 2925, + s_partysearchregister_invalid_dungeonlevel = 2926, + s_partysearchregister_invalid_dungeondata = 2927, + s_partysearchregister_ask_remove = 2928, + s_err_cash_recall_cannot_now = 2929, + s_err_cash_recall_prohibit_map = 2930, + s_err_cash_recall_cannot_place = 2931, + s_err_cash_recall_not_guild = 2932, + s_err_cash_recall_not_party = 2933, + s_err_cash_recall_no_guildmember = 2934, + s_err_cash_recall_no_partymember = 2935, + s_err_cash_recall_no_weddingmember = 2936, + s_err_cash_recall_overflow = 2937, + s_err_cash_recall_cannot_dead = 2938, + s_err_cash_recall_cannot_battle = 2939, + s_cash_recall_confirm = 2940, + s_cash_recall_party_notice = 2941, + s_cash_recall_guild_notice = 2942, + s_cash_recall_expired_notice = 2943, + s_cash_recall_end_notice = 2944, + s_cash_recall_other_continent = 2945, + s_enchantscroll_openscroll = 2946, + s_enchantscroll_desc_enchant = 2947, + s_enchantscroll_desc_restore = 2948, + s_enchantscroll_desc_repair = 2949, + s_enchantscroll_desc_random_enchant = 2950, + s_enchantscroll_limit = 2951, + s_enchantscroll_itemname = 2952, + s_enchantscroll_breaking = 2953, + s_enchantscroll_successprop = 2954, + s_enchantscroll_ok = 2955, + s_enchantscroll_invalid_scroll = 2956, + s_enchantscroll_invalid_item = 2957, + s_enchantscroll_breaking_item = 2958, + s_enchantscroll_invalid_level = 2959, + s_enchantscroll_invalid_slot = 2960, + s_enchantscroll_invalid_rank = 2961, + s_enchantscroll_invalid_grade = 2962, + s_enchantscroll_not_breaking_item = 2963, + s_systemmail_notify_ontime_confirm = 2964, + s_server_name_scania = 2965, + s_server_name_mardia = 2966, + s_server_name_hasello = 2967, + s_server_name_windia = 2968, + s_server_name_flata = 2969, + s_recovery_mail_title = 2970, + s_recovery_mail_content = 2971, + s_recovery_mail_sender = 2972, + s_trade_recovery_mail_title = 2973, + s_trade_recovery_mail_content = 2974, + s_trade_recovery_mail_sender = 2975, + s_doll_recovery_mail_title = 2976, + s_doll_recovery_mail_content = 2977, + s_doll_recovery_mail_sender = 2978, + s_function_cube_error_invalid_cube = 2979, + s_function_cube_error_invalid_pos = 2980, + s_function_cube_error_invalid_summon_user = 2981, + s_err_ugcmap_package_cant_use = 2982, + s_err_ugcmap_package_cant_use_in_this_map = 2983, + [Description("Can only be used in the indoor space of the house.")] + s_err_ugcmap_package_should_use_in_indoor = 2984, + s_err_ugcmap_package_not_a_valid_package_item = 2985, + s_err_ugcmap_package_cant_use_in_others_home = 2986, + [Description("You must clear your home of furnishings first.")] + s_err_ugcmap_package_clear_indoor_first = 2987, + s_err_ugcmap_package_not_a_valid_indoor = 2988, + s_err_ugcmap_package_not_a_indoor_for_package_item = 2989, + s_err_ugcmap_package_not_a_indoor_for_this_package_item = 2990, + s_err_ugcmap_package_failed_to_consume_package_item = 2991, + s_err_ugcmap_package_automatic_creation_is_in_progress = 2992, + s_err_ugcmap_package_automatic_removal_is_in_progress = 2993, + [Description("'The saved design has been applied.")] + s_ugcmap_package_automatic_creation_completed = 2994, + [Description("The home's interior has been cleared of all furnishings.")] + s_ugcmap_package_automatic_removal_completed = 2995, + s_ugcmap_package_automatic_creation_suspended = 2996, + s_ugcmap_package_automatic_removal_suspended = 2997, + s_ugcmap_package_automatic_creation_skip = 2998, + s_ugcmap_package_open_package_when_indoor_size_is_different = 2999, + s_ugcmap_package_open_package_when_indoor_is_empty = 3000, + s_err_ugcmap_design_home_should_use_in_design_home = 3001, + s_err_ugcmap_design_home_not_my_design_home = 3002, + s_err_ugcmap_design_home_indoor_isnt_empty = 3003, + s_err_ugcmap_design_home_should_use_in_home = 3004, + s_err_ugcmap_design_home_nothing_to_import = 3005, + s_err_ugcmap_design_home_invalid_item = 3006, + s_err_ugcmap_design_home_not_enough_meso = 3007, + s_err_ugcmap_design_home_not_enough_merat = 3008, + s_err_ugcmap_design_home_not_enough_item = 3009, + s_err_ugcmap_design_home_not_enough_honor_token = 3010, + s_err_ugcmap_design_home_not_enough_karma_token = 3011, + s_err_ugcmap_design_home_not_enough_lu_token = 3012, + s_err_ugcmap_design_home_not_enough_habi_token = 3013, + s_err_ugcmap_design_home_not_enough_shard_token = 3014, + s_err_ugcmap_design_home_not_enough_red_merat = 3015, + s_err_ugcmap_design_home_not_enough_reverse_coin = 3016, + s_err_ugcmap_design_home_not_enough_star_point = 3017, + s_err_ugcmap_design_home_invalid_design_index = 3018, + s_err_ugcmap_cant_save_maid = 3019, + s_err_ugcmap_guide_object_cant_go_far = 3020, + s_err_ugcmap_cant_find_delegate_owner = 3021, + s_err_ugcmap_cant_use_clear_ugc_map = 3022, + s_err_ugcmap_not_massive_event_field = 3023, + s_err_ugcmap_only_owner_can_set_balance = 3024, + s_err_ugcmap_meso_balance_should_be_positive_number = 3025, + s_err_ugcmap_merat_balance_should_be_positive_number = 3026, + s_err_ugcmap_meso_merat_balance_description = 3027, + s_err_ugcmap_nothing_to_export = 3028, + s_fishing_grade_Lv1 = 3029, + s_fishing_grade_Lv2 = 3030, + s_fishing_grade_Lv3 = 3031, + s_fishing_grade_Lv4 = 3032, + s_fishing_grade_Lv5 = 3033, + s_fishing_grade_Lv6 = 3034, + s_fishing_grade_Lv7 = 3035, + s_fishing_grade_Lv8 = 3036, + s_fishing_grade_Lv9 = 3037, + s_fishing_grade_Lv10 = 3038, + s_fishing_grade_Lv11 = 3039, + s_fishing_grade_Lv12 = 3040, + s_fishing_grade_Lv13 = 3041, + s_fishing_grade_Lv14 = 3042, + s_fishing_grade_Lv15 = 3043, + s_fishing_grade_Lv16 = 3044, + s_fishing_grade_Lv17 = 3045, + s_fishing_grade_Lv18 = 3046, + s_fishing_grade_Lv19 = 3047, + s_fishing_grade_Lv20 = 3048, + s_fishing_grade_Lv21 = 3049, + s_fishing_habitat_water = 3050, + s_fishing_habitat_seawater = 3051, + s_fishing_habitat_poison = 3052, + s_fishing_habitat_lava = 3053, + s_fishing_habitat_oil = 3054, + s_fishing_habitat_devilwater = 3055, + s_fishing_habitat_emeraldwater = 3056, + s_fishing_habitat_all = 3057, + s_fishing_notify_success = 3058, + s_fishing_notify_fail = 3059, + s_fishing_fish_grade = 3060, + s_fishing_fish_catch_count = 3061, + s_fishing_fish_size = 3062, + s_fishing_error_invalid_cube = 3063, + s_fishing_error_notexist_water = 3064, + s_fishing_error_invalid_item = 3065, + s_fishing_error_lack_mastery = 3066, + s_fishing_error_notexist_fish = 3067, + s_fishing_error_system_error = 3068, + s_fishing_error_fishingrod_mastery = 3069, + s_fishing_error_ride = 3070, + s_fishing_error_inventory_full = 3071, + s_fishing_error_ugcmap = 3072, + s_fishing_offset_mastery = 3073, + s_fishing_offset_mastery_firstcatch = 3074, + s_fishing_offset_mastery_bigsizecatch = 3075, + s_fishing_total_mastery = 3076, + s_fishing_need_mastery = 3077, + s_fishing_size_small = 3078, + s_fishing_size_medium = 3079, + s_fishing_size_large = 3080, + s_fishing_size_bigfish = 3081, + s_remake_itemoption_error = 3082, + s_remake_itemoption_error_impossible = 3083, + s_remake_itemoption_error_limitlevel = 3084, + s_tooltip_itemoption_kinds_constant = 3085, + s_tooltip_itemoption_kinds_static = 3086, + s_tooltip_itemoption_kinds_random = 3087, + s_tooltip_itemoption_kinds_title = 3088, + s_tooltip_itemoption_kinds_space = 3089, + s_tooltip_itemoption_enchant = 3090, + s_tooltip_itemoption_enchant_broken = 3091, + s_tooltip_itemoption_stat_row = 3092, + s_tooltip_itemremake_count = 3093, + s_tooltip_itemremake_enable = 3094, + s_wedding_visit_confirm = 3095, + s_wedding_visit_failed_common = 3096, + s_wedding_visit_failed_dead = 3097, + s_wedding_visit_failed_invalid_time = 3098, + s_wedding_visit_failed_user_count_limit_exceeded = 3099, + s_wedding_visit_failed_user_self_link = 3100, + s_wedding_visit_failed_disablemap = 3101, + s_wedding_visit_failed_solo_instance = 3102, + s_wedding_visit_failed_same_place = 3103, + s_err_wedding_chat_cannot_use_common = 3104, + s_err_wedding_chat_cannot_use_no_reservation = 3105, + s_err_wedding_chat_cannot_use_complete = 3106, + s_anti_addiction_cannot_receive = 3107, + s_auto_itemuse_default = 3108, + s_auto_itemuse_condition = 3109, + s_pet_input_name = 3110, + s_pet_summon_on = 3111, + s_pet_summon_off = 3112, + s_pet_btn_summon_on = 3113, + s_pet_btn_summon_off = 3114, + s_pet_inventory_ask_in = 3115, + s_pet_inventory_ask_out = 3116, + s_pet_inventory_slot_use_count = 3117, + s_pet_inventory_expire = 3118, + s_pet_inventory_end_time = 3119, + s_pet_inventory_not_use = 3120, + s_pet_inventory_not_sendin = 3121, + s_pet_inventory_not_sendin_petitem = 3122, + s_pet_effect_end_time = 3123, + s_pet_additional_desc_name = 3124, + s_pet_change_name_free = 3125, + s_pet_change_name_merat = 3126, + s_pet_change_name_samename = 3127, + s_pet_itemuse_cannot_not_posion = 3128, + s_pet_itemuse_cannot_same_item = 3129, + s_pet_itemuse_cannot_same_condition = 3130, + s_pet_itemuse_cannot_notexist_condition = 3131, + s_pet_itemuse_cannot_drag_user_inventory = 3132, + s_pet_error_summon_potion = 3133, + s_pet_extension_ontarget = 3134, + s_pet_extension_period = 3135, + s_pet_extension_period_limit = 3136, + s_pet_extension_period_remain = 3137, + s_pet_extension_period_hungry = 3138, + s_pet_extension_period_not_extendlife = 3139, + s_pet_nutrient_ontarget = 3140, + s_pet_nutrient_period_limit = 3141, + s_pet_nutrient_confirm_replace_effect = 3142, + s_pet_nutrient_success = 3143, + s_pet_nutrient_success_extension = 3144, + s_pet_nutrient_cannot_hungry = 3145, + s_usercommanddescription_user = 3146, + s_usercommanddescription_tester = 3147, + s_usercommanddescription_admin = 3148, + s_timeevent_common_1 = 3149, + s_timeevent_common_4 = 3150, + s_timeevent_common_7 = 3151, + s_timestring_am = 3152, + s_timestring_pm = 3153, + s_skill_compact_control_add_tab = 3154, + s_skill_compact_control_rename_tab = 3155, + s_skill_compact_control_same_tabname = 3156, + s_skill_compact_control_default_tabname = 3157, + s_skill_level_max = 3158, + s_window_title_name = 3159, + s_crashreporter_title = 3160, + s_crashreporter_text = 3161, + s_gameevent_enterfield_confirm = 3162, + s_card_reverse_game_consume_fail = 3163, + s_card_reverse_game_select_count = 3164, + s_card_reverse_game_close_message = 3165, + s_card_reverse_game_reward_notice = 3166, + s_rank_duel_arena_fail = 3167, + s_invalid_break_skinitem = 3168, + s_itemlock_unknown_err = 3169, + s_itemlock_invalid_item_lock = 3170, + s_itemlock_invalid_item_unlock = 3171, + s_party_search_default_msg_1 = 3172, + s_party_search_default_msg_2 = 3173, + s_party_search_default_msg_3 = 3174, + s_party_search_default_msg_4 = 3175, + s_party_search_default_msg_5 = 3176, + s_character_ability_character_category_name = 3177, + s_character_ability_monster_and_dungeon_category_name = 3178, + s_character_ability_play_and_feature_category_name = 3179, + s_character_ability_quest_and_grow_category_name = 3180, + s_character_ability_content_category_name = 3181, + s_character_ability_character_category_tooltip = 3182, + s_character_ability_monster_and_dungeon_category_tooltip = 3183, + s_character_ability_play_and_feature_category_tooltip = 3184, + s_character_ability_quest_and_grow_category_tooltip = 3185, + s_character_ability_content_category_tooltip = 3186, + s_character_ability_err_no_abilitypoint = 3187, + s_character_ability_err_no_meso = 3188, + s_character_ability_err_no_merat = 3189, + s_character_ability_point_info_text = 3190, + s_character_ability_meso_reset_desc = 3191, + s_character_ability_meso_reset_button = 3192, + s_character_ability_merat_reset_desc = 3193, + s_character_ability_merat_reset_button = 3194, + s_character_ability_err_reset_cooltime = 3195, + s_character_ability_disabled_quest_desc = 3196, + s_character_ability_reset_cooltime = 3197, + s_character_ability_reset_cooltime_request = 3198, + s_character_ability_err_level_reset_cooltime = 3199, + s_party_recall_scroll_buy = 3200, + s_msg_autofishing_extend = 3201, + s_msg_autofishing_extend_desc = 3202, + s_msg_autofishing_extend_showcurrentduration_desc = 3203, + s_system_shop_title_extend_autofishing = 3204, + s_notice_bypass = 3205, + s_notice_unstable_network_state = 3206, + s_ugcmap_fun_host_gravity_change = 3207, + s_ugcmap_fun_card_info_user_open = 3208, + s_ugcmap_fun_card_info_user_private = 3209, + s_ugcmap_fun_card_info_deck_open = 3210, + s_ugcmap_fun_card_info_deck_private = 3211, + s_ugcmap_fun_card_info_mine = 3212, + s_ugcmap_fun_card_open_mine = 3213, + s_ugcmap_fun_card_discard_open = 3214, + s_ugcmap_fun_card_discard_private = 3215, + s_ugcmap_fun_card_discard_mine = 3216, + s_ugcmap_fun_card_verify_owned = 3217, + s_ugcmap_fun_card_verify_not_owned = 3218, + s_ugcmap_fun_card_handover_open = 3219, + s_ugcmap_fun_card_handover_private = 3220, + s_ugcmap_fun_card_handover_sender = 3221, + s_ugcmap_fun_card_handover_receiver = 3222, + s_ugcmap_fun_card_add = 3223, + s_ugcmap_fun_card_receive_open = 3224, + s_ugcmap_fun_card_receive_private = 3225, + s_ugcmap_fun_card_receive_mine = 3226, + s_ugcmap_fun_card_deck_not_exist = 3227, + s_ugcmap_fun_card_deck_reset = 3228, + s_ugcmap_fun_card_deck_reset_empty = 3229, + s_ugcmap_fun_card_not_exist = 3230, + s_ugcmap_fun_card_empty_in_deck = 3231, + s_ugcmap_fun_card_too_many_in_deck = 3232, + s_ugcmap_fun_card_target_not_exist = 3233, + s_ugcmap_fun_card_deck_name_empty = 3234, + s_ugcmap_fun_card_deck_name_invalid = 3235, + s_ugcmap_fun_card_deck_too_many = 3236, + s_ugcmap_fun_card_deck_exist_already = 3237, + s_ugcmap_fun_card_deck_is_permanent = 3238, + s_ugcmap_fun_card_deck_add = 3239, + s_ugcmap_fun_card_deck_discard = 3240, + s_ugcmap_fun_survey_open = 3241, + s_ugcmap_fun_survey_secret = 3242, + s_ugcmap_fun_survey_abstention = 3243, + s_ugcmap_fun_survey_guide = 3244, + s_ugcmap_fun_survey_info_head = 3245, + s_ugcmap_fun_survey_info_footer = 3246, + s_ugcmap_fun_survey_create_success = 3247, + s_ugcmap_fun_survey_create_error = 3248, + s_ugcmap_fun_survey_add_success = 3249, + s_ugcmap_fun_survey_add_error = 3250, + s_ugcmap_fun_survey_start = 3251, + s_ugcmap_fun_survey_vote = 3252, + s_ugcmap_fun_survey_end = 3253, + s_ugcmap_fun_survey_result = 3254, + s_ugcmap_fun_survey_close_without_vote = 3255, + s_ugcmap_fun_portal_name_duplicated = 3256, + s_ugcmap_fun_roll = 3257, + s_ugcmap_fun_roll_error = 3258, + s_ugcmap_fun_random_group = 3259, + s_ugcmap_fun_random_pick = 3260, + s_ugcmap_fun_random_pick_special = 3261, + s_ugcmap_fun_typing_ready = 3262, + s_ugcmap_fun_typing_begin = 3263, + s_ugcmap_fun_typing_submit = 3264, + s_ugcmap_fun_typing_end = 3265, + s_ugcmap_fun_typing_ranking = 3266, + s_ugcmap_fun_pvp_ffa_finished = 3267, + s_ugcmap_fun_pvp_ffa_finished_draw = 3268, + s_ugcmap_fun_pvp_ffa_winnter_name = 3269, + s_ugcmap_cant_extend_area_level_anymore = 3270, + s_ugcmap_cant_extend_height_level_anymore = 3271, + s_ugcmap_cube_lock = 3272, + s_ugcmap_cant_additionalbuy = 3273, + s_ugcmap_admin_only = 3274, + s_ugcmap_not_allowed_item = 3275, + s_err_ugcmap_not_enough_meso_balance = 3276, + s_err_ugcmap_not_enough_merat_balance = 3277, + s_err_ugcmap_cant_find_delegator_in_this_map = 3278, + s_err_ugcmap_cant_full_delegator_user = 3279, + s_err_ugcmap_cant_duplicate_delegator_in_this_map = 3280, + s_err_ugcmap_cant_build_empty_ugc = 3281, + s_err_ugcmap_should_use_in_home = 3282, + s_err_ugcmap_construct_exp_overtime = 3283, + s_ugcmap_add_delegator_user = 3284, + s_ugcmap_remove_delegator_user = 3285, + s_ugcmap_give_delegator_user = 3286, + s_ugcmap_release_delegator_user = 3287, + s_ugcmap_removeall_delegator_user = 3288, + s_err_ugcmap_blueprint_preview_cube_action_disabled = 3289, + s_home_today_reward = 3290, + s_home_bill_description = 3291, + s_home_interior_grade_gift_taken = 3292, + s_platform_common_error_unknown_error_code = 3293, + s_platform_common_error_unknown = 3294, + s_platform_common_error_invalid_param = 3295, + s_platform_common_error_not_initialize = 3296, + s_platform_common_error_unstable_install = 3297, + s_platform_common_error_unstable_install_path = 3298, + s_platform_common_error_unstable_install_data = 3299, + s_gamehelper_game_optimize_apply = 3300, + s_gamehelper_voice_chat_enter = 3301, + s_gamehelper_voice_chat_leave = 3302, + s_vip_coupon_extend_msg = 3303, + s_vip_coupon_new_msg = 3304, + s_word_tab_petinfo = 3305, + s_word_tab_petcompose = 3306, + s_word_tab_petevolution = 3307, + s_word_tab_petcollect = 3308, + s_word_tab_remakeoption = 3309, + s_char_input_itemname = 3310, + s_timeevent_boss_lifetimetext1 = 3311, + s_timeevent_boss_lifetimetext2 = 3312, + s_word_pet = 3313, + s_word_battle_pet = 3314, + s_live_broadcast_system_error = 3315, + s_ugcox_create_portal = 3316, + s_ugcox_host_commission_meso_get = 3317, + s_ugcox_entry_prize_meso_get = 3318, + s_ugcox_entry_fee_refund_meso_get = 3319, + s_ugcox_enter_fail_full = 3320, + s_couple_effect_error_openbox_unknown = 3321, + s_couple_effect_error_openbox_charname = 3322, + s_couple_effect_error_openbox_myself_char = 3323, + s_couple_effect_error_openbox_myself_account = 3324, + s_couple_effect_mail_sender = 3325, + s_couple_effect_mail_title_receiver = 3326, + s_couple_effect_mail_content_receiver = 3327, + s_couple_effect_mail_send_partner = 3328, + s_couple_emotion_request_recv = 3329, + s_couple_emotion_request_success = 3330, + s_couple_emotion_failed = 3331, + s_couple_emotion_response_accept = 3332, + s_couple_emotion_response_decline = 3333, + s_couple_emotion_target_user_wrong_position = 3334, + s_couple_emotion_recv_request_in_progressed = 3335, + s_couple_emotion_cannot_request_wrong_state = 3336, + s_couple_emotion_cannot_request_already_in_recv_state = 3337, + s_couple_emotion_cannot_request_long_distance = 3338, + s_couple_emotion_cannot_request_blocked_target = 3339, + s_couple_emotion_cannot_request_in_this_map = 3340, + s_couple_emotion_cannot_response_not_exist_request_user = 3341, + s_couple_emotion_failed_request_not_exist_skill = 3342, + s_couple_emotion_failed_accept_request_user_wrong_state = 3343, + s_couple_emotion_failed_request_already_recv = 3344, + s_couple_emotion_failed_request_already_in_action = 3345, + s_couple_emotion_failed_requset_auto_decline = 3346, + s_couple_emotion_failed_request_wrong_state_target_user = 3347, + s_couple_emotion_failed_accept_cannot_find_request_user = 3348, + s_couple_emotion_failed_teleport_limit_distance = 3349, + s_couple_emotion_failed_response_wrong_state_target_user = 3350, + s_galleryevent_cannot_open_card = 3351, + s_galleryevent_invalid_event = 3352, + s_microgame_rps_open_banner_failed_not_exist_ticket = 3353, + s_microgame_rps_open_banner_failed_action_key = 3354, + s_microgame_rps_open_banner_failed_wrong_state = 3355, + s_microgame_rps_request_failed_not_exist_ticket = 3356, + s_microgame_rps_request_failed_wrong_distance = 3357, + s_microgame_rps_request_failed_wrong_position = 3358, + s_microgame_rps_request_failed_wrong_state = 3359, + s_microgame_rps_request_cancel = 3360, + s_microgame_rps_peer_game_cancel = 3361, + s_microgame_rps_banner_failed_blocked_in_this_field = 3362, + s_microgame_rps_request_failed_blocked_in_this_field = 3363, + s_microgame_rps_request_failed_blocked_user = 3364, + s_microgame_rps_response_failed_blocked_user = 3365, + s_microgame_rps_request_failed_peer_wrong_state = 3366, + s_microgame_rps_accept_failed_peer_wrong_state = 3367, + s_microgame_rps_close = 3368, + s_microgame_rps_waiting = 3369, + s_microgame_rps_request_recv = 3370, + s_microgame_rps_response_decline = 3371, + s_microgame_rps_response_accept = 3372, + s_microgame_rps_failed = 3373, + s_microgame_rps_result_win = 3374, + s_microgame_rps_result_lose = 3375, + s_microgame_rps_result_draw = 3376, + s_survival_event_reduce_safezone_ready = 3377, + s_survival_event_reduce_safezone_start = 3378, + s_function_item_survival_scan_info = 3379, + s_function_item_survival_scan_announce = 3380, + s_treewatering_watering = 3381, + s_treewatering_emotion = 3382, + s_treewatering_watering_casting = 3383, + s_treewatering_emotion_casting = 3384, + s_treewatering_wateringreward = 3385, + s_treewatering_emotionreward = 3386, + s_treewatering_no_waterpot = 3387, + s_treewatering_nomore_watering_reward = 3388, + s_treewatering_nomore_emotion_reward = 3389, + s_notify_adventure_levelup = 3390, + s_cashshop_err_purchase_steam_restircted_country = 3391, + s_cashshop_err_purchase_product_info_expired = 3392, + s_closet_msg_success = 3393, + s_closet_msg_item_not_exist = 3394, + s_closet_msg_item_failed = 3395, + s_socket_error_common = 3396, + s_socket_error_10004 = 3397, + s_socket_error_10013 = 3398, + s_socket_error_10014 = 3399, + s_socket_error_10022 = 3400, + s_socket_error_10024 = 3401, + s_socket_error_10035 = 3402, + s_socket_error_10036 = 3403, + s_socket_error_10037 = 3404, + s_socket_error_10039 = 3405, + s_socket_error_10040 = 3406, + s_socket_error_10048 = 3407, + s_socket_error_10050 = 3408, + s_socket_error_10051 = 3409, + s_socket_error_10052 = 3410, + s_socket_error_10053 = 3411, + s_socket_error_10054 = 3412, + s_socket_error_10055 = 3413, + s_socket_error_10056 = 3414, + s_socket_error_10058 = 3415, + s_socket_error_10060 = 3416, + s_socket_error_10061 = 3417, + s_socket_error_10064 = 3418, + s_socket_error_10065 = 3419, + s_socket_error_10067 = 3420, + s_socket_error_10101 = 3421, + s_socket_error_11001 = 3422, + s_socket_error_11002 = 3423, + s_steam_purchase_restriction_adventure_level = 3424, + s_steam_purchase_restriction_character_level = 3425, + s_steam_purchase_restriction_account_create_days = 3426, + s_common_block_for_spamer_not_enough_max_level = 3427, + s_common_block_for_spamer_not_enough_adventure_level = 3428, + s_wedding_mail_title_receiver = 3429, + s_wedding_mail_contents_receiver = 3430, + s_wedding_mail_change_title_receiver = 3431, + s_wedding_mail_change_contents_receiver = 3432, + s_wedding_mail_cancel_title_receiver = 3433, + s_wedding_mail_cancel_contents_receiver = 3434, + s_payback_error_reward_time = 3435, + s_hideandseek_remain_user = 3436, + s_itemgacha_dialog_title = 3437, + s_itemgacha_dialog_gacha_type_skin = 3438, + s_itemgacha_dialog_gacha_type_look = 3439, + s_itemgacha_dialog_gacha_type_specup = 3440, + s_itemgacha_dialog_gacha_type_special = 3441, +} diff --git a/Maple2.Model/Enum/SystemBanner.cs b/Maple2.Model/Enum/SystemBanner.cs index 4d6fc3b01..4c7c2329e 100644 --- a/Maple2.Model/Enum/SystemBanner.cs +++ b/Maple2.Model/Enum/SystemBanner.cs @@ -1,23 +1,23 @@ -// ReSharper disable InconsistentNaming - -namespace Maple2.Model.Enum; - -public enum SystemBannerType { - none, - merat, - playgift, - pcbang, - pcbangnew, -} - -public enum SystemBannerFunction { - none, - cash, - link, - searchBySN, -} - -public enum SystemBannerLanguage { - Any = -1, - Korean = 2, -} +// ReSharper disable InconsistentNaming + +namespace Maple2.Model.Enum; + +public enum SystemBannerType { + none, + merat, + playgift, + pcbang, + pcbangnew, +} + +public enum SystemBannerFunction { + none, + cash, + link, + searchBySN, +} + +public enum SystemBannerLanguage { + Any = -1, + Korean = 2, +} diff --git a/Maple2.Model/Enum/TimeEventType.cs b/Maple2.Model/Enum/TimeEventType.cs index 10d92af61..f2d71f5fa 100644 --- a/Maple2.Model/Enum/TimeEventType.cs +++ b/Maple2.Model/Enum/TimeEventType.cs @@ -1,11 +1,11 @@ -namespace Maple2.Model.Enum; - -public enum TimeEventType { - Boss = 1, - HiddenRealmPortal = 2, - Unknown3 = 3, - FieldInteractObject = 4, // balloons, chest, etc. - GlobalEvent = 5, - KritiasChestPuzzle = 6, - KritiasInvasion = 7, -} +namespace Maple2.Model.Enum; + +public enum TimeEventType { + Boss = 1, + HiddenRealmPortal = 2, + Unknown3 = 3, + FieldInteractObject = 4, // balloons, chest, etc. + GlobalEvent = 5, + KritiasChestPuzzle = 6, + KritiasInvasion = 7, +} diff --git a/Maple2.Model/Enum/TransferFlag.cs b/Maple2.Model/Enum/TransferFlag.cs index ccc9a5065..b1a428025 100644 --- a/Maple2.Model/Enum/TransferFlag.cs +++ b/Maple2.Model/Enum/TransferFlag.cs @@ -1,21 +1,21 @@ -namespace Maple2.Model.Enum; - -[Flags] -public enum TransferFlag { - None = 0, - Split = 2, - Trade = 4, - Bind = 8, - LimitTrade = 16, -} - -public enum TransferType { - Tradable = 0, - Untradeable = 1, - BindOnLoot = 2, - BindOnEquip = 3, - BindOnUse = 4, - BindOnTrade = 5, - BlackMarketOnly = 6, - BindPet = 7, -} +namespace Maple2.Model.Enum; + +[Flags] +public enum TransferFlag { + None = 0, + Split = 2, + Trade = 4, + Bind = 8, + LimitTrade = 16, +} + +public enum TransferType { + Tradable = 0, + Untradeable = 1, + BindOnLoot = 2, + BindOnEquip = 3, + BindOnUse = 4, + BindOnTrade = 5, + BlackMarketOnly = 6, + BindPet = 7, +} diff --git a/Maple2.Model/Enum/Ugc.cs b/Maple2.Model/Enum/Ugc.cs index d81ad6a0d..d5bd14362 100644 --- a/Maple2.Model/Enum/Ugc.cs +++ b/Maple2.Model/Enum/Ugc.cs @@ -1,18 +1,18 @@ -namespace Maple2.Model.Enum; - -public enum UgcType : byte { - None = 0, - Item = 1, - Furniture = 2, - Banner = 3, - Unknown4 = 4, - ProfileAvatar = 5, - GuildEmblem = 6, - Mount = 7, - GuildBanner = 8, - LayoutBlueprint = 9, - Unknown10 = 10, - ItemIcon = 201, - Unknown11 = 202, - BlueprintIcon = 209, -} +namespace Maple2.Model.Enum; + +public enum UgcType : byte { + None = 0, + Item = 1, + Furniture = 2, + Banner = 3, + Unknown4 = 4, + ProfileAvatar = 5, + GuildEmblem = 6, + Mount = 7, + GuildBanner = 8, + LayoutBlueprint = 9, + Unknown10 = 10, + ItemIcon = 201, + Unknown11 = 202, + BlueprintIcon = 209, +} diff --git a/Maple2.Model/Enum/UgcMarketHomeCategory.cs b/Maple2.Model/Enum/UgcMarketHomeCategory.cs index 9a0765a5b..77d167e4f 100644 --- a/Maple2.Model/Enum/UgcMarketHomeCategory.cs +++ b/Maple2.Model/Enum/UgcMarketHomeCategory.cs @@ -1,14 +1,14 @@ -namespace Maple2.Model.Enum; - -public enum UgcMarketHomeCategory : byte { - None = 0, - Promoted = 1, - New = 2, -} - -public enum UgcMarketListingStatus : byte { - None = 0, - Hold = 1, - Active = 2, - Expired = 3, -} +namespace Maple2.Model.Enum; + +public enum UgcMarketHomeCategory : byte { + None = 0, + Promoted = 1, + New = 2, +} + +public enum UgcMarketListingStatus : byte { + None = 0, + Hold = 1, + Active = 2, + Expired = 3, +} diff --git a/Maple2.Model/Enum/Wedding.cs b/Maple2.Model/Enum/Wedding.cs index c1c799461..7c25ba703 100644 --- a/Maple2.Model/Enum/Wedding.cs +++ b/Maple2.Model/Enum/Wedding.cs @@ -1,41 +1,41 @@ -namespace Maple2.Model.Enum; - -public enum MaritalStatus : short { - Single = 0, - Engaged = 1, - Married = 2, - ConsensualDivorce = 3, - ForceDivorce = 4, - DivorceCoolOff = 5, -} - -public enum MarriageExpType : short { - None = 0, - coupleMessage = 2, - Online = 4, // ?? - logoutPanelty = 6, - attendance = 7, // ?? - - // TODO: figure out the values for these - marriedLife, - weddingExpItem, -} - -public enum MarriageExpLimit { - none, - day, - month, -} - -[Flags] -public enum WeddingHallEntryType { - Guest = 1, - Bride = 2, - Groom = 4, - GroomBride = Groom | Bride, -} - -public enum WeddingHallState { - weddingComplete, - -} +namespace Maple2.Model.Enum; + +public enum MaritalStatus : short { + Single = 0, + Engaged = 1, + Married = 2, + ConsensualDivorce = 3, + ForceDivorce = 4, + DivorceCoolOff = 5, +} + +public enum MarriageExpType : short { + None = 0, + coupleMessage = 2, + Online = 4, // ?? + logoutPanelty = 6, + attendance = 7, // ?? + + // TODO: figure out the values for these + marriedLife, + weddingExpItem, +} + +public enum MarriageExpLimit { + none, + day, + month, +} + +[Flags] +public enum WeddingHallEntryType { + Guest = 1, + Bride = 2, + Groom = 4, + GroomBride = Groom | Bride, +} + +public enum WeddingHallState { + weddingComplete, + +} diff --git a/Maple2.Model/Enum/Widget.cs b/Maple2.Model/Enum/Widget.cs index 62dec456c..cc12c40c3 100644 --- a/Maple2.Model/Enum/Widget.cs +++ b/Maple2.Model/Enum/Widget.cs @@ -1,8 +1,8 @@ -namespace Maple2.Model.Enum; - -public enum WidgetType : byte { - None = 0, - Guide = 1, - SceneMovie = 5, - Round = 12, -} +namespace Maple2.Model.Enum; + +public enum WidgetType : byte { + None = 0, + Guide = 1, + SceneMovie = 5, + Round = 12, +} diff --git a/Maple2.Model/Error/AttendanceError.cs b/Maple2.Model/Error/AttendanceError.cs index 7e7093ec2..39e0a2777 100644 --- a/Maple2.Model/Error/AttendanceError.cs +++ b/Maple2.Model/Error/AttendanceError.cs @@ -1,18 +1,18 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum AttendanceError : byte { - [Description("Not enough mesos.")] - s_attendGift_payAttend_result_lackMoney = 1, - [Description("Not enough merets.")] - s_attendGift_payAttend_result_lackMerat = 2, - [Description("No vouchers.")] - s_attendGift_payAttend_result_hasNotCoupon = 3, - [Description("This event has already been completed.")] - s_attendGift_item_attend_already_used = 5, - [Description("Event not found.")] - s_attendGift_item_attend_not_found_evnet = 6, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum AttendanceError : byte { + [Description("Not enough mesos.")] + s_attendGift_payAttend_result_lackMoney = 1, + [Description("Not enough merets.")] + s_attendGift_payAttend_result_lackMerat = 2, + [Description("No vouchers.")] + s_attendGift_payAttend_result_hasNotCoupon = 3, + [Description("This event has already been completed.")] + s_attendGift_item_attend_already_used = 5, + [Description("Event not found.")] + s_attendGift_item_attend_not_found_evnet = 6, +} diff --git a/Maple2.Model/Error/BeautyError.cs b/Maple2.Model/Error/BeautyError.cs index 16c9a1df6..1bed90333 100644 --- a/Maple2.Model/Error/BeautyError.cs +++ b/Maple2.Model/Error/BeautyError.cs @@ -1,29 +1,29 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum BeautyError { - [Description("Not enough currency.")] - lack_currency = 2, // The client somehow determines which currency is lacking. - [Description("This dye can't be used.")] - s_beauty_msg_error_color = 11, - [Description("You have reached the maximum number of hairstyle slots.")] - s_beauty_msg_error_style_slot_extend_max = 12, - [Description("")] - none = 17, - [Description("You have reached the maximum number of hairstyle slots.\nSaved hairstyles can be deleted by the hair designer.")] - s_beauty_msg_error_style_slot_max = 18, - [Description("Not enough merets.\nDo you want to buy merets?")] - s_err_lack_merat_ask = 19, - [Description("Incorrect character gender.")] - s_beauty_msg_error_style_disable_gender = 22, - [Description("This hairstyle is already applied.")] - s_beauty_msg_error_style_apply_already = 23, - [Description("During the beauty day event, discounted hair cannot be stored.")] - s_beauty_msg_error_style_save_cant_not_beauty_day = 24, - - [Description("System Error. Code = {0}\nWill return to the game.")] - s_beauty_msg_error_code = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum BeautyError { + [Description("Not enough currency.")] + lack_currency = 2, // The client somehow determines which currency is lacking. + [Description("This dye can't be used.")] + s_beauty_msg_error_color = 11, + [Description("You have reached the maximum number of hairstyle slots.")] + s_beauty_msg_error_style_slot_extend_max = 12, + [Description("")] + none = 17, + [Description("You have reached the maximum number of hairstyle slots.\nSaved hairstyles can be deleted by the hair designer.")] + s_beauty_msg_error_style_slot_max = 18, + [Description("Not enough merets.\nDo you want to buy merets?")] + s_err_lack_merat_ask = 19, + [Description("Incorrect character gender.")] + s_beauty_msg_error_style_disable_gender = 22, + [Description("This hairstyle is already applied.")] + s_beauty_msg_error_style_apply_already = 23, + [Description("During the beauty day event, discounted hair cannot be stored.")] + s_beauty_msg_error_style_save_cant_not_beauty_day = 24, + + [Description("System Error. Code = {0}\nWill return to the game.")] + s_beauty_msg_error_code = byte.MaxValue, +} diff --git a/Maple2.Model/Error/BlackMarketError.cs b/Maple2.Model/Error/BlackMarketError.cs index f390a85bd..513a72a66 100644 --- a/Maple2.Model/Error/BlackMarketError.cs +++ b/Maple2.Model/Error/BlackMarketError.cs @@ -1,73 +1,73 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum BlackMarketError : int { - none = 0, - [Description("Failed to list item.\nThe item and deposit will be returned by mail.")] - s_blackmarket_error_fail_register = 5, - [Description("The item could not be sold.")] - s_err_null_product = 13, - [Description("You can't list items that are not in your inventory.\nSelect another item.")] - s_blackmarket_error_register_not_exist_in_inven = 14, - [Description("You have reached the listing limit.\nYou can list up to {0} items.")] - s_blackmarket_error_max_register_item = 21, - [Description("Not enough mesos.")] - s_err_lack_meso = 22, - [Description("This item is sold out.")] - s_err_lack_itemcount = 23, - [Description("Please enter the amount you wish to sell once more.")] - s_blackmarket_error_invalid_sale_count = 25, - [Description("Please enter the amount you wish to sell once more.")] - s_blackmarket_error_lack_sale_count = 26, - [Description("The item has expired.")] - s_blackmarket_error_buy_expired = 27, - [Description("The Black Market can't be used right now.")] - s_blackmarket_error_not_useable = 29, - [Description("This item cannot be listed due to the initial listing.\nPlease try listing the item again.")] - s_blackmarket_error_already_add = 30, - [Description("This item can't be listed on the Black Market.")] - s_blackmarket_error_disable_registitem = 31, - [Description("This word cannot be used.")] - s_ban_check_err_all_word = 36, - [Description("A restriction will be placed on Black Market for 60 seconds after entering the game.")] - s_system_property_protection_time = 37, - [Description("The game will not run due to fatigue time.")] - s_anti_addiction_cannot_receive = 38, - [Description("The Black Market can't be used right now.")] - s_blackmarket_error_close = 39, - [Description("Not enough quantity available for purchase.")] - s_blackmarket_error_purchase_count = 41, - [Description("You cannot purchase an item listed by a character on your account.")] - s_blackmarket_error_cannot_buy_ownProduct = 42, - [Description("You cannot sell in the Black Market if the highest level character in your account is less than {0}.")] - s_blackmarket_error_sell_restricted_by_user_level = 43, - [Description("You cannot purchase from the Black Market if the highest level character in your account is less than {0}.")] - s_blackmarket_error_buy_restricted_by_user_level = 44, - - // Unknown values for the following: - /*[Description("This item includes untradeable gemstones and cannot be listed on the Black Market.")] - s_blackmarket_error_disable_registitem_by_gemstone, - [Description("The item has already been sold.")] - s_blackmarket_error_already_remove, - [Description("Please enter the sale price once more.")] - s_blackmarket_error_invalid_sale_price, - [Description("The total sale price must be between {0} and {1}.")] - s_blackmarket_error_invalid_sale_price_range, - [Description("The Black Market can't be used right now.")] - s_blackmarket_error_not_useable_by_dead, - [Description("You cannot afford the listing deposit.")] - s_blackmarket_error_register_lack_deposit, - [Description("You must set a name or classification to use advanced search.")] - s_blackmarket_error_cannot_searchex_without_condition, - [Description("Your character must be at least {0} hours old to sell on the Black Market.")] - s_blackmarket_error_sell_restricted_by_char_cdate, - [Description("Your character must be at least {0} hours old to buy from the Black Market.")] - s_blackmarket_error_buy_restricted_by_char_cdate, - [Description("You must have a character that's Lv. {1} or higher to list {0} on the Black Market.")] - s_blackmarket_error_sell_restricted_by_user_level_ex, - [Description("You must have a character that's Lv. {1} or higher to buy {0} on the Black Market.")] - s_blackmarket_error_buy_restricted_by_user_level_ex,*/ -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum BlackMarketError : int { + none = 0, + [Description("Failed to list item.\nThe item and deposit will be returned by mail.")] + s_blackmarket_error_fail_register = 5, + [Description("The item could not be sold.")] + s_err_null_product = 13, + [Description("You can't list items that are not in your inventory.\nSelect another item.")] + s_blackmarket_error_register_not_exist_in_inven = 14, + [Description("You have reached the listing limit.\nYou can list up to {0} items.")] + s_blackmarket_error_max_register_item = 21, + [Description("Not enough mesos.")] + s_err_lack_meso = 22, + [Description("This item is sold out.")] + s_err_lack_itemcount = 23, + [Description("Please enter the amount you wish to sell once more.")] + s_blackmarket_error_invalid_sale_count = 25, + [Description("Please enter the amount you wish to sell once more.")] + s_blackmarket_error_lack_sale_count = 26, + [Description("The item has expired.")] + s_blackmarket_error_buy_expired = 27, + [Description("The Black Market can't be used right now.")] + s_blackmarket_error_not_useable = 29, + [Description("This item cannot be listed due to the initial listing.\nPlease try listing the item again.")] + s_blackmarket_error_already_add = 30, + [Description("This item can't be listed on the Black Market.")] + s_blackmarket_error_disable_registitem = 31, + [Description("This word cannot be used.")] + s_ban_check_err_all_word = 36, + [Description("A restriction will be placed on Black Market for 60 seconds after entering the game.")] + s_system_property_protection_time = 37, + [Description("The game will not run due to fatigue time.")] + s_anti_addiction_cannot_receive = 38, + [Description("The Black Market can't be used right now.")] + s_blackmarket_error_close = 39, + [Description("Not enough quantity available for purchase.")] + s_blackmarket_error_purchase_count = 41, + [Description("You cannot purchase an item listed by a character on your account.")] + s_blackmarket_error_cannot_buy_ownProduct = 42, + [Description("You cannot sell in the Black Market if the highest level character in your account is less than {0}.")] + s_blackmarket_error_sell_restricted_by_user_level = 43, + [Description("You cannot purchase from the Black Market if the highest level character in your account is less than {0}.")] + s_blackmarket_error_buy_restricted_by_user_level = 44, + + // Unknown values for the following: + /*[Description("This item includes untradeable gemstones and cannot be listed on the Black Market.")] + s_blackmarket_error_disable_registitem_by_gemstone, + [Description("The item has already been sold.")] + s_blackmarket_error_already_remove, + [Description("Please enter the sale price once more.")] + s_blackmarket_error_invalid_sale_price, + [Description("The total sale price must be between {0} and {1}.")] + s_blackmarket_error_invalid_sale_price_range, + [Description("The Black Market can't be used right now.")] + s_blackmarket_error_not_useable_by_dead, + [Description("You cannot afford the listing deposit.")] + s_blackmarket_error_register_lack_deposit, + [Description("You must set a name or classification to use advanced search.")] + s_blackmarket_error_cannot_searchex_without_condition, + [Description("Your character must be at least {0} hours old to sell on the Black Market.")] + s_blackmarket_error_sell_restricted_by_char_cdate, + [Description("Your character must be at least {0} hours old to buy from the Black Market.")] + s_blackmarket_error_buy_restricted_by_char_cdate, + [Description("You must have a character that's Lv. {1} or higher to list {0} on the Black Market.")] + s_blackmarket_error_sell_restricted_by_user_level_ex, + [Description("You must have a character that's Lv. {1} or higher to buy {0} on the Black Market.")] + s_blackmarket_error_buy_restricted_by_user_level_ex,*/ +} diff --git a/Maple2.Model/Error/BuddyError.cs b/Maple2.Model/Error/BuddyError.cs index 1112188f8..6d3fa296c 100644 --- a/Maple2.Model/Error/BuddyError.cs +++ b/Maple2.Model/Error/BuddyError.cs @@ -1,30 +1,30 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum BuddyError : byte { - ok = 0, - [Description("Character not found.")] - s_buddy_err_miss_id = 1, - [Description("You have already sent a friend request to {0}.")] - s_buddy_err_already_request_somebody = 2, - [Description("{0} is already your friend.")] - s_buddy_err_already_buddy = 3, - [Description("You cannot add yourself.")] - s_buddy_err_my_id_ex = 4, - [Description("You cannot send a friend request to {0}.")] - s_buddy_err_request_somebody = 5, - [Description("You cannot block {0}.")] - s_buddy_err_max_block = 6, - [Description("No friends can be added now.")] - s_buddy_err_max_buddy = 7, - [Description("{0} cannot add any friends right now.")] - s_buddy_err_target_full = 8, - [Description("{0} has declined your friend request.")] - s_buddy_refused_request_from_somebody = 9, // and 11 - - [Description("System Error: Community")] - s_buddy_err_unknown = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum BuddyError : byte { + ok = 0, + [Description("Character not found.")] + s_buddy_err_miss_id = 1, + [Description("You have already sent a friend request to {0}.")] + s_buddy_err_already_request_somebody = 2, + [Description("{0} is already your friend.")] + s_buddy_err_already_buddy = 3, + [Description("You cannot add yourself.")] + s_buddy_err_my_id_ex = 4, + [Description("You cannot send a friend request to {0}.")] + s_buddy_err_request_somebody = 5, + [Description("You cannot block {0}.")] + s_buddy_err_max_block = 6, + [Description("No friends can be added now.")] + s_buddy_err_max_buddy = 7, + [Description("{0} cannot add any friends right now.")] + s_buddy_err_target_full = 8, + [Description("{0} has declined your friend request.")] + s_buddy_refused_request_from_somebody = 9, // and 11 + + [Description("System Error: Community")] + s_buddy_err_unknown = byte.MaxValue, +} diff --git a/Maple2.Model/Error/ChangeAttributesError.cs b/Maple2.Model/Error/ChangeAttributesError.cs index 5661539d9..f3b217004 100644 --- a/Maple2.Model/Error/ChangeAttributesError.cs +++ b/Maple2.Model/Error/ChangeAttributesError.cs @@ -1,25 +1,25 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum ChangeAttributesError { - [Description("You do not own this item.")] - s_itemremake_error_server_not_in_inven = 1, - [Description("This item's bonus attributes cannot be modified.")] - s_itemremake_error_server_impossible = 2, - [Description("Incorrect item.")] - s_itemremake_error_server_null_status = 3, // and 4 - [Description("Not enough materials.")] - s_itemremake_error_server_lack_price = 5, - [Description("The Bonus Attribute change failed.")] - s_itemremake_error_server_fail_apply_option = 6, // and 7 - [Description("Cannot lock attribute.")] - s_itemremake_error_server_fail_lack_lock_consume_item = 9, - [Description("This function has been temporarily restricted.")] - s_content_shutdown_notice = 10, - - [Description("Bonus Attributes Modification Error: code=[{0},{1}]")] - s_itemremake_error_server_default = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum ChangeAttributesError { + [Description("You do not own this item.")] + s_itemremake_error_server_not_in_inven = 1, + [Description("This item's bonus attributes cannot be modified.")] + s_itemremake_error_server_impossible = 2, + [Description("Incorrect item.")] + s_itemremake_error_server_null_status = 3, // and 4 + [Description("Not enough materials.")] + s_itemremake_error_server_lack_price = 5, + [Description("The Bonus Attribute change failed.")] + s_itemremake_error_server_fail_apply_option = 6, // and 7 + [Description("Cannot lock attribute.")] + s_itemremake_error_server_fail_lack_lock_consume_item = 9, + [Description("This function has been temporarily restricted.")] + s_content_shutdown_notice = 10, + + [Description("Bonus Attributes Modification Error: code=[{0},{1}]")] + s_itemremake_error_server_default = byte.MaxValue, +} diff --git a/Maple2.Model/Error/ChangeAttributesScrollError.cs b/Maple2.Model/Error/ChangeAttributesScrollError.cs index 1fcc3c640..cd82880e5 100644 --- a/Maple2.Model/Error/ChangeAttributesScrollError.cs +++ b/Maple2.Model/Error/ChangeAttributesScrollError.cs @@ -1,42 +1,42 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum ChangeAttributesScrollError { - none = 0, - [Description("This item is not eligible.")] - s_itemremake_scroll_error_invalid_target = 1, - [Description("The selected items are no longer valid.")] - s_itemremake_scroll_error_invalid_target_data = 2, - [Description("The selected item cannot use this scroll.")] - s_itemremake_scroll_error_invalid_target_stat = 3, - [Description("The selected item cannot use this scroll.")] - s_itemremake_scroll_error_impossible_slot = 4, - [Description("This scroll cannot be used on the selected item due to its quality.")] - s_itemremake_scroll_error_impossible_rank = 5, - [Description("The selected item cannot use this scroll due to its level.")] - s_itemremake_scroll_error_impossible_level = 6, - [Description("The attributes of the selected item cannot be modified.")] - s_itemremake_scroll_error_impossible_property = 7, - [Description("The scroll cannot be used on the selected item.")] - s_itemremake_scroll_error_impossible_item = 8, - [Description("The selected items are no longer valid.")] - s_itemremake_scroll_error_invalid_scroll = 10, - [Description("The selected items are no longer valid.")] - s_itemremake_scroll_error_invalid_scroll_data = 11, - [Description("Failed to modify attributes.")] - s_itemremake_scroll_error_server_fail_remake = 12, // and 13 - [Description("Failed to apply attributes.")] - s_itemremake_scroll_error_server_fail_apply_before_option = 14, - [Description("Failed to use scroll.")] - s_itemremake_scroll_error_server_fail_consume_scroll = 15, - [Description("Cannot lock attribute.")] - s_itemremake_error_server_fail_lack_lock_consume_item = 16, - [Description("This function has been temporarily restricted.")] - s_content_shutdown_notice = 17, - - [Description("Bonus Attributes Modification Scroll Error: code=[{0},{1}]")] - s_itemremake_scroll_error_server_default = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum ChangeAttributesScrollError { + none = 0, + [Description("This item is not eligible.")] + s_itemremake_scroll_error_invalid_target = 1, + [Description("The selected items are no longer valid.")] + s_itemremake_scroll_error_invalid_target_data = 2, + [Description("The selected item cannot use this scroll.")] + s_itemremake_scroll_error_invalid_target_stat = 3, + [Description("The selected item cannot use this scroll.")] + s_itemremake_scroll_error_impossible_slot = 4, + [Description("This scroll cannot be used on the selected item due to its quality.")] + s_itemremake_scroll_error_impossible_rank = 5, + [Description("The selected item cannot use this scroll due to its level.")] + s_itemremake_scroll_error_impossible_level = 6, + [Description("The attributes of the selected item cannot be modified.")] + s_itemremake_scroll_error_impossible_property = 7, + [Description("The scroll cannot be used on the selected item.")] + s_itemremake_scroll_error_impossible_item = 8, + [Description("The selected items are no longer valid.")] + s_itemremake_scroll_error_invalid_scroll = 10, + [Description("The selected items are no longer valid.")] + s_itemremake_scroll_error_invalid_scroll_data = 11, + [Description("Failed to modify attributes.")] + s_itemremake_scroll_error_server_fail_remake = 12, // and 13 + [Description("Failed to apply attributes.")] + s_itemremake_scroll_error_server_fail_apply_before_option = 14, + [Description("Failed to use scroll.")] + s_itemremake_scroll_error_server_fail_consume_scroll = 15, + [Description("Cannot lock attribute.")] + s_itemremake_error_server_fail_lack_lock_consume_item = 16, + [Description("This function has been temporarily restricted.")] + s_content_shutdown_notice = 17, + + [Description("Bonus Attributes Modification Scroll Error: code=[{0},{1}]")] + s_itemremake_scroll_error_server_default = byte.MaxValue, +} diff --git a/Maple2.Model/Error/CharacterCreateError.cs b/Maple2.Model/Error/CharacterCreateError.cs index 7c8d4a1f0..b87956c54 100644 --- a/Maple2.Model/Error/CharacterCreateError.cs +++ b/Maple2.Model/Error/CharacterCreateError.cs @@ -1,29 +1,29 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum CharacterCreateError : byte { - [Description("Enter at least 2 letters.")] - s_char_err_name = 1, - [Description("This name cannot be used.")] - s_char_err_ban_all = 8, - [Description("Contains a forbidden word ({0}).")] - s_char_err_ban_any = 9, - [Description("You can make up to {0} characters.")] - s_char_err_char_count = 6, - [Description("You can make up to {0} characters.")] - s_char_err_char_count_by_gameevent = 6, - [Description("Incorrect gear.")] - s_char_err_invalid_def_item = 10, - [Description("This name is already being used.")] - s_char_err_already_taken = 11, - [Description("The character cannot be created because of a job restriction.")] - s_char_err_job_forbidden = 12, - [Description("Abnormal activity detected. Character creation will be limited for a period of time.")] - s_char_err_creation_restriction = 14, - - [Description("The character cannot be created because of a system error.")] - s_char_err_system = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum CharacterCreateError : byte { + [Description("Enter at least 2 letters.")] + s_char_err_name = 1, + [Description("This name cannot be used.")] + s_char_err_ban_all = 8, + [Description("Contains a forbidden word ({0}).")] + s_char_err_ban_any = 9, + [Description("You can make up to {0} characters.")] + s_char_err_char_count = 6, + [Description("You can make up to {0} characters.")] + s_char_err_char_count_by_gameevent = 6, + [Description("Incorrect gear.")] + s_char_err_invalid_def_item = 10, + [Description("This name is already being used.")] + s_char_err_already_taken = 11, + [Description("The character cannot be created because of a job restriction.")] + s_char_err_job_forbidden = 12, + [Description("Abnormal activity detected. Character creation will be limited for a period of time.")] + s_char_err_creation_restriction = 14, + + [Description("The character cannot be created because of a system error.")] + s_char_err_system = byte.MaxValue, +} diff --git a/Maple2.Model/Error/CharacterDeleteError.cs b/Maple2.Model/Error/CharacterDeleteError.cs index 652782789..1823e3d94 100644 --- a/Maple2.Model/Error/CharacterDeleteError.cs +++ b/Maple2.Model/Error/CharacterDeleteError.cs @@ -1,35 +1,35 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum CharacterDeleteError : int { - ok = 0, - [Description("This character has already been deleted.")] - s_char_err_already_destroy = 1, - [Description("Because you own real estate, there must be at least 1 active character on the account.")] - s_char_err_exist_ugc_map = 2, - [Description("A guild leader cannot be deleted.")] - s_char_err_guild_master = 3, - [Description("You cannot delete a character while they are a member of a guild.")] - s_char_err_guild = 4, - [Description("The character cannot be deleted because they have an item listed in the Design Shop.")] - s_char_err_ugc_market = 5, - [Description("The character cannot be deleted because they have an item listed on the Black Market.")] - s_char_err_black_market_count = 6, - [Description("The character cannot be deleted because it has unread mail.")] - s_char_err_unread_mail = 7, - [Description("This character is not waiting to be deleted.")] - s_char_err_no_destroy_wait = 8, - [Description("The character cannot be deleted because they have mesos listed on the Black Market.")] - s_char_err_meso_market_count = 9, - // {0}: 2018-01-00 00:00:00 Time Format - [Description("You cannot delete this character until {0}.")] - s_char_err_next_delete_char_date = 10, - [Description("The character cannot be deleted because they are engaged, married, or in process of divorce.")] - s_char_err_wedding = 11, - - [Description("System Error: Can't delete character [code = {0}]")] - s_char_err_destroy = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum CharacterDeleteError : int { + ok = 0, + [Description("This character has already been deleted.")] + s_char_err_already_destroy = 1, + [Description("Because you own real estate, there must be at least 1 active character on the account.")] + s_char_err_exist_ugc_map = 2, + [Description("A guild leader cannot be deleted.")] + s_char_err_guild_master = 3, + [Description("You cannot delete a character while they are a member of a guild.")] + s_char_err_guild = 4, + [Description("The character cannot be deleted because they have an item listed in the Design Shop.")] + s_char_err_ugc_market = 5, + [Description("The character cannot be deleted because they have an item listed on the Black Market.")] + s_char_err_black_market_count = 6, + [Description("The character cannot be deleted because it has unread mail.")] + s_char_err_unread_mail = 7, + [Description("This character is not waiting to be deleted.")] + s_char_err_no_destroy_wait = 8, + [Description("The character cannot be deleted because they have mesos listed on the Black Market.")] + s_char_err_meso_market_count = 9, + // {0}: 2018-01-00 00:00:00 Time Format + [Description("You cannot delete this character until {0}.")] + s_char_err_next_delete_char_date = 10, + [Description("The character cannot be deleted because they are engaged, married, or in process of divorce.")] + s_char_err_wedding = 11, + + [Description("System Error: Can't delete character [code = {0}]")] + s_char_err_destroy = byte.MaxValue, +} diff --git a/Maple2.Model/Error/ChatStickerError.cs b/Maple2.Model/Error/ChatStickerError.cs index 4cf628959..832523be7 100644 --- a/Maple2.Model/Error/ChatStickerError.cs +++ b/Maple2.Model/Error/ChatStickerError.cs @@ -1,12 +1,12 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum ChatStickerError : byte { - [Description("Failed to add stickers.")] - s_msg_chat_emoticon_add_failed = 1, - [Description("You can't add these stickers for one of the following reasons: - pack is expired - pack has shorter duration than what you have - you have a permanent version of the sticker pack.")] - s_msg_chat_emoticon_add_failed_already_exist = 2, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum ChatStickerError : byte { + [Description("Failed to add stickers.")] + s_msg_chat_emoticon_add_failed = 1, + [Description("You can't add these stickers for one of the following reasons: - pack is expired - pack has shorter duration than what you have - you have a permanent version of the sticker pack.")] + s_msg_chat_emoticon_add_failed_already_exist = 2, +} diff --git a/Maple2.Model/Error/ClubError.cs b/Maple2.Model/Error/ClubError.cs index f4fa817e9..55725c4a6 100644 --- a/Maple2.Model/Error/ClubError.cs +++ b/Maple2.Model/Error/ClubError.cs @@ -1,52 +1,52 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum ClubError : int { - none = 0, - [Description("Unknown club error")] - s_club_err_unknown = 1, - [Description("Cannot invite to the club.")] - s_club_err_null_user = 11, - [Description("Cannot invite to the club.")] - s_club_err_null_user_2 = 12, - [Description("Cannot find the club member.")] - s_club_err_null_member = 13, - [Description("Cannot find the club.")] - s_club_err_null_club = 14, - [Description("This character was not invited.")] - s_club_err_null_invite_member = 15, - // 10 ~ 50 - Undefined club error - [Description("Only club leaders can do this.")] - s_club_err_no_master = 51, - [Description("Not a registered club member.")] - s_club_err_not_join_member = 52, - [Description("Club leaders cannot leave their own clubs.")] - s_club_err_cannot_leave_master = 53, - [Description("Failed to deliver club invitation.")] - s_club_err_blocked = 54, - [Description("Failed to invite club member.")] - s_club_err_fail_addmember = 55, - [Description("The club is full.")] - s_club_err_full_member = 57, - [Description("That character cannot join any more clubs.")] - s_club_err_full_club_member = 58, - [Description("A club with this name already exists.")] - s_club_err_name_exist = 60, - [Description("Contains a forbidden word.")] - s_club_err_name_value = 61, - [Description("Clubs cannot be disbanded while they still have members.")] - s_club_err_exist_member = 62, - [Description("That character is already a member of the club.")] - s_club_err_already_exist = 63, - [Description("Some of your party members cannot be invited to the club.")] - s_club_err_notparty_alllogin = 67, - [Description("Clubs can be renamed only once every hour.\nPlease try again later.")] - s_club_err_remain_time = 72, - [Description("This is the club's current name.")] - s_club_err_same_club_name = 73, - [Description("You cannot use spaces in club names.")] - s_club_err_clubname_has_blank = 74, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum ClubError : int { + none = 0, + [Description("Unknown club error")] + s_club_err_unknown = 1, + [Description("Cannot invite to the club.")] + s_club_err_null_user = 11, + [Description("Cannot invite to the club.")] + s_club_err_null_user_2 = 12, + [Description("Cannot find the club member.")] + s_club_err_null_member = 13, + [Description("Cannot find the club.")] + s_club_err_null_club = 14, + [Description("This character was not invited.")] + s_club_err_null_invite_member = 15, + // 10 ~ 50 - Undefined club error + [Description("Only club leaders can do this.")] + s_club_err_no_master = 51, + [Description("Not a registered club member.")] + s_club_err_not_join_member = 52, + [Description("Club leaders cannot leave their own clubs.")] + s_club_err_cannot_leave_master = 53, + [Description("Failed to deliver club invitation.")] + s_club_err_blocked = 54, + [Description("Failed to invite club member.")] + s_club_err_fail_addmember = 55, + [Description("The club is full.")] + s_club_err_full_member = 57, + [Description("That character cannot join any more clubs.")] + s_club_err_full_club_member = 58, + [Description("A club with this name already exists.")] + s_club_err_name_exist = 60, + [Description("Contains a forbidden word.")] + s_club_err_name_value = 61, + [Description("Clubs cannot be disbanded while they still have members.")] + s_club_err_exist_member = 62, + [Description("That character is already a member of the club.")] + s_club_err_already_exist = 63, + [Description("Some of your party members cannot be invited to the club.")] + s_club_err_notparty_alllogin = 67, + [Description("Clubs can be renamed only once every hour.\nPlease try again later.")] + s_club_err_remain_time = 72, + [Description("This is the club's current name.")] + s_club_err_same_club_name = 73, + [Description("You cannot use spaces in club names.")] + s_club_err_clubname_has_blank = 74, +} diff --git a/Maple2.Model/Error/DungeonMatchError.cs b/Maple2.Model/Error/DungeonMatchError.cs index 5623f9afa..5d8e8aaaf 100644 --- a/Maple2.Model/Error/DungeonMatchError.cs +++ b/Maple2.Model/Error/DungeonMatchError.cs @@ -1,27 +1,27 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum DungeonMatchError : byte { - ok = 0, - [Description("You or your party are not eligible to enter any dungeons at this time.\nPlease check again after increasing your Levels and Gear Scores.")] - s_dungeonMatch_error_notFoundDungeon = 1, - [Description("Up to {0} people are allowed.")] - s_dungeonMatch_error_overMaxRegisterUser = 2, - [Description("You do not meet the dungeon entry requirements.")] - s_dungeonMatch_error_lackRequire = 3, - [Description("One of the selected dungeons cannot be entered. Please adjust your selection.")] - s_dungeonMatch_error_containCouldNotEnter = 4, - [Description("A party member is still in the dungeon.\nPlease try again after all party members have exited the dungeon.")] - s_dungeonMatch_error_insideDungeonUser = 5, - [Description("Only the party leader can send the request.")] - s_dungeonMatch_error_isNotChief = 6, - [Description("A party member has disconnected.")] - s_dungeonMatch_error_hasOfflineUser = 7, - [Description("You cannot search for a new recommended normal adventure dungeon party while [Comradery] is active.")] - s_dungeonMatch_error_hasDungeonMatchCooldown = 8, - [Description("[Comradery] will expire in {0}. You may not search for a new recommended normal adventure dungeon party during this time.")] - s_dungeonMatch_error_hasDungeonMatchCooldownParty = 9, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum DungeonMatchError : byte { + ok = 0, + [Description("You or your party are not eligible to enter any dungeons at this time.\nPlease check again after increasing your Levels and Gear Scores.")] + s_dungeonMatch_error_notFoundDungeon = 1, + [Description("Up to {0} people are allowed.")] + s_dungeonMatch_error_overMaxRegisterUser = 2, + [Description("You do not meet the dungeon entry requirements.")] + s_dungeonMatch_error_lackRequire = 3, + [Description("One of the selected dungeons cannot be entered. Please adjust your selection.")] + s_dungeonMatch_error_containCouldNotEnter = 4, + [Description("A party member is still in the dungeon.\nPlease try again after all party members have exited the dungeon.")] + s_dungeonMatch_error_insideDungeonUser = 5, + [Description("Only the party leader can send the request.")] + s_dungeonMatch_error_isNotChief = 6, + [Description("A party member has disconnected.")] + s_dungeonMatch_error_hasOfflineUser = 7, + [Description("You cannot search for a new recommended normal adventure dungeon party while [Comradery] is active.")] + s_dungeonMatch_error_hasDungeonMatchCooldown = 8, + [Description("[Comradery] will expire in {0}. You may not search for a new recommended normal adventure dungeon party during this time.")] + s_dungeonMatch_error_hasDungeonMatchCooldownParty = 9, +} diff --git a/Maple2.Model/Error/DungeonRoomError.cs b/Maple2.Model/Error/DungeonRoomError.cs index 4d49d8c4c..3c7fc043c 100644 --- a/Maple2.Model/Error/DungeonRoomError.cs +++ b/Maple2.Model/Error/DungeonRoomError.cs @@ -1,73 +1,73 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum DungeonRoomError { - none = 0, - [Description("The party leader must enter first.")] - s_room_party_err_not_chief = 1, - [Description("Entry limit reached.")] - s_room_party_err_full_room = 2, - [Description("You do not have permission to enter.")] - s_room_dungeon_error_invalidPartyOID = 3, - [Description("The number of rewards for this dungeon cannot be increased any further.")] - s_room_dungeon_reward_CantUseExtraReward = 5, - [Description("You do not have the {0} item.\nDo you want to purchase the item from the Meret Market?")] - s_dungeonRoom_lack_extra_ticket = 6, - [Description("You can no longer increase the number of rewards.")] - s_room_dungeon_max_reward_count = 7, - [Description("This dungeon has expired. Entry is no longer possible.")] - s_room_dungeon_expired = 8, - [Description("Preparing.")] - s_room_dungeon_commingSoon = 9, - [Description("You cannot enter a dungeon while searching for a dungeon.")] - s_room_dungeon_canEnterOnDungeonMatch = 10, - [Description("You cannot enter a dungeon during the tutorial.")] - s_room_dungeon_canEnterTutorialField = 11, - [Description("No entry allowed today.\\nPlease check the entry conditions in the dungeon information.")] - s_room_dungeon_canEnterDayOfWeeks = 12, - [Description("You are already in the hall.")] - s_room_dungeon_AlreadyChaosHall = 13, - [Description("You are already Within the Anomaly.")] - s_room_dungeon_AlreadyLapentaHall = 14, - [Description("You are already in the Queen's Parlor.")] - s_room_dungeon_AlreadyColosseumHall = 15, - [Description("You cannot move while inside a dungeon.")] - s_room_dungeon_CantEnterInDungeon = 16, - [Description("Up to {0} people are allowed.")] - s_room_dungeon_OverMaxUserCount = 17, - [Description("Requires at least {0} party members.")] - s_room_dungeon_UnderMinUserCount = 18, - [Description("Rewards can only be taken on days when entry is allowed.")] - s_room_dungeon_noRewardDayOfWeeks = 19, - [Description("You do not have the item needed for entry.")] - s_room_dungeon_HasNotLimitItem = 20, - [Description("Entry is not allowed at this time.\\nPlease check the entry conditions in the dungeon information.")] - s_room_dungeon_NotAllowTime = 21, - [Description("That cannot be used while inside a dungeon.")] - s_room_dungeon_CantUseAtDungeonRoom = 22, - [Description("You cannot enter at this time.")] - s_room_dungeon_notOpenTimeDungeon = 23, - [Description("It is too early to abandon the dungeon.")] - s_room_dungeon_cannot_giveup_yet = 24, - [Description("In order to enter, all party members must be from the same guild.")] - s_room_dungeon_require_guild_partry = 25, - [Description("New guild members cannot participate until the Guild Raid Score is reset.")] - s_room_dungeon_require_guild_join_date = 26, - [Description("This function is not currently available.")] - s_room_dungeon_shutdown_find_dungeon_helper = 27, - [Description("This dungeon is not available yet. \\nCheck the requirements in the Dungeon Info menu.")] - s_room_dungeon_is_not_open_period_date = 28, - [Description("This dungeon is not available yet. \\nCheck the requirements in the Dungeon Info menu.")] - s_room_dungeon_is_not_open_period = 29, - [Description("One or more party members can't enter.")] - s_room_dungeon_cant_enter_in_partymember = 30, - [Description("You've already reset the weekly clear count for a character on your account this week.\\nTry again after midnight on Friday.")] - s_room_dungeon_error_used_reset_united_reward = 31, - [Description("You have not yet reached the weekly clear count limit.")] - s_room_dungeon_error_still_have_united_reawrd = 32, - [Description("The weekly clear count reset feature is still in its beta phase and has been temporarily disabled.")] - s_room_dungeon_error_shutdown_united_reawrd_reset = 33, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum DungeonRoomError { + none = 0, + [Description("The party leader must enter first.")] + s_room_party_err_not_chief = 1, + [Description("Entry limit reached.")] + s_room_party_err_full_room = 2, + [Description("You do not have permission to enter.")] + s_room_dungeon_error_invalidPartyOID = 3, + [Description("The number of rewards for this dungeon cannot be increased any further.")] + s_room_dungeon_reward_CantUseExtraReward = 5, + [Description("You do not have the {0} item.\nDo you want to purchase the item from the Meret Market?")] + s_dungeonRoom_lack_extra_ticket = 6, + [Description("You can no longer increase the number of rewards.")] + s_room_dungeon_max_reward_count = 7, + [Description("This dungeon has expired. Entry is no longer possible.")] + s_room_dungeon_expired = 8, + [Description("Preparing.")] + s_room_dungeon_commingSoon = 9, + [Description("You cannot enter a dungeon while searching for a dungeon.")] + s_room_dungeon_canEnterOnDungeonMatch = 10, + [Description("You cannot enter a dungeon during the tutorial.")] + s_room_dungeon_canEnterTutorialField = 11, + [Description("No entry allowed today.\\nPlease check the entry conditions in the dungeon information.")] + s_room_dungeon_canEnterDayOfWeeks = 12, + [Description("You are already in the hall.")] + s_room_dungeon_AlreadyChaosHall = 13, + [Description("You are already Within the Anomaly.")] + s_room_dungeon_AlreadyLapentaHall = 14, + [Description("You are already in the Queen's Parlor.")] + s_room_dungeon_AlreadyColosseumHall = 15, + [Description("You cannot move while inside a dungeon.")] + s_room_dungeon_CantEnterInDungeon = 16, + [Description("Up to {0} people are allowed.")] + s_room_dungeon_OverMaxUserCount = 17, + [Description("Requires at least {0} party members.")] + s_room_dungeon_UnderMinUserCount = 18, + [Description("Rewards can only be taken on days when entry is allowed.")] + s_room_dungeon_noRewardDayOfWeeks = 19, + [Description("You do not have the item needed for entry.")] + s_room_dungeon_HasNotLimitItem = 20, + [Description("Entry is not allowed at this time.\\nPlease check the entry conditions in the dungeon information.")] + s_room_dungeon_NotAllowTime = 21, + [Description("That cannot be used while inside a dungeon.")] + s_room_dungeon_CantUseAtDungeonRoom = 22, + [Description("You cannot enter at this time.")] + s_room_dungeon_notOpenTimeDungeon = 23, + [Description("It is too early to abandon the dungeon.")] + s_room_dungeon_cannot_giveup_yet = 24, + [Description("In order to enter, all party members must be from the same guild.")] + s_room_dungeon_require_guild_partry = 25, + [Description("New guild members cannot participate until the Guild Raid Score is reset.")] + s_room_dungeon_require_guild_join_date = 26, + [Description("This function is not currently available.")] + s_room_dungeon_shutdown_find_dungeon_helper = 27, + [Description("This dungeon is not available yet. \\nCheck the requirements in the Dungeon Info menu.")] + s_room_dungeon_is_not_open_period_date = 28, + [Description("This dungeon is not available yet. \\nCheck the requirements in the Dungeon Info menu.")] + s_room_dungeon_is_not_open_period = 29, + [Description("One or more party members can't enter.")] + s_room_dungeon_cant_enter_in_partymember = 30, + [Description("You've already reset the weekly clear count for a character on your account this week.\\nTry again after midnight on Friday.")] + s_room_dungeon_error_used_reset_united_reward = 31, + [Description("You have not yet reached the weekly clear count limit.")] + s_room_dungeon_error_still_have_united_reawrd = 32, + [Description("The weekly clear count reset feature is still in its beta phase and has been temporarily disabled.")] + s_room_dungeon_error_shutdown_united_reawrd_reset = 33, +} diff --git a/Maple2.Model/Error/EmoteError.cs b/Maple2.Model/Error/EmoteError.cs index 30b394d02..36614538c 100644 --- a/Maple2.Model/Error/EmoteError.cs +++ b/Maple2.Model/Error/EmoteError.cs @@ -1,38 +1,38 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum EmoteError : byte { - [Description("System Error: Emote")] - s_dynamic_action_item_invalid = 0, - [Description("You have already learned this emote.")] - s_dynamic_action_already_learn = 1, -} - -public enum BuddyEmoteError : byte { - [Description("You can't use that buddy emote.")] - s_couple_emotion_failed_request_not_exist_skill = 0, - [Description("You must wait until your partner has finished their current emote.")] - s_couple_emotion_failed_request_already_recv = 1, - [Description("You must wait until your partner has finished their current emote.")] - s_couple_emotion_failed_request_already_in_action = 2, - [Description("{0} is not accepting buddy emote invites.")] - s_couple_emotion_failed_requset_auto_decline = 3, - [Description("Your partner has become busy with something else and cannot participate in the buddy emote.")] - s_couple_emotion_failed_accept_request_user_wrong_state = 4, - [Description("Your partner is busy with something else and can't participate in a buddy emote right now.")] - s_couple_emotion_failed_request_wrong_state_target_user = 5, - [Description("The character who invited you to participate in a buddy emote could not be found.")] - s_couple_emotion_failed_accept_cannot_find_request_user = 6, - [Description("{0} can't participate in a buddy emote from their current location.")] - s_couple_emotion_target_user_wrong_position = 7, - [Description("Your partner is too far away to participate in a buddy emote.")] - s_couple_emotion_failed_teleport_limit_distance = 8, - [Description("You cannot participate in a buddy emote on this map.")] - s_couple_emotion_cannot_request_in_this_map = 9, - - [Description("Unable to initiate buddy emote.")] - s_couple_emotion_failed = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum EmoteError : byte { + [Description("System Error: Emote")] + s_dynamic_action_item_invalid = 0, + [Description("You have already learned this emote.")] + s_dynamic_action_already_learn = 1, +} + +public enum BuddyEmoteError : byte { + [Description("You can't use that buddy emote.")] + s_couple_emotion_failed_request_not_exist_skill = 0, + [Description("You must wait until your partner has finished their current emote.")] + s_couple_emotion_failed_request_already_recv = 1, + [Description("You must wait until your partner has finished their current emote.")] + s_couple_emotion_failed_request_already_in_action = 2, + [Description("{0} is not accepting buddy emote invites.")] + s_couple_emotion_failed_requset_auto_decline = 3, + [Description("Your partner has become busy with something else and cannot participate in the buddy emote.")] + s_couple_emotion_failed_accept_request_user_wrong_state = 4, + [Description("Your partner is busy with something else and can't participate in a buddy emote right now.")] + s_couple_emotion_failed_request_wrong_state_target_user = 5, + [Description("The character who invited you to participate in a buddy emote could not be found.")] + s_couple_emotion_failed_accept_cannot_find_request_user = 6, + [Description("{0} can't participate in a buddy emote from their current location.")] + s_couple_emotion_target_user_wrong_position = 7, + [Description("Your partner is too far away to participate in a buddy emote.")] + s_couple_emotion_failed_teleport_limit_distance = 8, + [Description("You cannot participate in a buddy emote on this map.")] + s_couple_emotion_cannot_request_in_this_map = 9, + + [Description("Unable to initiate buddy emote.")] + s_couple_emotion_failed = byte.MaxValue, +} diff --git a/Maple2.Model/Error/EnchantScrollError.cs b/Maple2.Model/Error/EnchantScrollError.cs index c687317cb..afeacf719 100644 --- a/Maple2.Model/Error/EnchantScrollError.cs +++ b/Maple2.Model/Error/EnchantScrollError.cs @@ -1,26 +1,26 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum EnchantScrollError : short { - [Description("The enchantment was a success!")] - s_enchantscroll_ok = 0, - [Description("The selected items are no longer valid.")] - s_enchantscroll_invalid_scroll = 1, - [Description("That item is not eligible.")] - s_enchantscroll_invalid_item = 2, - [Description("Unstable items cannot be enchanted.")] - s_enchantscroll_breaking_item = 3, - [Description("The selected gear cannot be enchanted with this scroll due to its level.")] - s_enchantscroll_invalid_level = 4, - [Description("The selected gear cannot be enchanted with this scroll.")] - s_enchantscroll_invalid_slot = 5, - [Description("The selected gear cannot be enchanted with this scroll due to its grade.")] - s_enchantscroll_invalid_rank = 6, - [Description("Cannot be used because the enchantment grade of the selected gear is higher.")] - s_enchantscroll_invalid_grade = 7, - [Description("If the item is unstable, it cannot be restored.")] - s_enchantscroll_not_breaking_item = 8, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum EnchantScrollError : short { + [Description("The enchantment was a success!")] + s_enchantscroll_ok = 0, + [Description("The selected items are no longer valid.")] + s_enchantscroll_invalid_scroll = 1, + [Description("That item is not eligible.")] + s_enchantscroll_invalid_item = 2, + [Description("Unstable items cannot be enchanted.")] + s_enchantscroll_breaking_item = 3, + [Description("The selected gear cannot be enchanted with this scroll due to its level.")] + s_enchantscroll_invalid_level = 4, + [Description("The selected gear cannot be enchanted with this scroll.")] + s_enchantscroll_invalid_slot = 5, + [Description("The selected gear cannot be enchanted with this scroll due to its grade.")] + s_enchantscroll_invalid_rank = 6, + [Description("Cannot be used because the enchantment grade of the selected gear is higher.")] + s_enchantscroll_invalid_grade = 7, + [Description("If the item is unstable, it cannot be restored.")] + s_enchantscroll_not_breaking_item = 8, +} diff --git a/Maple2.Model/Error/FishingError.cs b/Maple2.Model/Error/FishingError.cs index a9f70c4cb..3931659a0 100644 --- a/Maple2.Model/Error/FishingError.cs +++ b/Maple2.Model/Error/FishingError.cs @@ -1,25 +1,25 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum FishingError : short { - none = 0, - [Description("You can only fish near swimmable water.")] - s_fishing_error_notexist_water = 1, - [Description("The fishing pole is not valid.")] - s_fishing_error_invalid_item = 2, - [Description("Your fishing mastery is too low to fish here.")] - s_fishing_error_lack_mastery = 3, - [Description("You cannot fish here.")] - s_fishing_error_notexist_fish = 5, - [Description("Your fishing mastery is too low to use this fishing pole.")] - s_fishing_error_fishingrod_mastery = 6, - [Description("You cannot fish because your Gear tab or Misc tab is full.")] - s_fishing_error_inventory_full = 7, - [Description("You cannot fish here.")] - s_fishing_error_ugcmap = 8, - [Description("System error")] - s_fishing_error_system_error = 9, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum FishingError : short { + none = 0, + [Description("You can only fish near swimmable water.")] + s_fishing_error_notexist_water = 1, + [Description("The fishing pole is not valid.")] + s_fishing_error_invalid_item = 2, + [Description("Your fishing mastery is too low to fish here.")] + s_fishing_error_lack_mastery = 3, + [Description("You cannot fish here.")] + s_fishing_error_notexist_fish = 5, + [Description("Your fishing mastery is too low to use this fishing pole.")] + s_fishing_error_fishingrod_mastery = 6, + [Description("You cannot fish because your Gear tab or Misc tab is full.")] + s_fishing_error_inventory_full = 7, + [Description("You cannot fish here.")] + s_fishing_error_ugcmap = 8, + [Description("System error")] + s_fishing_error_system_error = 9, +} diff --git a/Maple2.Model/Error/FunctionCubeError.cs b/Maple2.Model/Error/FunctionCubeError.cs index 468bd6a3c..c8985044a 100644 --- a/Maple2.Model/Error/FunctionCubeError.cs +++ b/Maple2.Model/Error/FunctionCubeError.cs @@ -1,15 +1,15 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum FunctionCubeError { - ok = 0, - [Description("This block has not been placed.")] - s_function_cube_error_invalid_cube = 1, // and 2 - [Description("Too far to operate.")] - s_function_cube_error_invalid_pos = 3, - [Description("Only characters currently in the indoor space can be summoned.")] - s_function_cube_error_invalid_summon_user = 4, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum FunctionCubeError { + ok = 0, + [Description("This block has not been placed.")] + s_function_cube_error_invalid_cube = 1, // and 2 + [Description("Too far to operate.")] + s_function_cube_error_invalid_pos = 3, + [Description("Only characters currently in the indoor space can be summoned.")] + s_function_cube_error_invalid_summon_user = 4, +} diff --git a/Maple2.Model/Error/GroupChatError.cs b/Maple2.Model/Error/GroupChatError.cs index 37392fb3b..4a2c5ff23 100644 --- a/Maple2.Model/Error/GroupChatError.cs +++ b/Maple2.Model/Error/GroupChatError.cs @@ -1,17 +1,17 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum GroupChatError { - none = 0, - [Description("You cannot invite that player to the group chat.")] - s_err_groupchat_null_target_user = 3, - [Description("You cannot invite that player to the group chat.")] - s_err_groupchat_add_member_target = 8, - [Description("{0} is already participating in 3 group chats.")] - s_err_groupchat_maxgroup = 10, - [Description("\"{0}\" contains inappropriate words.\\nPlease enter another name.")] - s_change_charname_err_bad_words = 13, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum GroupChatError { + none = 0, + [Description("You cannot invite that player to the group chat.")] + s_err_groupchat_null_target_user = 3, + [Description("You cannot invite that player to the group chat.")] + s_err_groupchat_add_member_target = 8, + [Description("{0} is already participating in 3 group chats.")] + s_err_groupchat_maxgroup = 10, + [Description("\"{0}\" contains inappropriate words.\\nPlease enter another name.")] + s_change_charname_err_bad_words = 13, +} diff --git a/Maple2.Model/Error/GuildError.cs b/Maple2.Model/Error/GuildError.cs index 09670066e..363605854 100644 --- a/Maple2.Model/Error/GuildError.cs +++ b/Maple2.Model/Error/GuildError.cs @@ -1,98 +1,98 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum GuildError : byte { - none = 0, - [Description("Unknown Guild Error")] - s_guild_err_unknown = 1, - [Description("Guild not found.")] - s_guild_err_null_guild = 3, - [Description("This character is already a member of a guild.")] - s_guild_err_already_exist = 4, - [Description("Unable to send invite.")] - s_guild_err_wait_inviting = 5, - [Description("Guild invitation failed.")] - s_guild_err_blocked = 6, - [Description("They have already joined another guild.")] - s_guild_err_has_guild = 7, - [Description("The guild is no longer valid.")] - s_guild_err_invalid_guild = 8, - [Description("Unable to invite the player to your guild.")] - s_guild_err_null_user = 10, - [Description("A guild with the same name already exists.")] - s_guild_err_name_exist = 11, - [Description("Contains a forbidden word.")] - s_guild_err_name_value = 12, - [Description("Guild member not found.")] - s_guild_err_null_member = 13, - [Description("The guild cannot be disbanded if there are any guild members.")] - s_guild_err_exist_member = 14, - [Description("You have reached the maximum number of guild members.")] - s_guild_err_full_member = 15, - [Description("This guild member has not joined.")] - s_guild_err_not_join_member = 16, - [Description("The guild leader cannot leave the guild.")] - s_guild_err_cannot_leave_master = 17, - [Description("You cannot kick the guild leader.")] - s_guild_err_expel_target_master = 18, - [Description("To create a guild, you must be above level {0}.")] - s_guild_err_not_enough_level = 19, - [Description("Not enough mesos.")] - s_guild_err_no_money = 20, - [Description("You don't have permission to do that.")] - s_guild_err_no_authority = 21, - [Description("Only the guild leader can do that.")] - s_guild_err_no_master = 22, - [Description("This rank cannot be used.")] - s_guild_err_invalid_grade_range = 23, - [Description("You cannot change the maximum amount of guild members to this value.")] - s_guild_err_invalid_capacity_range = 24, - [Description("This rank cannot be used.")] - s_guild_err_invalid_grade_data = 25, - [Description("Incorrect rank.")] - s_guild_err_invalid_grade_index = 26, - [Description("This rank cannot be granted.")] - s_guild_err_exist_empty_grade_index = 27, - [Description("Rank setting failed.")] - s_guild_err_set_grade_failed = 28, - [Description("Guild member invitation failed.")] - s_guild_err_fail_addmember = 30, - [Description("This character was not invited.")] - s_guild_err_null_invite_member = 32, - [Description("Cannot be done during a guild battle.")] - s_guild_err_cant_during_pvp = 33, - [Description("The guild member maximum cannot be changed.")] - s_guild_extend_capacity_err_cannot = 34, - [Description("A problem occurred while changing the maximum amount of guild members.")] - s_guild_extend_capacity_err_current = 35, - [Description("Preferences have remained the same.")] - s_guild_search_same_propensity = 36, - [Description("You can submit up to 10.")] - s_guild_search_max_join_request = 37, - [Description("Please wait a moment.")] - s_guild_search_last_request = 38, - [Description("Application not found.")] - s_guild_search_null_join_guild_request = 39, - [Description("The target is in a location where they cannot be invited.")] - s_guild_err_fail_this_field = 41, - [Description("The guild level is not high enough.")] - s_guild_err_not_enough_guild_level = 42, - [Description("Not enough guild funds.")] - s_guild_err_not_enough_guild_fund = 43, - [Description("You cannot use the guild skill right now.")] - s_guild_err_cannot_use_skill = 44, - [Description("Please try again later.")] - s_guild_err_too_fast_to_search_by_name = 45, - [Description("You have already arrived at the Glorious Arena.")] - s_guild_pvp_already_pvp_field = 46, - [Description("Applications are not currently being accepted.")] - s_guild_err_isNotVsGameTime = 47, - [Description("You need at least {0} players online.")] - s_guild_err_requireOnlineUserCount = 48, - - [Description("Undefined Guild Error")] - s_guild_err_none = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum GuildError : byte { + none = 0, + [Description("Unknown Guild Error")] + s_guild_err_unknown = 1, + [Description("Guild not found.")] + s_guild_err_null_guild = 3, + [Description("This character is already a member of a guild.")] + s_guild_err_already_exist = 4, + [Description("Unable to send invite.")] + s_guild_err_wait_inviting = 5, + [Description("Guild invitation failed.")] + s_guild_err_blocked = 6, + [Description("They have already joined another guild.")] + s_guild_err_has_guild = 7, + [Description("The guild is no longer valid.")] + s_guild_err_invalid_guild = 8, + [Description("Unable to invite the player to your guild.")] + s_guild_err_null_user = 10, + [Description("A guild with the same name already exists.")] + s_guild_err_name_exist = 11, + [Description("Contains a forbidden word.")] + s_guild_err_name_value = 12, + [Description("Guild member not found.")] + s_guild_err_null_member = 13, + [Description("The guild cannot be disbanded if there are any guild members.")] + s_guild_err_exist_member = 14, + [Description("You have reached the maximum number of guild members.")] + s_guild_err_full_member = 15, + [Description("This guild member has not joined.")] + s_guild_err_not_join_member = 16, + [Description("The guild leader cannot leave the guild.")] + s_guild_err_cannot_leave_master = 17, + [Description("You cannot kick the guild leader.")] + s_guild_err_expel_target_master = 18, + [Description("To create a guild, you must be above level {0}.")] + s_guild_err_not_enough_level = 19, + [Description("Not enough mesos.")] + s_guild_err_no_money = 20, + [Description("You don't have permission to do that.")] + s_guild_err_no_authority = 21, + [Description("Only the guild leader can do that.")] + s_guild_err_no_master = 22, + [Description("This rank cannot be used.")] + s_guild_err_invalid_grade_range = 23, + [Description("You cannot change the maximum amount of guild members to this value.")] + s_guild_err_invalid_capacity_range = 24, + [Description("This rank cannot be used.")] + s_guild_err_invalid_grade_data = 25, + [Description("Incorrect rank.")] + s_guild_err_invalid_grade_index = 26, + [Description("This rank cannot be granted.")] + s_guild_err_exist_empty_grade_index = 27, + [Description("Rank setting failed.")] + s_guild_err_set_grade_failed = 28, + [Description("Guild member invitation failed.")] + s_guild_err_fail_addmember = 30, + [Description("This character was not invited.")] + s_guild_err_null_invite_member = 32, + [Description("Cannot be done during a guild battle.")] + s_guild_err_cant_during_pvp = 33, + [Description("The guild member maximum cannot be changed.")] + s_guild_extend_capacity_err_cannot = 34, + [Description("A problem occurred while changing the maximum amount of guild members.")] + s_guild_extend_capacity_err_current = 35, + [Description("Preferences have remained the same.")] + s_guild_search_same_propensity = 36, + [Description("You can submit up to 10.")] + s_guild_search_max_join_request = 37, + [Description("Please wait a moment.")] + s_guild_search_last_request = 38, + [Description("Application not found.")] + s_guild_search_null_join_guild_request = 39, + [Description("The target is in a location where they cannot be invited.")] + s_guild_err_fail_this_field = 41, + [Description("The guild level is not high enough.")] + s_guild_err_not_enough_guild_level = 42, + [Description("Not enough guild funds.")] + s_guild_err_not_enough_guild_fund = 43, + [Description("You cannot use the guild skill right now.")] + s_guild_err_cannot_use_skill = 44, + [Description("Please try again later.")] + s_guild_err_too_fast_to_search_by_name = 45, + [Description("You have already arrived at the Glorious Arena.")] + s_guild_pvp_already_pvp_field = 46, + [Description("Applications are not currently being accepted.")] + s_guild_err_isNotVsGameTime = 47, + [Description("You need at least {0} players online.")] + s_guild_err_requireOnlineUserCount = 48, + + [Description("Undefined Guild Error")] + s_guild_err_none = byte.MaxValue, +} diff --git a/Maple2.Model/Error/ItemBoxError.cs b/Maple2.Model/Error/ItemBoxError.cs index ebe302342..cbd0170ee 100644 --- a/Maple2.Model/Error/ItemBoxError.cs +++ b/Maple2.Model/Error/ItemBoxError.cs @@ -1,13 +1,13 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum ItemBoxError : short { - ok = 2, - [Description("Unable to open boxes.")] - s_err_cannot_open_multi_itembox_inventory_fail = 3, - [Description("No additional containers can be opened, as your inventory is full. Please make space in your inventory.")] - s_err_cannot_open_multi_itembox_inventory = 4, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum ItemBoxError : short { + ok = 2, + [Description("Unable to open boxes.")] + s_err_cannot_open_multi_itembox_inventory_fail = 3, + [Description("No additional containers can be opened, as your inventory is full. Please make space in your inventory.")] + s_err_cannot_open_multi_itembox_inventory = 4, +} diff --git a/Maple2.Model/Error/ItemEnchantError.cs b/Maple2.Model/Error/ItemEnchantError.cs index cf3f53d9a..f54ce818d 100644 --- a/Maple2.Model/Error/ItemEnchantError.cs +++ b/Maple2.Model/Error/ItemEnchantError.cs @@ -1,20 +1,20 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum ItemEnchantError : short { - [Description("System Error: Item. code = {0}")] - s_itemenchant_unknown_err = 0, - [Description("This item cannot be enchanted.")] - s_itemenchant_invalid_item = 1, - [Description("The item is unstable.")] - s_itemenchant_damaged_item = 2, - [Description("Not enough materials.")] - s_itemenchant_lack_ingredient = 3, - not_enough_fodder = 4, - excess_fodder = 5, - max_fodder = 6, - over_100_success_rate = 7, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum ItemEnchantError : short { + [Description("System Error: Item. code = {0}")] + s_itemenchant_unknown_err = 0, + [Description("This item cannot be enchanted.")] + s_itemenchant_invalid_item = 1, + [Description("The item is unstable.")] + s_itemenchant_damaged_item = 2, + [Description("Not enough materials.")] + s_itemenchant_lack_ingredient = 3, + not_enough_fodder = 4, + excess_fodder = 5, + max_fodder = 6, + over_100_success_rate = 7, +} diff --git a/Maple2.Model/Error/ItemExchangeScrollError.cs b/Maple2.Model/Error/ItemExchangeScrollError.cs index 905680c1f..dbbf7ae8f 100644 --- a/Maple2.Model/Error/ItemExchangeScrollError.cs +++ b/Maple2.Model/Error/ItemExchangeScrollError.cs @@ -1,26 +1,26 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum ItemExchangeScrollError : short { - [Description("Fusion successful.")] - s_itemslot_exchange_ok = 0, - [Description("The selected items are no longer valid.")] - s_itemslot_exchange_scroll_invalid = 1, - [Description("This item cannot be fused.")] - s_itemslot_exchange_upgrade_invalid = 2, - [Description("Not enough mesos.")] - s_itemslot_exchange_money_invalid = 3, - [Description("You do not have enough items.")] - s_itemslot_exchange_count_invalid = 4, - [Description("The enchant level for this equipment is too high to fuse.")] - s_itemslot_exchange_grade_invalid = 5, - [Description("This item is locked.")] - s_itemslot_exchange_lockstate_item = 6, - [Description("Please double-check the number of fusions.")] - s_itemslot_exchange_count_check = 7, - [Description("System error.")] - s_itemslot_exchange_unknown = 8, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum ItemExchangeScrollError : short { + [Description("Fusion successful.")] + s_itemslot_exchange_ok = 0, + [Description("The selected items are no longer valid.")] + s_itemslot_exchange_scroll_invalid = 1, + [Description("This item cannot be fused.")] + s_itemslot_exchange_upgrade_invalid = 2, + [Description("Not enough mesos.")] + s_itemslot_exchange_money_invalid = 3, + [Description("You do not have enough items.")] + s_itemslot_exchange_count_invalid = 4, + [Description("The enchant level for this equipment is too high to fuse.")] + s_itemslot_exchange_grade_invalid = 5, + [Description("This item is locked.")] + s_itemslot_exchange_lockstate_item = 6, + [Description("Please double-check the number of fusions.")] + s_itemslot_exchange_count_check = 7, + [Description("System error.")] + s_itemslot_exchange_unknown = 8, +} diff --git a/Maple2.Model/Error/ItemInventoryError.cs b/Maple2.Model/Error/ItemInventoryError.cs index adf80880c..2f1821fbc 100644 --- a/Maple2.Model/Error/ItemInventoryError.cs +++ b/Maple2.Model/Error/ItemInventoryError.cs @@ -1,30 +1,30 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum ItemInventoryError : int { - [Description("The item amounts do not match.")] - s_item_err_invalid_count = 12, - [Description("Your inventory is full.")] - s_err_inventory = 13, - [Description("You cannot discard that pet, as there are items in its bag.")] - s_err_cannot_destroy_petitem_hasitem = 31, - [Description("Not enough merets.")] - s_cannot_charge_merat = 34, - [Description("A character's item can only be worn by that character.")] - s_item_err_puton_invalid_binding = 35, - [Description("A character's item can only be used by that character.")] - s_item_err_use_invalid_binding = 36, - [Description("This slot cannot be used.")] - s_item_err_Invalid_slot = 37, // This may not work - [Description("The tab you have selected in is inactive.")] - s_item_err_not_active_tab = 38, - [Description("You must have a character that's Lv. {1} or higher to drop {0}.")] - s_itemdrop_error_restricted_by_user_level_ex = 40, - [Description("You must have a character that's Lv. {1} or higher to pick up {0}.")] - s_itempickup_error_restricted_by_user_level_ex = 41, - [Description("This item cannot be discarded.")] - s_item_err_drop = 42, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum ItemInventoryError : int { + [Description("The item amounts do not match.")] + s_item_err_invalid_count = 12, + [Description("Your inventory is full.")] + s_err_inventory = 13, + [Description("You cannot discard that pet, as there are items in its bag.")] + s_err_cannot_destroy_petitem_hasitem = 31, + [Description("Not enough merets.")] + s_cannot_charge_merat = 34, + [Description("A character's item can only be worn by that character.")] + s_item_err_puton_invalid_binding = 35, + [Description("A character's item can only be used by that character.")] + s_item_err_use_invalid_binding = 36, + [Description("This slot cannot be used.")] + s_item_err_Invalid_slot = 37, // This may not work + [Description("The tab you have selected in is inactive.")] + s_item_err_not_active_tab = 38, + [Description("You must have a character that's Lv. {1} or higher to drop {0}.")] + s_itemdrop_error_restricted_by_user_level_ex = 40, + [Description("You must have a character that's Lv. {1} or higher to pick up {0}.")] + s_itempickup_error_restricted_by_user_level_ex = 41, + [Description("This item cannot be discarded.")] + s_item_err_drop = 42, +} diff --git a/Maple2.Model/Error/ItemMergeError.cs b/Maple2.Model/Error/ItemMergeError.cs index 0841c9c25..69ce7948e 100644 --- a/Maple2.Model/Error/ItemMergeError.cs +++ b/Maple2.Model/Error/ItemMergeError.cs @@ -1,23 +1,23 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum ItemMergeError : short { - ok = 0, - [Description("Not enough materials.")] - s_item_merge_option_error_lack_material = 1, - [Description("Not enough mesos.")] - s_item_merge_option_error_lack_meso = 2, - [Description("This empowerment item is no longer valid.")] - s_item_merge_option_error_invalid_mergeitem = 3, - [Description("Invalid material item.")] - s_item_merge_option_error_invalid_material = 4, - [Description("That empowerment crystal is no longer valid.")] - s_item_merge_option_error_invalid_merge_scroll = 5, - [Description("You can only remove the attributes of an item that is the same tier as the empowerment crystal.")] - s_item_merge_option_error_invalid_revert_rank = 6, - [Description("No attributes to remove.")] - s_item_merge_option_error_invalid_revert_option = 7, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum ItemMergeError : short { + ok = 0, + [Description("Not enough materials.")] + s_item_merge_option_error_lack_material = 1, + [Description("Not enough mesos.")] + s_item_merge_option_error_lack_meso = 2, + [Description("This empowerment item is no longer valid.")] + s_item_merge_option_error_invalid_mergeitem = 3, + [Description("Invalid material item.")] + s_item_merge_option_error_invalid_material = 4, + [Description("That empowerment crystal is no longer valid.")] + s_item_merge_option_error_invalid_merge_scroll = 5, + [Description("You can only remove the attributes of an item that is the same tier as the empowerment crystal.")] + s_item_merge_option_error_invalid_revert_rank = 6, + [Description("No attributes to remove.")] + s_item_merge_option_error_invalid_revert_option = 7, +} diff --git a/Maple2.Model/Error/ItemRepackError.cs b/Maple2.Model/Error/ItemRepackError.cs index 603105fce..8e6cb7ff0 100644 --- a/Maple2.Model/Error/ItemRepackError.cs +++ b/Maple2.Model/Error/ItemRepackError.cs @@ -1,23 +1,23 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum ItemRepackError { - ok = 0, - [Description("This item cannot be packaged.")] - s_item_repacking_scroll_error_invalid_target = 1, - [Description("This item is no longer valid.")] - s_item_repacking_scroll_error_invalid_target_data = 2, - [Description("You cannot package right now.")] - s_item_repacking_scroll_error_impossible_slot = 3, - [Description("Packaging not allowed at this rank.")] - s_item_repacking_scroll_error_impossible_rank = 4, - [Description("Packaging not allowed at this level.")] - s_item_repacking_scroll_error_impossible_level = 5, - [Description("This item is no longer valid.")] - s_item_repacking_scroll_error_invalid_scroll = 7, - [Description("Item packaging failed.")] - s_item_repacking_scroll_error_server_fail_consume_scroll = 12, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum ItemRepackError { + ok = 0, + [Description("This item cannot be packaged.")] + s_item_repacking_scroll_error_invalid_target = 1, + [Description("This item is no longer valid.")] + s_item_repacking_scroll_error_invalid_target_data = 2, + [Description("You cannot package right now.")] + s_item_repacking_scroll_error_impossible_slot = 3, + [Description("Packaging not allowed at this rank.")] + s_item_repacking_scroll_error_impossible_rank = 4, + [Description("Packaging not allowed at this level.")] + s_item_repacking_scroll_error_impossible_level = 5, + [Description("This item is no longer valid.")] + s_item_repacking_scroll_error_invalid_scroll = 7, + [Description("Item packaging failed.")] + s_item_repacking_scroll_error_server_fail_consume_scroll = 12, +} diff --git a/Maple2.Model/Error/ItemSocketError.cs b/Maple2.Model/Error/ItemSocketError.cs index fa11b9bef..833fcac7e 100644 --- a/Maple2.Model/Error/ItemSocketError.cs +++ b/Maple2.Model/Error/ItemSocketError.cs @@ -1,36 +1,36 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum ItemSocketError { - [Description("The selected target is not in your inventory.")] - s_itemsocketsystem_error_invalid_target = 1, - [Description("The selected item is not in your inventory.")] - s_itemsocketsystem_error_invalid_target_gemstone = 2, - [Description("This item cannot be used as material.")] - s_itemsocketsystem_error_invalid_target_ingredient = 3, - [Description("Confirm the number of items you're using as a catalyst.")] - s_itemsocketsystem_error_invalid_target_ingredient_count = 4, - [Description("A socket system error has occurred.\nPlease close the window and try again.\nCode = {0}, {1}")] - s_itemsocketsystem_error_server_default_msgbox = 5, // 5 to 12 - [Description("This socket is locked.")] - s_itemsocketsystem_error_socket_lock = 15, - [Description("This socket already has a gemstone.")] - s_itemsocketsystem_error_socket_used = 16, - [Description("This socket is empty.")] - s_itemsocketsystem_error_socket_empty = 17, - [Description("Sockets cannot be extended further.")] - s_itemsocketsystem_error_socket_unlock_all = 18, - [Description("Cannot be upgraded further.")] - s_itemsocketsystem_error_gemstone_maxlevel = 20, - [Description("Not enough materials.")] - s_itemsocketsystem_error_lack_price = 21, - // 22 = NOP - [Description("")] - s_itemsocketsystem_error_bind_owner = 23, - - [Description("Socket system error. Code: {0}, {1}")] - s_itemsocketsystem_error_server_default = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum ItemSocketError { + [Description("The selected target is not in your inventory.")] + s_itemsocketsystem_error_invalid_target = 1, + [Description("The selected item is not in your inventory.")] + s_itemsocketsystem_error_invalid_target_gemstone = 2, + [Description("This item cannot be used as material.")] + s_itemsocketsystem_error_invalid_target_ingredient = 3, + [Description("Confirm the number of items you're using as a catalyst.")] + s_itemsocketsystem_error_invalid_target_ingredient_count = 4, + [Description("A socket system error has occurred.\nPlease close the window and try again.\nCode = {0}, {1}")] + s_itemsocketsystem_error_server_default_msgbox = 5, // 5 to 12 + [Description("This socket is locked.")] + s_itemsocketsystem_error_socket_lock = 15, + [Description("This socket already has a gemstone.")] + s_itemsocketsystem_error_socket_used = 16, + [Description("This socket is empty.")] + s_itemsocketsystem_error_socket_empty = 17, + [Description("Sockets cannot be extended further.")] + s_itemsocketsystem_error_socket_unlock_all = 18, + [Description("Cannot be upgraded further.")] + s_itemsocketsystem_error_gemstone_maxlevel = 20, + [Description("Not enough materials.")] + s_itemsocketsystem_error_lack_price = 21, + // 22 = NOP + [Description("")] + s_itemsocketsystem_error_bind_owner = 23, + + [Description("Socket system error. Code: {0}, {1}")] + s_itemsocketsystem_error_server_default = byte.MaxValue, +} diff --git a/Maple2.Model/Error/ItemSocketScrollError.cs b/Maple2.Model/Error/ItemSocketScrollError.cs index ea92190eb..ffd951309 100644 --- a/Maple2.Model/Error/ItemSocketScrollError.cs +++ b/Maple2.Model/Error/ItemSocketScrollError.cs @@ -1,34 +1,34 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum ItemSocketScrollError { - none = 0, - [Description("This item is not eligible.")] - s_itemsocket_scroll_error_invalid_target = 1, - [Description("The selected items are no longer valid.")] - s_itemsocket_scroll_error_invalid_scroll = 2, - [Description("The selected item cannot use this scroll.")] - s_itemsocket_scroll_error_invalid_disable = 3, - [Description("This item cannot have any more sockets.")] - s_itemsocket_scroll_error_socket_unlock_all = 4, - [Description("The selected item cannot use this scroll.")] - s_itemsocket_scroll_error_impossible_slot = 5, - [Description("This scroll cannot be used on the selected item due to its quality.")] - s_itemsocket_scroll_error_impossible_rank = 6, - [Description("The selected item cannot use this scroll due to its level.")] - s_itemsocket_scroll_error_impossible_level = 7, - [Description("The selected item cannot use this scroll.")] - s_itemsocket_scroll_error_impossible_usepart = 8, - [Description("Failed to use scroll.")] - s_itemsocket_scroll_error_server_fail_consume_scroll = 9, - [Description("Socket activation failed.")] - s_itemsocket_scroll_error_server_fail_unlock_socket = 10, - [Description("This item already has as many active sockets as the scroll can add.")] - s_itemsocket_scroll_error_already_socket_unlock = 11, - - [Description("Socket scroll error. Code: {0}, {1}")] - s_itemsocket_scroll_error_server_default = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum ItemSocketScrollError { + none = 0, + [Description("This item is not eligible.")] + s_itemsocket_scroll_error_invalid_target = 1, + [Description("The selected items are no longer valid.")] + s_itemsocket_scroll_error_invalid_scroll = 2, + [Description("The selected item cannot use this scroll.")] + s_itemsocket_scroll_error_invalid_disable = 3, + [Description("This item cannot have any more sockets.")] + s_itemsocket_scroll_error_socket_unlock_all = 4, + [Description("The selected item cannot use this scroll.")] + s_itemsocket_scroll_error_impossible_slot = 5, + [Description("This scroll cannot be used on the selected item due to its quality.")] + s_itemsocket_scroll_error_impossible_rank = 6, + [Description("The selected item cannot use this scroll due to its level.")] + s_itemsocket_scroll_error_impossible_level = 7, + [Description("The selected item cannot use this scroll.")] + s_itemsocket_scroll_error_impossible_usepart = 8, + [Description("Failed to use scroll.")] + s_itemsocket_scroll_error_server_fail_consume_scroll = 9, + [Description("Socket activation failed.")] + s_itemsocket_scroll_error_server_fail_unlock_socket = 10, + [Description("This item already has as many active sockets as the scroll can add.")] + s_itemsocket_scroll_error_already_socket_unlock = 11, + + [Description("Socket scroll error. Code: {0}, {1}")] + s_itemsocket_scroll_error_server_default = byte.MaxValue, +} diff --git a/Maple2.Model/Error/JobError.cs b/Maple2.Model/Error/JobError.cs index 5c056bd47..c6a68f9ee 100644 --- a/Maple2.Model/Error/JobError.cs +++ b/Maple2.Model/Error/JobError.cs @@ -1,31 +1,31 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum JobError : byte { - [Description("Job advancement is only possible as a beginner.")] - s_err_job_bad_job = 2, - [Description("You have not completed the quest.")] - s_err_job_not_complete_quest = 3, // 4 - [Description("")] - s_err_job_privilege = 5, - [Description("You do not have enough mesos to raise your job rank.")] - s_err_job_not_enough_meso = 8, - [Description("Your level is not high enough to raise your job rank.")] - s_err_job_not_enough_level = 10, - [Description("")] - s_err_job_no_home = 11, - [Description("You do not need treatment when healthy.")] - s_err_job_no_penalty = 12, - [Description("You must join a guild.")] - s_err_job_guild = 14, - [Description("Chat conditions do not apply.")] - s_err_job_dayofweek = 15, - [Description("Empty {0} spaces in your inventory.")] - s_err_job_inventory_full = 18, - - [Description("Cannot be used due to conditions.")] - s_err_job_unknown = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum JobError : byte { + [Description("Job advancement is only possible as a beginner.")] + s_err_job_bad_job = 2, + [Description("You have not completed the quest.")] + s_err_job_not_complete_quest = 3, // 4 + [Description("")] + s_err_job_privilege = 5, + [Description("You do not have enough mesos to raise your job rank.")] + s_err_job_not_enough_meso = 8, + [Description("Your level is not high enough to raise your job rank.")] + s_err_job_not_enough_level = 10, + [Description("")] + s_err_job_no_home = 11, + [Description("You do not need treatment when healthy.")] + s_err_job_no_penalty = 12, + [Description("You must join a guild.")] + s_err_job_guild = 14, + [Description("Chat conditions do not apply.")] + s_err_job_dayofweek = 15, + [Description("Empty {0} spaces in your inventory.")] + s_err_job_inventory_full = 18, + + [Description("Cannot be used due to conditions.")] + s_err_job_unknown = byte.MaxValue, +} diff --git a/Maple2.Model/Error/LimitBreakError.cs b/Maple2.Model/Error/LimitBreakError.cs index 70366e862..0eb5e5d58 100644 --- a/Maple2.Model/Error/LimitBreakError.cs +++ b/Maple2.Model/Error/LimitBreakError.cs @@ -1,11 +1,11 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -namespace Maple2.Model.Error; - -public enum LimitBreakError : short { - none = 0, - s_unlimited_enchant_err_invalid_item = 1, - s_unlimited_enchant_err_lack_ingredient = 2, - s_unlimited_enchant_err_lack_meso = 3, - s_unlimited_enchant_err_max_unlimited_grade = 5, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +namespace Maple2.Model.Error; + +public enum LimitBreakError : short { + none = 0, + s_unlimited_enchant_err_invalid_item = 1, + s_unlimited_enchant_err_lack_ingredient = 2, + s_unlimited_enchant_err_lack_meso = 3, + s_unlimited_enchant_err_max_unlimited_grade = 5, +} diff --git a/Maple2.Model/Error/MailError.cs b/Maple2.Model/Error/MailError.cs index 35bfd920a..81dd9b4e5 100644 --- a/Maple2.Model/Error/MailError.cs +++ b/Maple2.Model/Error/MailError.cs @@ -1,55 +1,55 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum MailError : byte { - none = 0, - [Description("Character not found.")] - s_mail_error_username = 1, - [Description("The item amounts do not match.")] - s_mail_error_attachcount = 2, // also 5 - [Description("This item cannot be sent.")] - s_mail_error_cannot_attach_item = 3, // also 4 - [Description("Mail has not been sent.")] - s_mail_error_sendmail = 12, - [Description("This mail has been read already.")] - s_mail_error_alreadyread = 16, - [Description("The attached item on this mail has already been retrieved.")] - s_mail_error_already_receive = 17, // also 21 - [Description("The item cannot be retrieved because your inventory is full.")] - s_mail_error_receiveitem_to_inven = 20, - [Description("The item cannot be retrieved because the mail has expired.")] - s_mail_error_receive_expired = 21, - [Description("The item amounts do not match.")] - s_mail_error_attachcount_effect18 = 22, // + effect(18) - [Description("The sale has ended.")] - s_mail_error_ad_expired = 23, - [Description("Mail creation failed.")] - s_mail_error_createmail = 24, - [Description("The sender and recipient are the same player.")] - s_mail_error_recipient_equal_sender = 25, - [Description("Not enough mesos.")] - s_err_lack_meso = 26, - [Description("The mail recipient has been blocked. Please unblock and try again.")] - s_mail_error_block_from_me = 27, - [Description("The mail recipient has blocked you. You will not be able to send them mail.")] - s_mail_error_block_from_other = 28, - [Description("Mail cannot be sent.")] - s_mail_error_admin_character = 29, - [Description("GMs cannot send mail directly to players. Please use the proper GM tool.")] - s_mail_error_from_admin_to_user = 30, - [Description("The title and/or text contains a forbidden word.")] - s_mail_error_bancheck = 31, - [Description("Your mail privileges have been suspended.")] - s_mail_error_admin_block = 32, - [Description("The game will not run due to fatigue time.")] - s_anti_addiction_cannot_receive = 33, - - // Custom Error Codes - mail_not_found = 44, - - [Description("System Error: Mail. p = {0}, code = {1}")] - s_mail_error = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum MailError : byte { + none = 0, + [Description("Character not found.")] + s_mail_error_username = 1, + [Description("The item amounts do not match.")] + s_mail_error_attachcount = 2, // also 5 + [Description("This item cannot be sent.")] + s_mail_error_cannot_attach_item = 3, // also 4 + [Description("Mail has not been sent.")] + s_mail_error_sendmail = 12, + [Description("This mail has been read already.")] + s_mail_error_alreadyread = 16, + [Description("The attached item on this mail has already been retrieved.")] + s_mail_error_already_receive = 17, // also 21 + [Description("The item cannot be retrieved because your inventory is full.")] + s_mail_error_receiveitem_to_inven = 20, + [Description("The item cannot be retrieved because the mail has expired.")] + s_mail_error_receive_expired = 21, + [Description("The item amounts do not match.")] + s_mail_error_attachcount_effect18 = 22, // + effect(18) + [Description("The sale has ended.")] + s_mail_error_ad_expired = 23, + [Description("Mail creation failed.")] + s_mail_error_createmail = 24, + [Description("The sender and recipient are the same player.")] + s_mail_error_recipient_equal_sender = 25, + [Description("Not enough mesos.")] + s_err_lack_meso = 26, + [Description("The mail recipient has been blocked. Please unblock and try again.")] + s_mail_error_block_from_me = 27, + [Description("The mail recipient has blocked you. You will not be able to send them mail.")] + s_mail_error_block_from_other = 28, + [Description("Mail cannot be sent.")] + s_mail_error_admin_character = 29, + [Description("GMs cannot send mail directly to players. Please use the proper GM tool.")] + s_mail_error_from_admin_to_user = 30, + [Description("The title and/or text contains a forbidden word.")] + s_mail_error_bancheck = 31, + [Description("Your mail privileges have been suspended.")] + s_mail_error_admin_block = 32, + [Description("The game will not run due to fatigue time.")] + s_anti_addiction_cannot_receive = 33, + + // Custom Error Codes + mail_not_found = 44, + + [Description("System Error: Mail. p = {0}, code = {1}")] + s_mail_error = byte.MaxValue, +} diff --git a/Maple2.Model/Error/MapleopolyError.cs b/Maple2.Model/Error/MapleopolyError.cs index daacb289d..ad3a209cd 100644 --- a/Maple2.Model/Error/MapleopolyError.cs +++ b/Maple2.Model/Error/MapleopolyError.cs @@ -1,20 +1,20 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum MapleopolyError : byte { - [Description("None")] - ok = 0, - [Description("Not enough $itemPlural:{0}$.")] - s_bluemarble_result_consume_fail = 1, - [Description("You failed to escape.")] - s_bluemarble_result_trap_escape_fail = 2, - [Description("You escaped.")] - s_bluemarble_result_trap_escape_success = 3, - [Description("You have already rolled the dice.")] - s_bluemarble_result_dice_complete = 4, - [Description("You cannot roll the dice right now.")] - s_bluemarble_result_dice_not_complete = 5, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum MapleopolyError : byte { + [Description("None")] + ok = 0, + [Description("Not enough $itemPlural:{0}$.")] + s_bluemarble_result_consume_fail = 1, + [Description("You failed to escape.")] + s_bluemarble_result_trap_escape_fail = 2, + [Description("You escaped.")] + s_bluemarble_result_trap_escape_success = 3, + [Description("You have already rolled the dice.")] + s_bluemarble_result_dice_complete = 4, + [Description("You cannot roll the dice right now.")] + s_bluemarble_result_dice_not_complete = 5, +} diff --git a/Maple2.Model/Error/MasteryError.cs b/Maple2.Model/Error/MasteryError.cs index 35799d415..207eac12e 100644 --- a/Maple2.Model/Error/MasteryError.cs +++ b/Maple2.Model/Error/MasteryError.cs @@ -1,22 +1,22 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum MasteryError : short { - [Description("Not enough mastery.")] - s_mastery_error_lack_mastery = 1, - [Description("Not enough mesos.")] - s_mastery_error_lack_meso = 2, - [Description("You have not completed the required quest.")] - s_mastery_error_lack_quest = 3, - [Description("Not enough items.")] - s_mastery_error_lack_item = 4, - [Description("System error")] - s_mastery_error_unknown = 5, - [Description("Insufficient level.")] - s_mastery_error_invalid_level = 7, - [Description("The game will not run due to fatigue time.")] - s_anti_addiction_cannot_receive = 12, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum MasteryError : short { + [Description("Not enough mastery.")] + s_mastery_error_lack_mastery = 1, + [Description("Not enough mesos.")] + s_mastery_error_lack_meso = 2, + [Description("You have not completed the required quest.")] + s_mastery_error_lack_quest = 3, + [Description("Not enough items.")] + s_mastery_error_lack_item = 4, + [Description("System error")] + s_mastery_error_unknown = 5, + [Description("Insufficient level.")] + s_mastery_error_invalid_level = 7, + [Description("The game will not run due to fatigue time.")] + s_anti_addiction_cannot_receive = 12, +} diff --git a/Maple2.Model/Error/MesoMarketError.cs b/Maple2.Model/Error/MesoMarketError.cs index 9da9e2198..b3c2c646e 100644 --- a/Maple2.Model/Error/MesoMarketError.cs +++ b/Maple2.Model/Error/MesoMarketError.cs @@ -1,50 +1,50 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum MesoMarketError { - none = 0, - [Description("An error occurred with the request. Please try again.")] - s_mesoMarket_error_errorDB = 2, - [Description("Meso trading is currently unavailable.")] - s_mesoMarket_error_shutDown = 3, - [Description("Another request is being processed. Please try again later.")] - s_mesoMarket_error_alreadyDbWork = 4, - [Description("You do not have enough Mesos to list.")] - s_mesoMarket_error_hasNotMeso = 5, - [Description("Not enough merets.\nDo you want to buy merets?")] - s_err_lack_merat_ask = 6, - [Description("You don't have enough Meso Tokens. Do you want to buy some?")] - s_err_lack_meso_makret_token_ask = 7, - [Description("You reached the listing limit.\nYou cannot list any more.")] - s_mesoMarket_error_maxCountOwnProduct = 8, - [Description("The item could not be sold.")] - s_mesoMarket_error_notFoundProduct = 9, - [Description("An error occurred in the search range.")] - s_mesoMarket_error_invalidSearchParam = 10, - [Description("You can't purchase your own mesos.")] - s_mesoMarket_error_cannotBuyOwnProduct = 11, - [Description("The item could not be sold.")] - s_mesoMarket_error_cannotBuyExpireDate = 12, - [Description("You reached the listing limit for the day.\nNo more mesos can be listed today.")] - s_mesoMarket_error_maxCountRegister = 13, - [Description("You've reached your meso purchase limit.\nThe limit is reset on the 1st of every month. You cannot purchase any more mesos this month.")] - s_mesoMarket_error_maxCountBuy = 14, - [Description("Invalid quantity of Mesos to sell.")] - s_mesoMarket_error_invalidSaleMoney = 15, - [Description("Your sale price must be within {0}% of the current average price, or between {1} and {2} Meso Tokens.")] - s_mesoMarket_error_invalidBuyMerat = 16, - [Description("This item is sold out.")] - s_mesoMarket_error_alreadySoldProduct = 17, - // Not sure exactly how these two work, but they have something to do with suspensions - // 1097, 1098, 1102, 1103, 1104, 1106 - [Description("Prohibited because of your Penalty.")] - s_admin_block_velma_msgbox_content = 18, - [Description("Prohibited because of your Citation.")] - s_admin_block_velma_ugc_msgbox_content = 19, - - [Description("System Error: Black Market Meso Trade code={0}")] - s_mesoMarket_error_unknown = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum MesoMarketError { + none = 0, + [Description("An error occurred with the request. Please try again.")] + s_mesoMarket_error_errorDB = 2, + [Description("Meso trading is currently unavailable.")] + s_mesoMarket_error_shutDown = 3, + [Description("Another request is being processed. Please try again later.")] + s_mesoMarket_error_alreadyDbWork = 4, + [Description("You do not have enough Mesos to list.")] + s_mesoMarket_error_hasNotMeso = 5, + [Description("Not enough merets.\nDo you want to buy merets?")] + s_err_lack_merat_ask = 6, + [Description("You don't have enough Meso Tokens. Do you want to buy some?")] + s_err_lack_meso_makret_token_ask = 7, + [Description("You reached the listing limit.\nYou cannot list any more.")] + s_mesoMarket_error_maxCountOwnProduct = 8, + [Description("The item could not be sold.")] + s_mesoMarket_error_notFoundProduct = 9, + [Description("An error occurred in the search range.")] + s_mesoMarket_error_invalidSearchParam = 10, + [Description("You can't purchase your own mesos.")] + s_mesoMarket_error_cannotBuyOwnProduct = 11, + [Description("The item could not be sold.")] + s_mesoMarket_error_cannotBuyExpireDate = 12, + [Description("You reached the listing limit for the day.\nNo more mesos can be listed today.")] + s_mesoMarket_error_maxCountRegister = 13, + [Description("You've reached your meso purchase limit.\nThe limit is reset on the 1st of every month. You cannot purchase any more mesos this month.")] + s_mesoMarket_error_maxCountBuy = 14, + [Description("Invalid quantity of Mesos to sell.")] + s_mesoMarket_error_invalidSaleMoney = 15, + [Description("Your sale price must be within {0}% of the current average price, or between {1} and {2} Meso Tokens.")] + s_mesoMarket_error_invalidBuyMerat = 16, + [Description("This item is sold out.")] + s_mesoMarket_error_alreadySoldProduct = 17, + // Not sure exactly how these two work, but they have something to do with suspensions + // 1097, 1098, 1102, 1103, 1104, 1106 + [Description("Prohibited because of your Penalty.")] + s_admin_block_velma_msgbox_content = 18, + [Description("Prohibited because of your Citation.")] + s_admin_block_velma_ugc_msgbox_content = 19, + + [Description("System Error: Black Market Meso Trade code={0}")] + s_mesoMarket_error_unknown = byte.MaxValue, +} diff --git a/Maple2.Model/Error/MigrationError.cs b/Maple2.Model/Error/MigrationError.cs index 613fbedc0..bdf583db7 100644 --- a/Maple2.Model/Error/MigrationError.cs +++ b/Maple2.Model/Error/MigrationError.cs @@ -1,43 +1,43 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum MigrationError : byte { - ok = 0, - [Description("The server is full. Try again later.")] - s_move_err_field_limit = 7, - [Description("Server not found.")] - s_move_err_no_server = 8, - [Description("Reached the player limit.")] - s_move_err_over_user = 9, // also 21 - [Description("You cannot enter the channel because it is full.\nPlease use another channel.")] - s_move_err_member_limit = 10, // also 22 - [Description("Cannot enter because the time limit has run out.")] - s_move_err_time_out = 12, - [Description("The dungeon cannot be found.")] - s_move_err_dungeon_not_exist = 17, - [Description("You are not the party leader.")] - s_party_err_not_chief = 27, - [Description("Only a party can enter.")] - s_move_err_NotFoundParty = 28, - // s_move_err_dungeon_not_exist message, but also sets v16 = 1? - [Description("The dungeon cannot be found.")] - s_move_err_dungeon_not_exist_2 = 29, - [Description("Failed to create a dungeon.")] - s_move_err_FailCreateDungeon = 30, - [Description("Cannot enter while looking for a dungeon.")] - s_move_err_DungeonMatch = 31, - [Description("A party member is still in the dungeon.\nPlease try again after all party members have exited the dungeon.")] - s_move_err_InsideDungeonUser = 32, - [Description("The entry time has passed. You can no longer enter.")] - s_move_err_ExpireEnterTime = 33, - [Description("A party member is still in Mushking Royale. Please try again after all party members have exited.")] - s_move_err_InsideSurvivalSquad = 34, - [Description("Cannot enter because the wedding has completed.")] - s_move_err_wedding_complete = 35, - - [Description("An unknown error has occurred while moving the server. Code={0}")] - s_move_err_default = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum MigrationError : byte { + ok = 0, + [Description("The server is full. Try again later.")] + s_move_err_field_limit = 7, + [Description("Server not found.")] + s_move_err_no_server = 8, + [Description("Reached the player limit.")] + s_move_err_over_user = 9, // also 21 + [Description("You cannot enter the channel because it is full.\nPlease use another channel.")] + s_move_err_member_limit = 10, // also 22 + [Description("Cannot enter because the time limit has run out.")] + s_move_err_time_out = 12, + [Description("The dungeon cannot be found.")] + s_move_err_dungeon_not_exist = 17, + [Description("You are not the party leader.")] + s_party_err_not_chief = 27, + [Description("Only a party can enter.")] + s_move_err_NotFoundParty = 28, + // s_move_err_dungeon_not_exist message, but also sets v16 = 1? + [Description("The dungeon cannot be found.")] + s_move_err_dungeon_not_exist_2 = 29, + [Description("Failed to create a dungeon.")] + s_move_err_FailCreateDungeon = 30, + [Description("Cannot enter while looking for a dungeon.")] + s_move_err_DungeonMatch = 31, + [Description("A party member is still in the dungeon.\nPlease try again after all party members have exited the dungeon.")] + s_move_err_InsideDungeonUser = 32, + [Description("The entry time has passed. You can no longer enter.")] + s_move_err_ExpireEnterTime = 33, + [Description("A party member is still in Mushking Royale. Please try again after all party members have exited.")] + s_move_err_InsideSurvivalSquad = 34, + [Description("Cannot enter because the wedding has completed.")] + s_move_err_wedding_complete = 35, + + [Description("An unknown error has occurred while moving the server. Code={0}")] + s_move_err_default = byte.MaxValue, +} diff --git a/Maple2.Model/Error/MyInfoError.cs b/Maple2.Model/Error/MyInfoError.cs index b5d2a672f..bd77752d8 100644 --- a/Maple2.Model/Error/MyInfoError.cs +++ b/Maple2.Model/Error/MyInfoError.cs @@ -1,13 +1,13 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum MyInfoError { - none = 0, - [Description("Contains a forbidden word ({0}).")] - s_ban_check_err_any_word = 1, - - custom_message = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum MyInfoError { + none = 0, + [Description("Contains a forbidden word ({0}).")] + s_ban_check_err_any_word = 1, + + custom_message = byte.MaxValue, +} diff --git a/Maple2.Model/Error/PartyError.cs b/Maple2.Model/Error/PartyError.cs index 00dc86df7..635c8bd5c 100644 --- a/Maple2.Model/Error/PartyError.cs +++ b/Maple2.Model/Error/PartyError.cs @@ -1,96 +1,96 @@ - -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum PartyError : byte { - none = 0, - [Description("The party is full.")] - s_party_err_full = 2, - [Description("You are not the party leader.")] - s_party_err_not_chief = 4, - [Description("The party has already been made.")] - s_party_err_already = 5, - [Description("You cannot invite that player to the party.")] - s_party_err_not_exist = 7, - [Description("{0} declined the party invitation.")] - s_party_err_deny = 9, - [Description("You cannot invite yourself to a party.")] - s_party_err_myself = 11, - [Description("{0} failed to respond to your party invitation.")] - s_party_err_deny_by_timeout = 12, - [Description("That player cannot accept party invites at this time.")] - s_party_err_cannot_invite = 14, - [Description("{0} has already received a party request.")] - s_party_err_alreadyInvite = 15, - [Description("You did not meet the entry requirements.")] - s_party_err_fail_enterable_result = 16, - [Description("Your Level is lower than the minimum level requirement.")] - s_party_err_lack_level = 17, - [Description("Your Gear Score is lower than the minimum Gear Score requirement.")] - s_party_err_lack_gear_score = 18, - [Description("The party is full.")] - s_party_err_full_limit_player = 19, - [Description("{0} refused the party invitation.")] - s_party_err_deny_by_auto = 20, - [Description("{0} cannot accept party invitations right now. Please try again later.")] - s_party_err_deny_by_system = 21, - [Description("This recruitment listing has been deleted.")] - s_party_err_invalid_party = 22, - [Description("Recruitment listing outdated. Please refresh and try again.")] - s_party_err_invalid_chief = 23, - [Description("This recruitment listing has been deleted.")] - s_party_err_invalid_recruit = 24, - [Description("Recruitment listing outdated. Please refresh and try again.")] - s_party_err_wrong_party = 25, - [Description("Recruitment listing outdated. Please refresh and try again.")] - s_party_err_wrong_recruit = 26, - [Description("Not enough merets.")] - s_err_lack_merat = 27, - [Description("You already received a party invite.")] - s_party_err_inviteMe = 28, - [Description("You cannot reset the dungeon while a party member is still inside.")] - s_room_party_err_is_in_user = 29, - [Description("You cannot send a party invite while fighting a dungeon boss.")] - s_party_invite_boss_room = 30, - [Description("Party not found.")] - s_party_err_not_found = 31, - [Description("You requested to join {0}'s party.")] - s_party_request_invite = 32, - [Description("Another request is already in progress.")] - s_party_err_already_vote = 33, - [Description("Not enough party members to start a kick vote.")] - s_party_err_vote_need_more_people = 34, - [Description("Please wait before requesting another vote.")] - s_party_err_vote_cooldown = 35, - [Description("That can only be done while battling the boss of {0}.")] - s_party_err_vote_cannot_kick_vote = 36, - [Description("That can only be done while fighting in dungeons.")] - s_party_err_vote_cannot_kick_vote_only_dungeon = 37, - [Description("You cannot kick a party member while you are fighting a dungeon boss.")] - s_party_expel_boss_room = 38, - [Description("You cannot kick a player that is already in a Mushking Royale match.")] - s_party_expel_maple_survival_squad = 39, - [Description("Only the party leader can send the request.")] - s_dungeonMatch_error_isNotChief = 40, - [Description("A party member has disconnected.")] - s_dungeonMatch_error_hasOfflineUser = 41, - [Description("A party member is still in the dungeon.\nPlease try again after all party members have exited the dungeon.")] - s_dungeonMatch_error_insideDungeonUser = 42, - [Description("You're already matching for other content.")] - s_party_err_dungeon_match_another = 43, - [Description("Only the party leader can send the request.")] - s_party_err_isNotChief = 44, - [Description("A party member is offline.")] - s_party_err_hasOfflineUser = 45, - [Description("A party member is already playing Mushking Royale.")] - s_party_err_inside_survival = 46, - [Description("You're already matching for other content.")] - s_party_err_another_matching = 47, - [Description("A party member is queueing for solo Mushking Royale.")] - s_party_err_survival_has_solo_register = 48, - [Description("A Mushking Royale squad cannot have more than 4 players.")] - s_maple_survival_error_squad_register_over_count = 49, -} + +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum PartyError : byte { + none = 0, + [Description("The party is full.")] + s_party_err_full = 2, + [Description("You are not the party leader.")] + s_party_err_not_chief = 4, + [Description("The party has already been made.")] + s_party_err_already = 5, + [Description("You cannot invite that player to the party.")] + s_party_err_not_exist = 7, + [Description("{0} declined the party invitation.")] + s_party_err_deny = 9, + [Description("You cannot invite yourself to a party.")] + s_party_err_myself = 11, + [Description("{0} failed to respond to your party invitation.")] + s_party_err_deny_by_timeout = 12, + [Description("That player cannot accept party invites at this time.")] + s_party_err_cannot_invite = 14, + [Description("{0} has already received a party request.")] + s_party_err_alreadyInvite = 15, + [Description("You did not meet the entry requirements.")] + s_party_err_fail_enterable_result = 16, + [Description("Your Level is lower than the minimum level requirement.")] + s_party_err_lack_level = 17, + [Description("Your Gear Score is lower than the minimum Gear Score requirement.")] + s_party_err_lack_gear_score = 18, + [Description("The party is full.")] + s_party_err_full_limit_player = 19, + [Description("{0} refused the party invitation.")] + s_party_err_deny_by_auto = 20, + [Description("{0} cannot accept party invitations right now. Please try again later.")] + s_party_err_deny_by_system = 21, + [Description("This recruitment listing has been deleted.")] + s_party_err_invalid_party = 22, + [Description("Recruitment listing outdated. Please refresh and try again.")] + s_party_err_invalid_chief = 23, + [Description("This recruitment listing has been deleted.")] + s_party_err_invalid_recruit = 24, + [Description("Recruitment listing outdated. Please refresh and try again.")] + s_party_err_wrong_party = 25, + [Description("Recruitment listing outdated. Please refresh and try again.")] + s_party_err_wrong_recruit = 26, + [Description("Not enough merets.")] + s_err_lack_merat = 27, + [Description("You already received a party invite.")] + s_party_err_inviteMe = 28, + [Description("You cannot reset the dungeon while a party member is still inside.")] + s_room_party_err_is_in_user = 29, + [Description("You cannot send a party invite while fighting a dungeon boss.")] + s_party_invite_boss_room = 30, + [Description("Party not found.")] + s_party_err_not_found = 31, + [Description("You requested to join {0}'s party.")] + s_party_request_invite = 32, + [Description("Another request is already in progress.")] + s_party_err_already_vote = 33, + [Description("Not enough party members to start a kick vote.")] + s_party_err_vote_need_more_people = 34, + [Description("Please wait before requesting another vote.")] + s_party_err_vote_cooldown = 35, + [Description("That can only be done while battling the boss of {0}.")] + s_party_err_vote_cannot_kick_vote = 36, + [Description("That can only be done while fighting in dungeons.")] + s_party_err_vote_cannot_kick_vote_only_dungeon = 37, + [Description("You cannot kick a party member while you are fighting a dungeon boss.")] + s_party_expel_boss_room = 38, + [Description("You cannot kick a player that is already in a Mushking Royale match.")] + s_party_expel_maple_survival_squad = 39, + [Description("Only the party leader can send the request.")] + s_dungeonMatch_error_isNotChief = 40, + [Description("A party member has disconnected.")] + s_dungeonMatch_error_hasOfflineUser = 41, + [Description("A party member is still in the dungeon.\nPlease try again after all party members have exited the dungeon.")] + s_dungeonMatch_error_insideDungeonUser = 42, + [Description("You're already matching for other content.")] + s_party_err_dungeon_match_another = 43, + [Description("Only the party leader can send the request.")] + s_party_err_isNotChief = 44, + [Description("A party member is offline.")] + s_party_err_hasOfflineUser = 45, + [Description("A party member is already playing Mushking Royale.")] + s_party_err_inside_survival = 46, + [Description("You're already matching for other content.")] + s_party_err_another_matching = 47, + [Description("A party member is queueing for solo Mushking Royale.")] + s_party_err_survival_has_solo_register = 48, + [Description("A Mushking Royale squad cannot have more than 4 players.")] + s_maple_survival_error_squad_register_over_count = 49, +} diff --git a/Maple2.Model/Error/PartySearchError.cs b/Maple2.Model/Error/PartySearchError.cs index 5d458853f..0505d1f9a 100644 --- a/Maple2.Model/Error/PartySearchError.cs +++ b/Maple2.Model/Error/PartySearchError.cs @@ -1,36 +1,36 @@ - -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum PartySearchError : int { - none = 0, - [Description("Party Finder system error.\nPlease try again later.\nIf the error continues to occur, please contact customer support.")] - s_partysearch_err_server_db = 2, - [Description("You are already registered in the party finder.")] - s_partysearch_err_server_already_register = 13, // or 14 or 15 or 105 - [Description("Processing the previous request. Please wait.")] - s_partysearch_err_server_lastaction = 99, - [Description("Only the party leader can do this.")] - s_partysearch_err_server_not_chief = 101, - [Description("Registration conditions do not match.\nPlease check again.")] - s_partysearch_err_server_invalid_type = 102, - [Description("Your listing is being posted.\nPlease wait.")] - s_partysearch_err_server_registring = 103, // Only works with category 1 - [Description("The party has already been made.")] - s_partysearch_err_server_in_party = 104, - [Description("You already removed the recruitment listing.")] - s_partysearch_err_server_not_find_recruit = 106, // Only works with category 1 - [Description("A prohibited word is in the title.\nPlease enter again.")] - s_partysearch_err_server_banword_title = 108, - [Description("A prohibited word is in the search text.\nPlease enter again.")] - s_partysearch_err_server_banword_findword = 108, // Only works with category 2 - [Description("You cannot access the Party Finder at this time.")] - s_partysearch_err_server_blocked = 109, - [Description("Cannot apply for party recruitment because\nthe max number of party members has been reached.")] - s_partysearch_err_server_max_member = 110, - [Description("You cannot post a recruitment listing until you have dealt with the pending party invitation.")] - s_partysearch_err_server_party_invited = 111, -} + +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum PartySearchError : int { + none = 0, + [Description("Party Finder system error.\nPlease try again later.\nIf the error continues to occur, please contact customer support.")] + s_partysearch_err_server_db = 2, + [Description("You are already registered in the party finder.")] + s_partysearch_err_server_already_register = 13, // or 14 or 15 or 105 + [Description("Processing the previous request. Please wait.")] + s_partysearch_err_server_lastaction = 99, + [Description("Only the party leader can do this.")] + s_partysearch_err_server_not_chief = 101, + [Description("Registration conditions do not match.\nPlease check again.")] + s_partysearch_err_server_invalid_type = 102, + [Description("Your listing is being posted.\nPlease wait.")] + s_partysearch_err_server_registring = 103, // Only works with category 1 + [Description("The party has already been made.")] + s_partysearch_err_server_in_party = 104, + [Description("You already removed the recruitment listing.")] + s_partysearch_err_server_not_find_recruit = 106, // Only works with category 1 + [Description("A prohibited word is in the title.\nPlease enter again.")] + s_partysearch_err_server_banword_title = 108, + [Description("A prohibited word is in the search text.\nPlease enter again.")] + s_partysearch_err_server_banword_findword = 108, // Only works with category 2 + [Description("You cannot access the Party Finder at this time.")] + s_partysearch_err_server_blocked = 109, + [Description("Cannot apply for party recruitment because\nthe max number of party members has been reached.")] + s_partysearch_err_server_max_member = 110, + [Description("You cannot post a recruitment listing until you have dealt with the pending party invitation.")] + s_partysearch_err_server_party_invited = 111, +} diff --git a/Maple2.Model/Error/PetError.cs b/Maple2.Model/Error/PetError.cs index a556e1989..2f6299dde 100644 --- a/Maple2.Model/Error/PetError.cs +++ b/Maple2.Model/Error/PetError.cs @@ -1,23 +1,23 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum PetError { - [Description("Not enough mesos.")] - s_err_lack_meso = 27, - [Description("Not enough merets.")] - s_err_lack_merat_ask = 28, - [Description("Storage is full.")] - s_item_err_store_full = 30, - [Description("You can feed your pet up to a year's worth of food at once.\nIt won't be healthy if you feed them more.")] - s_pet_extension_period_limit = 32, - [Description("You cannot summon a pet from this location.")] - s_pet_error_summon_potion = 33, - [Description("")] - none = 39, - - [Description("An unknown error has occurred.\nPlease try again later.")] - s_common_error_unknown = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum PetError { + [Description("Not enough mesos.")] + s_err_lack_meso = 27, + [Description("Not enough merets.")] + s_err_lack_merat_ask = 28, + [Description("Storage is full.")] + s_item_err_store_full = 30, + [Description("You can feed your pet up to a year's worth of food at once.\nIt won't be healthy if you feed them more.")] + s_pet_extension_period_limit = 32, + [Description("You cannot summon a pet from this location.")] + s_pet_error_summon_potion = 33, + [Description("")] + none = 39, + + [Description("An unknown error has occurred.\nPlease try again later.")] + s_common_error_unknown = byte.MaxValue, +} diff --git a/Maple2.Model/Error/QuestError.cs b/Maple2.Model/Error/QuestError.cs index 40af945cd..b263b5ced 100644 --- a/Maple2.Model/Error/QuestError.cs +++ b/Maple2.Model/Error/QuestError.cs @@ -1,21 +1,21 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum QuestError { - none = 0, - [Description("Clear out some space in your inventory.")] - s_quest_error_inventory_full = 1, - [Description("Failed to complete the quest.")] - s_quest_error_consume_fail = 2, - [Description("Failed to accept the quest.")] - s_quest_error_accept_fail = 3, - [Description("This quest has expired.")] - s_quest_error_invalid_date = 4, - [Description("You cannot receive rewards while tombstoned.")] - s_quest_error_fail_complete_by_dead = 5, - [Description("")] - s_alliance_quest_completion_ticket_error = 7, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum QuestError { + none = 0, + [Description("Clear out some space in your inventory.")] + s_quest_error_inventory_full = 1, + [Description("Failed to complete the quest.")] + s_quest_error_consume_fail = 2, + [Description("Failed to accept the quest.")] + s_quest_error_accept_fail = 3, + [Description("This quest has expired.")] + s_quest_error_invalid_date = 4, + [Description("You cannot receive rewards while tombstoned.")] + s_quest_error_fail_complete_by_dead = 5, + [Description("")] + s_alliance_quest_completion_ticket_error = 7, +} diff --git a/Maple2.Model/Error/ShopError.cs b/Maple2.Model/Error/ShopError.cs index dd7a76074..fc7aa7930 100644 --- a/Maple2.Model/Error/ShopError.cs +++ b/Maple2.Model/Error/ShopError.cs @@ -1,44 +1,44 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum ShopError { - [Description("Not enough supplies.")] - s_err_lack_shopitem = 2, - [Description("Item not found.")] - s_err_invalid_item = 4, - [Description("Cannot be sold.")] - s_msg_cant_sell = 9, - [Description("Not enough mesos.")] - s_err_lack_meso = 10, - [Description("Not enough merets.")] - s_err_lack_merat = 11, - [Description("Your inventory is full.")] - s_err_inventory = 14, - [Description("Not enough guild trophies.")] - s_err_lack_guild_trophy = 15, - [Description("Insufficient items. (Missing Item: $item:{0}$)")] - s_err_lack_payment_item = 17, - [Description("You can buy after being in the guild for 7 days.")] - s_err_lack_guild_require_date = 19, - [Description("The game will not run due to fatigue time.")] - s_anti_addiction_cannot_receive = 22, - [Description("You can't sell items to this shop.")] - s_msg_cant_sell_to_only_sell_shop = 23, - [Description("A restriction will be placed on Trade for 60 seconds after entering the game.")] - s_system_property_protection_time = 24, - [Description("Can only be purchased by guild leaders.")] - s_guild_err_buy_no_master = 25, - [Description("Not enough guild funds.")] - s_guild_err_not_enough_guild_fund = 26, - [Description("You cannot purchase this at this time.")] - s_err_invalid_item_cannot_buy_by_period = 27, - [Description("Can only be used during the event period.")] - s_shop_no_star_point_event = 29, - [Description("This item cannot be purchased in the country from which you are connecting.")] - s_meratmarket_error_country_limit = 31, - [Description("You cannot sell a pet while it is summoned. Please unsummon your pet and try again.")] - s_err_cannot_sell_petitem_summon = 32, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum ShopError { + [Description("Not enough supplies.")] + s_err_lack_shopitem = 2, + [Description("Item not found.")] + s_err_invalid_item = 4, + [Description("Cannot be sold.")] + s_msg_cant_sell = 9, + [Description("Not enough mesos.")] + s_err_lack_meso = 10, + [Description("Not enough merets.")] + s_err_lack_merat = 11, + [Description("Your inventory is full.")] + s_err_inventory = 14, + [Description("Not enough guild trophies.")] + s_err_lack_guild_trophy = 15, + [Description("Insufficient items. (Missing Item: $item:{0}$)")] + s_err_lack_payment_item = 17, + [Description("You can buy after being in the guild for 7 days.")] + s_err_lack_guild_require_date = 19, + [Description("The game will not run due to fatigue time.")] + s_anti_addiction_cannot_receive = 22, + [Description("You can't sell items to this shop.")] + s_msg_cant_sell_to_only_sell_shop = 23, + [Description("A restriction will be placed on Trade for 60 seconds after entering the game.")] + s_system_property_protection_time = 24, + [Description("Can only be purchased by guild leaders.")] + s_guild_err_buy_no_master = 25, + [Description("Not enough guild funds.")] + s_guild_err_not_enough_guild_fund = 26, + [Description("You cannot purchase this at this time.")] + s_err_invalid_item_cannot_buy_by_period = 27, + [Description("Can only be used during the event period.")] + s_shop_no_star_point_event = 29, + [Description("This item cannot be purchased in the country from which you are connecting.")] + s_meratmarket_error_country_limit = 31, + [Description("You cannot sell a pet while it is summoned. Please unsummon your pet and try again.")] + s_err_cannot_sell_petitem_summon = 32, +} diff --git a/Maple2.Model/Error/StorageInventoryError.cs b/Maple2.Model/Error/StorageInventoryError.cs index 2dd3495f0..6256a50a5 100644 --- a/Maple2.Model/Error/StorageInventoryError.cs +++ b/Maple2.Model/Error/StorageInventoryError.cs @@ -1,35 +1,35 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum StorageInventoryError { - [Description("The item amounts do not match.")] - s_item_err_invalid_count = 10, - [Description("This item cannot be stored here.")] - s_item_err_invaild_store_type = 12, - [Description("Storage is full.")] - s_item_err_store_full = 13, - [Description("This cannot be expanded any further.")] - s_store_err_expand_max = 14, - [Description("Not enough merets.")] - s_cannot_charge_merat = 15, - [Description("This item cannot be stored.")] - s_item_err_binditem = 16, - [Description("An item belonging to a specific character can only be retrieved by that character.")] - s_item_err_binditem_store_out = 17, - [Description("Meso storage is not possible with this safe.")] - s_store_err_deposit_disable_type = 18, - [Description("Confirm the amount entered.")] - s_store_err_deposit_invalid_money = 19, - [Description("Up to {0} mesos can be stored.")] - s_store_err_deposit_max_money = 20, - [Description("Not enough funds.")] - s_cashshop_lack_balance = 21, - [Description("This item can only be retrieved by the character that stored it.")] - s_item_err_moveDisableitem_store_out = 22, - - [Description("Bank safe error code: {0}")] - s_store_err_code = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum StorageInventoryError { + [Description("The item amounts do not match.")] + s_item_err_invalid_count = 10, + [Description("This item cannot be stored here.")] + s_item_err_invaild_store_type = 12, + [Description("Storage is full.")] + s_item_err_store_full = 13, + [Description("This cannot be expanded any further.")] + s_store_err_expand_max = 14, + [Description("Not enough merets.")] + s_cannot_charge_merat = 15, + [Description("This item cannot be stored.")] + s_item_err_binditem = 16, + [Description("An item belonging to a specific character can only be retrieved by that character.")] + s_item_err_binditem_store_out = 17, + [Description("Meso storage is not possible with this safe.")] + s_store_err_deposit_disable_type = 18, + [Description("Confirm the amount entered.")] + s_store_err_deposit_invalid_money = 19, + [Description("Up to {0} mesos can be stored.")] + s_store_err_deposit_max_money = 20, + [Description("Not enough funds.")] + s_cashshop_lack_balance = 21, + [Description("This item can only be retrieved by the character that stored it.")] + s_item_err_moveDisableitem_store_out = 22, + + [Description("Bank safe error code: {0}")] + s_store_err_code = byte.MaxValue, +} diff --git a/Maple2.Model/Error/TradeError.cs b/Maple2.Model/Error/TradeError.cs index e923df2bb..304d0707b 100644 --- a/Maple2.Model/Error/TradeError.cs +++ b/Maple2.Model/Error/TradeError.cs @@ -1,53 +1,53 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum TradeError : byte { - [Description("Trade failed.")] - s_trade_error_system = 0, - [Description("The trade request failed because the target is too far away.")] - s_trade_error_distance = 1, - [Description("{0} has already received a trade request.")] - s_trade_error_already_request = 2, - [Description("Trade in progress.")] - s_trade_error_trading_now = 3, - [Description("Your offer has already been finalized.")] - s_trade_error_latched = 5, - [Description("Trading is not possible with this target.")] - s_trade_error_decline = 6, - [Description("All slots in the trade window are full. Remove an item, or make deliver the item through a separate trade.")] - s_trade_error_itemcount = 7, - [Description("The other player's inventory is full.")] - s_trade_error_slotcount = 8, - [Description("The trade has been canceled because there are no items to trade.")] - s_trade_error_itemnone = 9, - [Description("You cannot trade during PvP.")] - s_trade_error_pvp = 10, - [Description("This can't be done here.")] - s_trade_error_mapLimit = 11, - [Description("{0} failed to respond to your trade request.")] - s_trade_error_timeout = 12, - [Description("Confirm the trade amount.")] - s_trade_error_invalid_meso = 13, - [Description("Not enough mesos.")] - s_trade_error_meso = 14, - [Description("{0} is in protected mode.")] - s_trade_error_target_property_protection_time = 15, - [Description("Trading is not possible due to {0}'s fatigue.")] - s_trade_error_target_fatigue_penalty = 16, - [Description("")] - unknown = 17, - [Description("You cannot trade if the highest level character in your account is less than {0}.")] - s_trade_error_restricted_userlevel_max = 18, - [Description("You cannot trade with this player because their highest level character is less than {0}.")] - s_trade_error_restricted_target_userlevel_max = 19, - [Description("You must have a character that's Lv. {1} or higher to trade {0}.")] - s_trade_error_send_restricted_by_user_level_ex = 20, - [Description("The person you are trading with must have a character that's Lv. {1} or higher to trade {0}.")] - s_trade_error_recv_restricted_by_user_level_ex = 21, - - [Description("System Error: Community")] - s_trade_error = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum TradeError : byte { + [Description("Trade failed.")] + s_trade_error_system = 0, + [Description("The trade request failed because the target is too far away.")] + s_trade_error_distance = 1, + [Description("{0} has already received a trade request.")] + s_trade_error_already_request = 2, + [Description("Trade in progress.")] + s_trade_error_trading_now = 3, + [Description("Your offer has already been finalized.")] + s_trade_error_latched = 5, + [Description("Trading is not possible with this target.")] + s_trade_error_decline = 6, + [Description("All slots in the trade window are full. Remove an item, or make deliver the item through a separate trade.")] + s_trade_error_itemcount = 7, + [Description("The other player's inventory is full.")] + s_trade_error_slotcount = 8, + [Description("The trade has been canceled because there are no items to trade.")] + s_trade_error_itemnone = 9, + [Description("You cannot trade during PvP.")] + s_trade_error_pvp = 10, + [Description("This can't be done here.")] + s_trade_error_mapLimit = 11, + [Description("{0} failed to respond to your trade request.")] + s_trade_error_timeout = 12, + [Description("Confirm the trade amount.")] + s_trade_error_invalid_meso = 13, + [Description("Not enough mesos.")] + s_trade_error_meso = 14, + [Description("{0} is in protected mode.")] + s_trade_error_target_property_protection_time = 15, + [Description("Trading is not possible due to {0}'s fatigue.")] + s_trade_error_target_fatigue_penalty = 16, + [Description("")] + unknown = 17, + [Description("You cannot trade if the highest level character in your account is less than {0}.")] + s_trade_error_restricted_userlevel_max = 18, + [Description("You cannot trade with this player because their highest level character is less than {0}.")] + s_trade_error_restricted_target_userlevel_max = 19, + [Description("You must have a character that's Lv. {1} or higher to trade {0}.")] + s_trade_error_send_restricted_by_user_level_ex = 20, + [Description("The person you are trading with must have a character that's Lv. {1} or higher to trade {0}.")] + s_trade_error_recv_restricted_by_user_level_ex = 21, + + [Description("System Error: Community")] + s_trade_error = byte.MaxValue, +} diff --git a/Maple2.Model/Error/UgcMapError.cs b/Maple2.Model/Error/UgcMapError.cs index 65776d2a3..f9e59d72c 100644 --- a/Maple2.Model/Error/UgcMapError.cs +++ b/Maple2.Model/Error/UgcMapError.cs @@ -1,227 +1,227 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum UgcMapError : byte { - [Description("")] - s_ugcmap_ok = 0, - [Description("")] - s_empty_string = 1, - [Description("There are still placed items.")] - s_ugcmap_create_on_non_empty_area = 2, - [Description("System Error: Item does not exist.")] - s_ugcmap_not_exist_craft_item = 3, - [Description("You do not own this item.")] - s_ugcmap_not_owned_item = 4, - [Description("That cannot be placed here.")] - s_ugcmap_cant_be_created = 5, - [Description("This cannot be placed at this location.")] - s_ugcmap_cant_create_on_place = 6, - [Description("That cannot be placed at this location.")] - s_ugcmap_no_base_cube = 7, - [Description("You do not own this area.")] - s_ugcmap_dont_have_ownership = 8, - [Description("This terrain cannot be placed above other terrain.")] - s_ugcmap_cant_create_ground_on_ground = 9, - [Description("Can't put this above terrain.")] - s_ugcmap_cant_create_on_ground = 10, - [Description("This can only be placed above terrain.")] - s_ugcmap_only_be_created_on_ground = 11, - [Description("That cannot be placed here.")] - s_ugcmap_cant_stack_on = 12, - [Description("System Error")] - s_ugcmap_db = 13, - [Description("System Error")] - s_ugcmap_center = 14, - [Description("You must have a nearby wall to place a wall decoration")] - s_ugcmap_no_wall_to_attach = 15, - [Description("This item cannot be placed on the wall.")] - s_ugcmap_not_wall_attachable = 16, - [Description("Wall decorations can only be placed on terrain block walls.")] - s_ugcmap_only_be_created_on_wall = 17, - [Description("I can't put that on this wall.")] - s_ugcmap_cant_attached_to_this_wall = 18, - [Description("That item has already been placed.")] - s_ugcmap_have_already_attached = 19, - [Description("There are no items to collect.")] - s_ugcmap_no_cube_to_remove = 20, - [Description("That cannot be retrieved.")] - s_ugcmap_cant_be_removed = 21, - [Description("The stacked items must be collected first.")] - s_ugcmap_cant_remove_before_remove_all_stacked = 22, - [Description("")] - s_ugcmap_cant_remove_building_with_indoor_items = 23, - [Description("")] - s_ugcmap_can_be_remove_from_wall = 24, - [Description("There is no wall decoration in the direction you are facing.")] - s_ugcmap_no_attached_object = 25, - [Description("There are no items to rotate.")] - s_ugcmap_no_cube_to_rotate = 26, - [Description("The default terrain cannot be rotated.")] - s_ugcmap_cant_rotate_default_cube = 27, - [Description("There are no items to exchange.")] - s_ugcmap_no_cube_to_replace = 28, - [Description("You cannot replace this with a furnishing of a different type.")] - s_ugcmap_cant_be_replaced = 29, - [Description("A different wall decoration has been placed.")] - s_ugcmap_attached_cube_exist = 30, - [Description("This can only be exchanged for items that can be stacked.")] - s_ugcmap_cant_replace_stackable_with_not_stackable = 31, - [Description("This location cannot be bought.")] - s_ugcmap_not_a_buyable = 32, - [Description("You do not have enough funds to complete the purchase.")] - s_ugcmap_not_enough_money = 33, - [Description("Another player has already completed the purchase.")] - s_ugcmap_already_owned = 35, - [Description("The contract cannot be canceled.")] - s_ugcmap_salable = 36, - [Description("There are no objects to lift.")] - s_ugcmap_no_cube_to_lift = 37, - [Description("This object cannot be lifted.")] - s_ugcmap_cant_lift_ugc_cube = 38, - [Description("You can't pick up items that belong to someone else.")] - s_ugcmap_cant_lift_salable = 39, - [Description("The default terrain cannot be retrieved.")] - s_ugcmap_cant_remove_default_cube = 40, - [Description("Collect the attached items first.")] - s_ugcmap_cant_remove_cube_with_attached = 41, - [Description("System Error: Furnishing information not found.")] - s_ugcmap_null_cube_item_info = 42, - [Description("You cannot place that above the home's ceiling.")] - s_ugcmap_height_limit = 43, - [Description("You can't place that here.")] - s_ugcmap_area_limit = 44, - [Description("You cannot place any more of this structure.")] - s_ugcmap_building_count = 45, - [Description("You cannot purchase at this time.")] - s_ugcmap_not_for_sale = 46, - [Description("")] - s_ugcmap_no_more_room = 47, - [Description("You have not visited the house. Click the house button to go there.")] - s_ugcmap_no_home = 48, - [Description("You have already purchased this home expansion.")] - s_ugcmap_my_house = 49, - [Description("The contract has expired.")] - s_ugcmap_already_expired = 50, - [Description("There is an item placed at the house.")] - s_ugcmap_have_equipitems = 52, - [Description("That item is already placed.")] - s_ugcmap_cant_replace_same_cube = 53, - [Description("You cannot buy this because you have already bought another area.")] - s_ugcmap_cant_buy_more_than_two_house = 54, - [Description("You do not meet the trophy requirements for this home expansion.")] - s_ugcmap_need_trophy = 55, - [Description("There are no items to place.")] - s_ugcmap_try_place_empty = 57, - [Description("This item cannot be exchanged.")] - s_ugcmap_cant_replace_type = 58, - [Description("This wall decoration cannot be rotated.")] - s_ugcmap_cant_rotate_attached = 59, - [Description("You cannot enter Furnishing Mode here.")] - s_ugcmap_cant_guide_build = 60, - [Description("This cannot be placed below a wall decoration.")] - s_ugcmap_cant_create_under_attach = 61, - [Description("A wall decoration cannot be placed above this item.")] - s_ugcmap_cant_attach_upper_cube = 62, - [Description("This cannot be placed below a wall decoration.")] - s_ugcmap_cant_replace_under_attach = 63, - [Description("You have already placed the maximum number of servants.")] - s_ugcmap_cant_place_maid = 64, - [Description("This can only be placed on the ground.")] - s_ugcmap_only_place_on_the_floor = 65, - [Description("The duration cannot be extended yet.")] - s_ugcmap_not_extension_date = 66, - [Description("You do not have the necessary funds to extend the contract.")] - s_ugcmap_need_extension_pay = 67, - [Description("This area is waiting to be sold.")] - s_ugcmap_expired_salable_group = 68, - [Description("Please sell while outdoors.")] - s_ugcmap_cant_sell_my_home_in_indoor = 69, - [Description("Houses planned for redevelopment cannot be purchased.")] - s_ugcmap_blocked_salable_group = 70, - [Description("System Error: Please try again later")] - s_ugcmap_retry_later = 71, - [Description("The block is being exchanged. Please try again later.")] - s_ugcmap_waiting_for_cube_to_be_replaced = 72, - [Description("The block is being placed. Please try again later.")] - s_ugcmap_waiting_for_cube_to_be_created = 73, - [Description("The block is being removed. Please try again later.")] - s_ugcmap_waiting_for_cube_to_be_removed = 74, - [Description("This cannot be placed.")] - s_ugcmap_trigger_count = 75, - [Description("This house cannot be recommended.")] - s_ugcmap_no_owner_to_commend = 76, - [Description("Star Architect nomination failed. Please try again later.")] - s_ugcmap_add_commend_home_fail_from_db = 77, - [Description("You cannot nominate yourself.")] - s_ugcmap_cant_commend_myself = 78, - [Description("Contains a forbidden word.")] - s_ugcmap_ban_word_included = 79, - [Description("This is not your house.")] - s_ugcmap_not_my_house = 80, - [Description("Already nominated.")] - s_ugcmap_cant_commend_duplicate = 81, - [Description("This cannot be expanded any further.")] - s_ugcmap_cant_extend_area_level_anymore = 83, - [Description("No more bonuses can be collected today.")] - s_ugcmap_cant_take_interior_gift_more = 84, - [Description("Bonuses cannot be received at the moment. Please try again later.")] - s_ugcmap_take_interior_gift_fail_from_db = 85, - [Description("You have already received the bonus.")] - s_ugcmap_already_taken_interior_grade_gift = 86, - [Description("This cannot be expanded any further.")] - s_ugcmap_cant_extend_height_level_anymore = 87, - [Description("You don't meet the requirements to place this.")] - s_ugcmap_cube_lock = 88, - [Description("You cannot buy that furnishing.")] - s_ugcmap_cant_additionalbuy = 89, - [Description("You cannot use this while furnishing.")] - s_err_cannot_use_in_design_home = 90, - [Description("You cannot place furnishings that are not currently available for purchase.")] - s_err_cannot_buy_limited_item_more = 91, - [Description("That furnishing cannot be placed in the current mode.")] - s_err_cannot_install_blueprint = 92, - [Description("Assistants cannot be placed in the current mode.")] - s_err_cannot_install_maid_in_practice = 93, - [Description("GMs only.")] - s_ugcmap_admin_only = 94, - [Description("This item cannot be used.")] - s_ugcmap_not_allowed_item = 95, - [Description("You do not have enough mesos.")] - s_err_ugcmap_not_enough_meso_balance = 96, - [Description("You do not have enough merets.")] - s_err_ugcmap_not_enough_merat_balance = 97, - [Description("Double-click the designer item icon to edit the template again.")] - s_err_ugcmap_cant_build_empty_ugc = 98, - [Description("This cannot be placed.")] - s_ugcmap_installnpc_count = 99, - [Description("You can't earn any more experience from decorating today.")] - s_err_ugcmap_construct_exp_overtime = 100, - [Description("Life Skill items cannot be placed in the current mode.")] - s_err_cannot_install_nurturing_in_design_home = 101, - [Description("That furnishing cannot be placed in the current mode.")] - s_ugcmap_not_use_blueprint_item = 102, - [Description("Interior portals cannot be placed in the current mode.")] - s_err_cannot_install_magic_portal = 103, - [Description("Trigger editors cannot be placed in the current mode.")] - s_err_cannot_install_trigger_editor = 104, - [Description("Trigger control items cannot be placed in the current mode.")] - s_err_cannot_install_trigger_controlobject = 105, - [Description("Interior message items cannot be placed in the current mode.")] - s_err_cannot_install_interior_message = 106, - [Description("Event items cannot be placed in the current mode.")] - s_err_cannot_install_event_cube = 107, - [Description("Trophy-related furnishings cannot be placed in the current mode.")] - s_err_cannot_install_trophy_relative_cube = 108, - [Description("Assistant cooking/alchemy/jeweling stations cannot be placed in the current mode.")] - s_err_cannot_install_workbench_cube = 109, - [Description("Mannequins cannot be placed in the current mode.")] - s_err_cannot_install_fittingdoll = 110, - [Description("UGC items made by others cannot be placed in the current mode.")] - s_err_cannot_install_ugcdesign_maidin_other = 111, - - [Description("Due to a system error, the contract cannot be made.")] - s_ugcmap_system_error = byte.MaxValue, -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum UgcMapError : byte { + [Description("")] + s_ugcmap_ok = 0, + [Description("")] + s_empty_string = 1, + [Description("There are still placed items.")] + s_ugcmap_create_on_non_empty_area = 2, + [Description("System Error: Item does not exist.")] + s_ugcmap_not_exist_craft_item = 3, + [Description("You do not own this item.")] + s_ugcmap_not_owned_item = 4, + [Description("That cannot be placed here.")] + s_ugcmap_cant_be_created = 5, + [Description("This cannot be placed at this location.")] + s_ugcmap_cant_create_on_place = 6, + [Description("That cannot be placed at this location.")] + s_ugcmap_no_base_cube = 7, + [Description("You do not own this area.")] + s_ugcmap_dont_have_ownership = 8, + [Description("This terrain cannot be placed above other terrain.")] + s_ugcmap_cant_create_ground_on_ground = 9, + [Description("Can't put this above terrain.")] + s_ugcmap_cant_create_on_ground = 10, + [Description("This can only be placed above terrain.")] + s_ugcmap_only_be_created_on_ground = 11, + [Description("That cannot be placed here.")] + s_ugcmap_cant_stack_on = 12, + [Description("System Error")] + s_ugcmap_db = 13, + [Description("System Error")] + s_ugcmap_center = 14, + [Description("You must have a nearby wall to place a wall decoration")] + s_ugcmap_no_wall_to_attach = 15, + [Description("This item cannot be placed on the wall.")] + s_ugcmap_not_wall_attachable = 16, + [Description("Wall decorations can only be placed on terrain block walls.")] + s_ugcmap_only_be_created_on_wall = 17, + [Description("I can't put that on this wall.")] + s_ugcmap_cant_attached_to_this_wall = 18, + [Description("That item has already been placed.")] + s_ugcmap_have_already_attached = 19, + [Description("There are no items to collect.")] + s_ugcmap_no_cube_to_remove = 20, + [Description("That cannot be retrieved.")] + s_ugcmap_cant_be_removed = 21, + [Description("The stacked items must be collected first.")] + s_ugcmap_cant_remove_before_remove_all_stacked = 22, + [Description("")] + s_ugcmap_cant_remove_building_with_indoor_items = 23, + [Description("")] + s_ugcmap_can_be_remove_from_wall = 24, + [Description("There is no wall decoration in the direction you are facing.")] + s_ugcmap_no_attached_object = 25, + [Description("There are no items to rotate.")] + s_ugcmap_no_cube_to_rotate = 26, + [Description("The default terrain cannot be rotated.")] + s_ugcmap_cant_rotate_default_cube = 27, + [Description("There are no items to exchange.")] + s_ugcmap_no_cube_to_replace = 28, + [Description("You cannot replace this with a furnishing of a different type.")] + s_ugcmap_cant_be_replaced = 29, + [Description("A different wall decoration has been placed.")] + s_ugcmap_attached_cube_exist = 30, + [Description("This can only be exchanged for items that can be stacked.")] + s_ugcmap_cant_replace_stackable_with_not_stackable = 31, + [Description("This location cannot be bought.")] + s_ugcmap_not_a_buyable = 32, + [Description("You do not have enough funds to complete the purchase.")] + s_ugcmap_not_enough_money = 33, + [Description("Another player has already completed the purchase.")] + s_ugcmap_already_owned = 35, + [Description("The contract cannot be canceled.")] + s_ugcmap_salable = 36, + [Description("There are no objects to lift.")] + s_ugcmap_no_cube_to_lift = 37, + [Description("This object cannot be lifted.")] + s_ugcmap_cant_lift_ugc_cube = 38, + [Description("You can't pick up items that belong to someone else.")] + s_ugcmap_cant_lift_salable = 39, + [Description("The default terrain cannot be retrieved.")] + s_ugcmap_cant_remove_default_cube = 40, + [Description("Collect the attached items first.")] + s_ugcmap_cant_remove_cube_with_attached = 41, + [Description("System Error: Furnishing information not found.")] + s_ugcmap_null_cube_item_info = 42, + [Description("You cannot place that above the home's ceiling.")] + s_ugcmap_height_limit = 43, + [Description("You can't place that here.")] + s_ugcmap_area_limit = 44, + [Description("You cannot place any more of this structure.")] + s_ugcmap_building_count = 45, + [Description("You cannot purchase at this time.")] + s_ugcmap_not_for_sale = 46, + [Description("")] + s_ugcmap_no_more_room = 47, + [Description("You have not visited the house. Click the house button to go there.")] + s_ugcmap_no_home = 48, + [Description("You have already purchased this home expansion.")] + s_ugcmap_my_house = 49, + [Description("The contract has expired.")] + s_ugcmap_already_expired = 50, + [Description("There is an item placed at the house.")] + s_ugcmap_have_equipitems = 52, + [Description("That item is already placed.")] + s_ugcmap_cant_replace_same_cube = 53, + [Description("You cannot buy this because you have already bought another area.")] + s_ugcmap_cant_buy_more_than_two_house = 54, + [Description("You do not meet the trophy requirements for this home expansion.")] + s_ugcmap_need_trophy = 55, + [Description("There are no items to place.")] + s_ugcmap_try_place_empty = 57, + [Description("This item cannot be exchanged.")] + s_ugcmap_cant_replace_type = 58, + [Description("This wall decoration cannot be rotated.")] + s_ugcmap_cant_rotate_attached = 59, + [Description("You cannot enter Furnishing Mode here.")] + s_ugcmap_cant_guide_build = 60, + [Description("This cannot be placed below a wall decoration.")] + s_ugcmap_cant_create_under_attach = 61, + [Description("A wall decoration cannot be placed above this item.")] + s_ugcmap_cant_attach_upper_cube = 62, + [Description("This cannot be placed below a wall decoration.")] + s_ugcmap_cant_replace_under_attach = 63, + [Description("You have already placed the maximum number of servants.")] + s_ugcmap_cant_place_maid = 64, + [Description("This can only be placed on the ground.")] + s_ugcmap_only_place_on_the_floor = 65, + [Description("The duration cannot be extended yet.")] + s_ugcmap_not_extension_date = 66, + [Description("You do not have the necessary funds to extend the contract.")] + s_ugcmap_need_extension_pay = 67, + [Description("This area is waiting to be sold.")] + s_ugcmap_expired_salable_group = 68, + [Description("Please sell while outdoors.")] + s_ugcmap_cant_sell_my_home_in_indoor = 69, + [Description("Houses planned for redevelopment cannot be purchased.")] + s_ugcmap_blocked_salable_group = 70, + [Description("System Error: Please try again later")] + s_ugcmap_retry_later = 71, + [Description("The block is being exchanged. Please try again later.")] + s_ugcmap_waiting_for_cube_to_be_replaced = 72, + [Description("The block is being placed. Please try again later.")] + s_ugcmap_waiting_for_cube_to_be_created = 73, + [Description("The block is being removed. Please try again later.")] + s_ugcmap_waiting_for_cube_to_be_removed = 74, + [Description("This cannot be placed.")] + s_ugcmap_trigger_count = 75, + [Description("This house cannot be recommended.")] + s_ugcmap_no_owner_to_commend = 76, + [Description("Star Architect nomination failed. Please try again later.")] + s_ugcmap_add_commend_home_fail_from_db = 77, + [Description("You cannot nominate yourself.")] + s_ugcmap_cant_commend_myself = 78, + [Description("Contains a forbidden word.")] + s_ugcmap_ban_word_included = 79, + [Description("This is not your house.")] + s_ugcmap_not_my_house = 80, + [Description("Already nominated.")] + s_ugcmap_cant_commend_duplicate = 81, + [Description("This cannot be expanded any further.")] + s_ugcmap_cant_extend_area_level_anymore = 83, + [Description("No more bonuses can be collected today.")] + s_ugcmap_cant_take_interior_gift_more = 84, + [Description("Bonuses cannot be received at the moment. Please try again later.")] + s_ugcmap_take_interior_gift_fail_from_db = 85, + [Description("You have already received the bonus.")] + s_ugcmap_already_taken_interior_grade_gift = 86, + [Description("This cannot be expanded any further.")] + s_ugcmap_cant_extend_height_level_anymore = 87, + [Description("You don't meet the requirements to place this.")] + s_ugcmap_cube_lock = 88, + [Description("You cannot buy that furnishing.")] + s_ugcmap_cant_additionalbuy = 89, + [Description("You cannot use this while furnishing.")] + s_err_cannot_use_in_design_home = 90, + [Description("You cannot place furnishings that are not currently available for purchase.")] + s_err_cannot_buy_limited_item_more = 91, + [Description("That furnishing cannot be placed in the current mode.")] + s_err_cannot_install_blueprint = 92, + [Description("Assistants cannot be placed in the current mode.")] + s_err_cannot_install_maid_in_practice = 93, + [Description("GMs only.")] + s_ugcmap_admin_only = 94, + [Description("This item cannot be used.")] + s_ugcmap_not_allowed_item = 95, + [Description("You do not have enough mesos.")] + s_err_ugcmap_not_enough_meso_balance = 96, + [Description("You do not have enough merets.")] + s_err_ugcmap_not_enough_merat_balance = 97, + [Description("Double-click the designer item icon to edit the template again.")] + s_err_ugcmap_cant_build_empty_ugc = 98, + [Description("This cannot be placed.")] + s_ugcmap_installnpc_count = 99, + [Description("You can't earn any more experience from decorating today.")] + s_err_ugcmap_construct_exp_overtime = 100, + [Description("Life Skill items cannot be placed in the current mode.")] + s_err_cannot_install_nurturing_in_design_home = 101, + [Description("That furnishing cannot be placed in the current mode.")] + s_ugcmap_not_use_blueprint_item = 102, + [Description("Interior portals cannot be placed in the current mode.")] + s_err_cannot_install_magic_portal = 103, + [Description("Trigger editors cannot be placed in the current mode.")] + s_err_cannot_install_trigger_editor = 104, + [Description("Trigger control items cannot be placed in the current mode.")] + s_err_cannot_install_trigger_controlobject = 105, + [Description("Interior message items cannot be placed in the current mode.")] + s_err_cannot_install_interior_message = 106, + [Description("Event items cannot be placed in the current mode.")] + s_err_cannot_install_event_cube = 107, + [Description("Trophy-related furnishings cannot be placed in the current mode.")] + s_err_cannot_install_trophy_relative_cube = 108, + [Description("Assistant cooking/alchemy/jeweling stations cannot be placed in the current mode.")] + s_err_cannot_install_workbench_cube = 109, + [Description("Mannequins cannot be placed in the current mode.")] + s_err_cannot_install_fittingdoll = 110, + [Description("UGC items made by others cannot be placed in the current mode.")] + s_err_cannot_install_ugcdesign_maidin_other = 111, + + [Description("Due to a system error, the contract cannot be made.")] + s_ugcmap_system_error = byte.MaxValue, +} diff --git a/Maple2.Model/Error/WeddingError.cs b/Maple2.Model/Error/WeddingError.cs index 6190e07b7..9813dc765 100644 --- a/Maple2.Model/Error/WeddingError.cs +++ b/Maple2.Model/Error/WeddingError.cs @@ -1,126 +1,126 @@ -// ReSharper disable InconsistentNaming, IdentifierTypo - -using System.ComponentModel; - -namespace Maple2.Model.Error; - -public enum WeddingError : short { - none = 0, - [Description("You are now engaged to {0}")] - s_wedding_propose_ok = 1, - [Description("{0} declined your proposal.")] - s_wedding_result_decline_propose = 2, - [Description("You have proposed to {0}. \\nIf they accept, you both will be engaged.")] - s_wedding_msg_box_propose_to = 3, - [Description("You cancelled the proposal.")] - s_wedding_result_cancel_propose_to = 4, - [Description("{0} cancelled the proposal.")] - s_wedding_result_cancel_propose_from = 5, - [Description("The proposal has been cancelled because the waiting time has expired.")] - s_wedding_result_cancel_propose_expire = 6, - confirm_reservation = 7, // Unknown what string this is supposed to be - decline_reservation = 8, // Unknown what string this is supposed to be - accept_reservation_cancel = 9, // Unknown what string this is supposed to be - decline_reservation_cancel = 10, // Unknown what string this is supposed to be - accept_reservation_change = 11, // Unknown what string this is supposed to be - decline_reservation_change = 12, // Unknown what string this is supposed to be - [Description("You sent a request to your fiancee to confirm the wedding hall reservation. The reservation will be complete when they confirm.")] - s_wedding_result_send_wedding_hall_reservation = 15, - [Description("You have sent a request to your fiancee to change the wedding reservation time.\\nThe reservation will be complete when they confirm.")] - s_wedding_result_send_wedding_hall_reservation_change = 16, - [Description("You have sent a request to your fiancee to cancel the wedding reservation time.\\nOnce confirmed, the cancellation will be complete and a free reservation coupon will be given to book a wedding venue of the same grade in the future.")] - s_wedding_result_send_wedding_hall_reservation_cancel = 17, - s_wedding_result_err_wedding_block_age_to = 19, - s_wedding_result_err_wedding_block_age_from = 20, - [Description("You cannot propose to a character of the same gender.")] - s_wedding_result_err_wedding_block_gender = 21, - [Description("You cannot propose to {0} because you are not friends.\\nBecome friends in order to propose.")] - s_wedding_result_err_not_buddy = 22, - [Description("You are currently engaged and cannot propose to {0}.")] - s_wedding_result_err_propose_state_to = 23, - [Description("You are currently married and cannot propose to {0}.")] - s_wedding_result_err_marriage_state_to = 24, - [Description("You currently have a divorce in progress and cannot propose to {0}.")] - s_wedding_result_err_coolingoff_state_to = 25, - [Description("You have to wait 48 hours after a divorce to remarry.")] - s_wedding_result_err_prpose_cooltime_state_to = 26, - [Description("You cannot propose to a character who is engaged.")] - s_wedding_result_err_propose_state_from = 27, - [Description("You cannot propose to a married character.")] - s_wedding_result_err_mareriage_state_from = 28, - [Description("You cannot propose to a character with a divorce in progress.")] - s_wedding_result_err_coolingoff_state_from = 29, - [Description("You cannot propose to a character with less than 48 hours after divorce.")] - s_wedding_result_err_propose_cooltime_state_from = 30, - [Description("Both characters must have {0} in order to propose.")] - s_wedding_result_err_not_find_propose_item = 31, - [Description("Distance is too far to propose.")] - s_wedding_result_err_propose_distance = 32, - [Description("You have already been proposed.")] - s_wedding_result_err_already_propose_request = 33, - [Description("In order to be engaged, reserve a wedding hall, change reservation, or settle a divorce, you must be together in the same map as the other individual.")] - s_wedding_result_err_same_field = 34, - [Description("Only available when engaged.")] - s_wedding_result_err_only_promise_user = 35, - [Description("This is an unknown wedding hall.")] - s_wedding_result_err_invalid_wedding_hall = 36, - [Description("Insufficent merets.")] - s_wedding_result_err_lack_meratal = 37, - [Description("There is a maintenance soon. Please make a reservation at a different time.")] - s_wedding_result_err_maintenance_time = 38, - [Description("Could not find the wedding hall.")] - s_wedding_result_not_find_weddinghall = 39, - [Description("The wedding invitation has already been sent.")] - s_wedding_result_already_send_invitation = 40, - [Description("The engagement has expired.")] - s_wedding_result_late_promise_time = 41, - [Description("It is too late to modify the reservation.")] - s_wedding_result_late_wedding_reserve_modify_time = 42, - [Description("You already have a wedding reservation.")] - s_wedding_result_already_reservation = 43, - [Description("There is a wedding already booked for this time.")] - s_wedding_result_already_same_reservation = 44, - [Description("You can only divorce if you are married.")] - s_wedding_result_invaild_state_divorce = 45, - [Description("Your marriage is too new and cannot file for divorce.")] - s_wedding_result_lack_marriage_date = 46, - [Description("You do not have enough mesos to divorce.")] - s_wedding_result_lack_meso_divorce = 47, - [Description("The divorce has been completed.")] - s_wedding_result_late_divorce_agree = 48, - [Description("Divorce consent can only be made by the person who asked for a divorce.")] - s_wedding_result_invalid_divorce_agree_user = 49, - [Description("Can only be used by married users.")] - s_wedding_result_only_marriage_user = 50, - [Description("You have exceeded the number of invitations you can send at one time.")] - s_wedding_result_wedding_invitation_count = 51, - [Description("You do not have enough mesos to send a wedding invitation.")] - s_wedding_result_lack_meso_invitation = 52, - [Description("Cannot send reminder not too long after a wedding.")] - s_wedding_result_early_remind_wedding = 53, - [Description("The reservation time is too early.")] - s_wedding_result_early_wedding_reserve = 54, - [Description("Forced divorce request cannot be canceled after the divorce meditation period has passed.")] - s_wedding_result_divorcecancel = 55, - [Description("You cannot enter the wedding at this time.")] - s_wedding_visit_failed_common = 56, - [Description("You cannot enter the wedding ceremony in this map.")] - s_wedding_visit_failed_disablemap = 57, - [Description("You cannot attend a wedding while you are tombstoned.")] - s_wedding_visit_failed_dead = 58, - [Description("You have already entered the wedding")] - s_wedding_visit_failed_same_place = 59, - [Description("You cannot enter the wedding ceremony while in a dungeon.")] - s_wedding_visit_failed_solo_instance = 60, - [Description("It is not possible to enter the wedding at this time.")] - s_wedding_visit_failed_invalid_time = 61, - [Description("Only the character who reserved the wedding hall can modify or cancel the reservation.")] - s_wedding_result_only_booking_character = 62, - [Description("A wedding hall has already been booked.")] - s_wedding_result_already_weddinghall_reserve = 63, - [Description("Not enough coupons")] - s_wedding_result_lack_coupon = 64, - [Description("System error. [{0}]")] - s_wedding_result_err_system = short.MaxValue, - -} +// ReSharper disable InconsistentNaming, IdentifierTypo + +using System.ComponentModel; + +namespace Maple2.Model.Error; + +public enum WeddingError : short { + none = 0, + [Description("You are now engaged to {0}")] + s_wedding_propose_ok = 1, + [Description("{0} declined your proposal.")] + s_wedding_result_decline_propose = 2, + [Description("You have proposed to {0}. \\nIf they accept, you both will be engaged.")] + s_wedding_msg_box_propose_to = 3, + [Description("You cancelled the proposal.")] + s_wedding_result_cancel_propose_to = 4, + [Description("{0} cancelled the proposal.")] + s_wedding_result_cancel_propose_from = 5, + [Description("The proposal has been cancelled because the waiting time has expired.")] + s_wedding_result_cancel_propose_expire = 6, + confirm_reservation = 7, // Unknown what string this is supposed to be + decline_reservation = 8, // Unknown what string this is supposed to be + accept_reservation_cancel = 9, // Unknown what string this is supposed to be + decline_reservation_cancel = 10, // Unknown what string this is supposed to be + accept_reservation_change = 11, // Unknown what string this is supposed to be + decline_reservation_change = 12, // Unknown what string this is supposed to be + [Description("You sent a request to your fiancee to confirm the wedding hall reservation. The reservation will be complete when they confirm.")] + s_wedding_result_send_wedding_hall_reservation = 15, + [Description("You have sent a request to your fiancee to change the wedding reservation time.\\nThe reservation will be complete when they confirm.")] + s_wedding_result_send_wedding_hall_reservation_change = 16, + [Description("You have sent a request to your fiancee to cancel the wedding reservation time.\\nOnce confirmed, the cancellation will be complete and a free reservation coupon will be given to book a wedding venue of the same grade in the future.")] + s_wedding_result_send_wedding_hall_reservation_cancel = 17, + s_wedding_result_err_wedding_block_age_to = 19, + s_wedding_result_err_wedding_block_age_from = 20, + [Description("You cannot propose to a character of the same gender.")] + s_wedding_result_err_wedding_block_gender = 21, + [Description("You cannot propose to {0} because you are not friends.\\nBecome friends in order to propose.")] + s_wedding_result_err_not_buddy = 22, + [Description("You are currently engaged and cannot propose to {0}.")] + s_wedding_result_err_propose_state_to = 23, + [Description("You are currently married and cannot propose to {0}.")] + s_wedding_result_err_marriage_state_to = 24, + [Description("You currently have a divorce in progress and cannot propose to {0}.")] + s_wedding_result_err_coolingoff_state_to = 25, + [Description("You have to wait 48 hours after a divorce to remarry.")] + s_wedding_result_err_prpose_cooltime_state_to = 26, + [Description("You cannot propose to a character who is engaged.")] + s_wedding_result_err_propose_state_from = 27, + [Description("You cannot propose to a married character.")] + s_wedding_result_err_mareriage_state_from = 28, + [Description("You cannot propose to a character with a divorce in progress.")] + s_wedding_result_err_coolingoff_state_from = 29, + [Description("You cannot propose to a character with less than 48 hours after divorce.")] + s_wedding_result_err_propose_cooltime_state_from = 30, + [Description("Both characters must have {0} in order to propose.")] + s_wedding_result_err_not_find_propose_item = 31, + [Description("Distance is too far to propose.")] + s_wedding_result_err_propose_distance = 32, + [Description("You have already been proposed.")] + s_wedding_result_err_already_propose_request = 33, + [Description("In order to be engaged, reserve a wedding hall, change reservation, or settle a divorce, you must be together in the same map as the other individual.")] + s_wedding_result_err_same_field = 34, + [Description("Only available when engaged.")] + s_wedding_result_err_only_promise_user = 35, + [Description("This is an unknown wedding hall.")] + s_wedding_result_err_invalid_wedding_hall = 36, + [Description("Insufficent merets.")] + s_wedding_result_err_lack_meratal = 37, + [Description("There is a maintenance soon. Please make a reservation at a different time.")] + s_wedding_result_err_maintenance_time = 38, + [Description("Could not find the wedding hall.")] + s_wedding_result_not_find_weddinghall = 39, + [Description("The wedding invitation has already been sent.")] + s_wedding_result_already_send_invitation = 40, + [Description("The engagement has expired.")] + s_wedding_result_late_promise_time = 41, + [Description("It is too late to modify the reservation.")] + s_wedding_result_late_wedding_reserve_modify_time = 42, + [Description("You already have a wedding reservation.")] + s_wedding_result_already_reservation = 43, + [Description("There is a wedding already booked for this time.")] + s_wedding_result_already_same_reservation = 44, + [Description("You can only divorce if you are married.")] + s_wedding_result_invaild_state_divorce = 45, + [Description("Your marriage is too new and cannot file for divorce.")] + s_wedding_result_lack_marriage_date = 46, + [Description("You do not have enough mesos to divorce.")] + s_wedding_result_lack_meso_divorce = 47, + [Description("The divorce has been completed.")] + s_wedding_result_late_divorce_agree = 48, + [Description("Divorce consent can only be made by the person who asked for a divorce.")] + s_wedding_result_invalid_divorce_agree_user = 49, + [Description("Can only be used by married users.")] + s_wedding_result_only_marriage_user = 50, + [Description("You have exceeded the number of invitations you can send at one time.")] + s_wedding_result_wedding_invitation_count = 51, + [Description("You do not have enough mesos to send a wedding invitation.")] + s_wedding_result_lack_meso_invitation = 52, + [Description("Cannot send reminder not too long after a wedding.")] + s_wedding_result_early_remind_wedding = 53, + [Description("The reservation time is too early.")] + s_wedding_result_early_wedding_reserve = 54, + [Description("Forced divorce request cannot be canceled after the divorce meditation period has passed.")] + s_wedding_result_divorcecancel = 55, + [Description("You cannot enter the wedding at this time.")] + s_wedding_visit_failed_common = 56, + [Description("You cannot enter the wedding ceremony in this map.")] + s_wedding_visit_failed_disablemap = 57, + [Description("You cannot attend a wedding while you are tombstoned.")] + s_wedding_visit_failed_dead = 58, + [Description("You have already entered the wedding")] + s_wedding_visit_failed_same_place = 59, + [Description("You cannot enter the wedding ceremony while in a dungeon.")] + s_wedding_visit_failed_solo_instance = 60, + [Description("It is not possible to enter the wedding at this time.")] + s_wedding_visit_failed_invalid_time = 61, + [Description("Only the character who reserved the wedding hall can modify or cancel the reservation.")] + s_wedding_result_only_booking_character = 62, + [Description("A wedding hall has already been booked.")] + s_wedding_result_already_weddinghall_reserve = 63, + [Description("Not enough coupons")] + s_wedding_result_lack_coupon = 64, + [Description("System error. [{0}]")] + s_wedding_result_err_system = short.MaxValue, + +} diff --git a/Maple2.Model/Game/Buddy.cs b/Maple2.Model/Game/Buddy.cs index 63112ac29..fb57f502e 100644 --- a/Maple2.Model/Game/Buddy.cs +++ b/Maple2.Model/Game/Buddy.cs @@ -1,104 +1,104 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class BuddyEntry { - public required long Id { get; init; } - public required long OwnerId { get; init; } - public required long BuddyId { get; init; } - public required long LastModified { get; init; } - - public string Message = string.Empty; - public BuddyType Type; - - public static implicit operator BuddyEntry(Buddy buddy) { - return new BuddyEntry { - Id = buddy.Id, - OwnerId = buddy.OwnerId, - BuddyId = buddy.Info.CharacterId, - LastModified = buddy.LastModified, - Message = buddy.Message, - Type = buddy.Type, - }; - } -} - -public class Buddy : IByteSerializable, IDisposable { - public readonly long Id; - public readonly long OwnerId; - public readonly long LastModified; - public readonly PlayerInfo Info; - - public string Message; - public BuddyType Type { get; private set; } - - public CancellationTokenSource? TokenSource; - - public Buddy(BuddyEntry entry, PlayerInfo info) { - Id = entry.Id; - OwnerId = entry.OwnerId; - LastModified = entry.LastModified; - Message = entry.Message; - Info = info.Clone(); - - SetType(entry.Type); - } - - public void SetType(BuddyType type) { - Type = type; - - // Remove any sensitive information from PlayerInfo - if (Type == BuddyType.Blocked) { - Info.Motto = string.Empty; - Info.Picture = string.Empty; - Info.Gender = Gender.Male; - Info.GearScore = 0; - Info.CurrentHp = 0; - Info.TotalHp = 0; - Info.MapId = 0; - Info.Channel = 0; - Info.HomeName = string.Empty; - Info.PlotMapId = 0; - Info.PlotNumber = 0; - Info.ApartmentNumber = 0; - Info.PlotExpiryTime = 0; - Info.AchievementInfo = default; - } - } - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(Id); - writer.WriteLong(Info.CharacterId); - writer.WriteLong(Info.AccountId); - writer.WriteUnicodeString(Info.Name); - writer.WriteUnicodeString(Type != BuddyType.Blocked ? Message : ""); - writer.WriteShort(Info.Channel); // Channel? - writer.WriteInt(Info.MapId); - writer.WriteInt((int) Info.Job.Code()); - writer.Write(Info.Job); - writer.WriteShort(Info.Level); - writer.WriteBool(Type.HasFlag(BuddyType.InRequest)); - writer.WriteBool(Type.HasFlag(BuddyType.OutRequest)); - writer.WriteBool(Type.HasFlag(BuddyType.Blocked)); - writer.WriteBool(Info.Online); - writer.WriteBool(false); - writer.WriteLong(LastModified); - writer.WriteUnicodeString(Info.Picture); - writer.WriteUnicodeString(Info.Motto); - writer.WriteUnicodeString(Type == BuddyType.Blocked ? Message : ""); - writer.WriteInt(Info.PlotMapId); - writer.WriteInt(Info.PlotNumber); - writer.WriteInt(Info.ApartmentNumber); - writer.WriteUnicodeString(Info.HomeName); - writer.WriteLong(Info.PlotExpiryTime); // Home expiry time? - writer.Write(Info.AchievementInfo); - } - - public void Dispose() { - TokenSource?.Cancel(); - TokenSource?.Dispose(); - TokenSource = null; - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class BuddyEntry { + public required long Id { get; init; } + public required long OwnerId { get; init; } + public required long BuddyId { get; init; } + public required long LastModified { get; init; } + + public string Message = string.Empty; + public BuddyType Type; + + public static implicit operator BuddyEntry(Buddy buddy) { + return new BuddyEntry { + Id = buddy.Id, + OwnerId = buddy.OwnerId, + BuddyId = buddy.Info.CharacterId, + LastModified = buddy.LastModified, + Message = buddy.Message, + Type = buddy.Type, + }; + } +} + +public class Buddy : IByteSerializable, IDisposable { + public readonly long Id; + public readonly long OwnerId; + public readonly long LastModified; + public readonly PlayerInfo Info; + + public string Message; + public BuddyType Type { get; private set; } + + public CancellationTokenSource? TokenSource; + + public Buddy(BuddyEntry entry, PlayerInfo info) { + Id = entry.Id; + OwnerId = entry.OwnerId; + LastModified = entry.LastModified; + Message = entry.Message; + Info = info.Clone(); + + SetType(entry.Type); + } + + public void SetType(BuddyType type) { + Type = type; + + // Remove any sensitive information from PlayerInfo + if (Type == BuddyType.Blocked) { + Info.Motto = string.Empty; + Info.Picture = string.Empty; + Info.Gender = Gender.Male; + Info.GearScore = 0; + Info.CurrentHp = 0; + Info.TotalHp = 0; + Info.MapId = 0; + Info.Channel = 0; + Info.HomeName = string.Empty; + Info.PlotMapId = 0; + Info.PlotNumber = 0; + Info.ApartmentNumber = 0; + Info.PlotExpiryTime = 0; + Info.AchievementInfo = default; + } + } + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(Id); + writer.WriteLong(Info.CharacterId); + writer.WriteLong(Info.AccountId); + writer.WriteUnicodeString(Info.Name); + writer.WriteUnicodeString(Type != BuddyType.Blocked ? Message : ""); + writer.WriteShort(Info.Channel); // Channel? + writer.WriteInt(Info.MapId); + writer.WriteInt((int) Info.Job.Code()); + writer.Write(Info.Job); + writer.WriteShort(Info.Level); + writer.WriteBool(Type.HasFlag(BuddyType.InRequest)); + writer.WriteBool(Type.HasFlag(BuddyType.OutRequest)); + writer.WriteBool(Type.HasFlag(BuddyType.Blocked)); + writer.WriteBool(Info.Online); + writer.WriteBool(false); + writer.WriteLong(LastModified); + writer.WriteUnicodeString(Info.Picture); + writer.WriteUnicodeString(Info.Motto); + writer.WriteUnicodeString(Type == BuddyType.Blocked ? Message : ""); + writer.WriteInt(Info.PlotMapId); + writer.WriteInt(Info.PlotNumber); + writer.WriteInt(Info.ApartmentNumber); + writer.WriteUnicodeString(Info.HomeName); + writer.WriteLong(Info.PlotExpiryTime); // Home expiry time? + writer.Write(Info.AchievementInfo); + } + + public void Dispose() { + TokenSource?.Cancel(); + TokenSource?.Dispose(); + TokenSource = null; + } +} diff --git a/Maple2.Model/Game/ChatSticker.cs b/Maple2.Model/Game/ChatSticker.cs index e4bc0596d..337972475 100644 --- a/Maple2.Model/Game/ChatSticker.cs +++ b/Maple2.Model/Game/ChatSticker.cs @@ -1,14 +1,14 @@ -using System.Runtime.InteropServices; - -namespace Maple2.Model.Game; - -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 12)] -public readonly struct ChatSticker { - public int Id { get; init; } - public long ExpiryTime { get; init; } - - public ChatSticker(int id, long expiration = long.MaxValue) { - Id = id; - ExpiryTime = expiration; - } -} +using System.Runtime.InteropServices; + +namespace Maple2.Model.Game; + +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 12)] +public readonly struct ChatSticker { + public int Id { get; init; } + public long ExpiryTime { get; init; } + + public ChatSticker(int id, long expiration = long.MaxValue) { + Id = id; + ExpiryTime = expiration; + } +} diff --git a/Maple2.Model/Game/Club/Club.cs b/Maple2.Model/Game/Club/Club.cs index 0efe4a1c8..6afda7bb1 100644 --- a/Maple2.Model/Game/Club/Club.cs +++ b/Maple2.Model/Game/Club/Club.cs @@ -1,49 +1,49 @@ -using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game.Club; - -public class Club : IByteSerializable { - - public long Id { get; init; } - public required string Name; - public required long LeaderId; - public ClubMember Leader; - public long CreationTime; - public ClubState State = ClubState.Staged; - public int BuffId; - public long NameChangeCooldown; - - public ConcurrentDictionary Members; - - [SetsRequiredMembers] - public Club(long id, string name, long leaderId) { - Id = id; - Name = name; - LeaderId = leaderId; - - Members = new ConcurrentDictionary(); - Leader = null!; - } - - [SetsRequiredMembers] - public Club(long id, string name, ClubMember leader) : this(id, name, leader.Info.CharacterId) { - Leader = leader; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(Id); - writer.WriteUnicodeString(Name); - writer.WriteLong(Leader.Info.AccountId); - writer.WriteLong(Leader.Info.CharacterId); - writer.WriteUnicodeString(Leader.Info.Name); - writer.WriteLong(CreationTime); - writer.Write(State); - writer.WriteInt(); - writer.WriteInt(); - writer.WriteLong(NameChangeCooldown); - } -} +using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game.Club; + +public class Club : IByteSerializable { + + public long Id { get; init; } + public required string Name; + public required long LeaderId; + public ClubMember Leader; + public long CreationTime; + public ClubState State = ClubState.Staged; + public int BuffId; + public long NameChangeCooldown; + + public ConcurrentDictionary Members; + + [SetsRequiredMembers] + public Club(long id, string name, long leaderId) { + Id = id; + Name = name; + LeaderId = leaderId; + + Members = new ConcurrentDictionary(); + Leader = null!; + } + + [SetsRequiredMembers] + public Club(long id, string name, ClubMember leader) : this(id, name, leader.Info.CharacterId) { + Leader = leader; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(Id); + writer.WriteUnicodeString(Name); + writer.WriteLong(Leader.Info.AccountId); + writer.WriteLong(Leader.Info.CharacterId); + writer.WriteUnicodeString(Leader.Info.Name); + writer.WriteLong(CreationTime); + writer.Write(State); + writer.WriteInt(); + writer.WriteInt(); + writer.WriteLong(NameChangeCooldown); + } +} diff --git a/Maple2.Model/Game/Club/ClubInvite.cs b/Maple2.Model/Game/Club/ClubInvite.cs index 146d298ed..7dac4a5fc 100644 --- a/Maple2.Model/Game/Club/ClubInvite.cs +++ b/Maple2.Model/Game/Club/ClubInvite.cs @@ -1,19 +1,19 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game.Club; - -public class ClubInvite : IByteSerializable { - - public long ClubId { get; init; } - public required string Name; - public required string LeaderName; - public required string Invitee; - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(ClubId); - writer.WriteUnicodeString(Name); - writer.WriteUnicodeString(LeaderName); - writer.WriteUnicodeString(Invitee); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game.Club; + +public class ClubInvite : IByteSerializable { + + public long ClubId { get; init; } + public required string Name; + public required string LeaderName; + public required string Invitee; + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(ClubId); + writer.WriteUnicodeString(Name); + writer.WriteUnicodeString(LeaderName); + writer.WriteUnicodeString(Invitee); + } +} diff --git a/Maple2.Model/Game/Club/ClubMember.cs b/Maple2.Model/Game/Club/ClubMember.cs index 8729a7aa9..a4c1324b6 100644 --- a/Maple2.Model/Game/Club/ClubMember.cs +++ b/Maple2.Model/Game/Club/ClubMember.cs @@ -1,54 +1,54 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game.Club; - -public class ClubMember : IByteSerializable, IDisposable { - public const byte TYPE = 2; - - public long ClubId { get; init; } - public required PlayerInfo Info; - public long AccountId => Info.AccountId; - public long CharacterId => Info.CharacterId; - public string Name => Info.Name; - public long JoinTime; - - public CancellationTokenSource? TokenSource; - - - public void Dispose() { - TokenSource?.Cancel(); - TokenSource?.Dispose(); - TokenSource = null; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteByte(TYPE); - writer.WriteLong(ClubId); - - WriteInfo(writer, this); - } - - public static void WriteInfo(IByteWriter writer, ClubMember member) { - PlayerInfo info = member.Info; - writer.WriteLong(info.AccountId); - writer.WriteLong(info.CharacterId); - writer.WriteUnicodeString(info.Name); - writer.Write(info.Gender); - writer.WriteInt((int) info.Job.Code()); - writer.Write(info.Job); - writer.WriteShort(info.Level); - writer.WriteInt(info.MapId); - writer.WriteShort(info.Channel); - writer.WriteUnicodeString(info.Picture); - writer.WriteInt(info.PlotMapId); - writer.WriteInt(info.PlotNumber); - writer.WriteInt(info.ApartmentNumber); - writer.WriteLong(info.PlotExpiryTime); - writer.Write(info.AchievementInfo); - writer.WriteLong(member.JoinTime); - writer.WriteLong(info.LastOnlineTime); - writer.WriteBool(!info.Online); - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game.Club; + +public class ClubMember : IByteSerializable, IDisposable { + public const byte TYPE = 2; + + public long ClubId { get; init; } + public required PlayerInfo Info; + public long AccountId => Info.AccountId; + public long CharacterId => Info.CharacterId; + public string Name => Info.Name; + public long JoinTime; + + public CancellationTokenSource? TokenSource; + + + public void Dispose() { + TokenSource?.Cancel(); + TokenSource?.Dispose(); + TokenSource = null; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteByte(TYPE); + writer.WriteLong(ClubId); + + WriteInfo(writer, this); + } + + public static void WriteInfo(IByteWriter writer, ClubMember member) { + PlayerInfo info = member.Info; + writer.WriteLong(info.AccountId); + writer.WriteLong(info.CharacterId); + writer.WriteUnicodeString(info.Name); + writer.Write(info.Gender); + writer.WriteInt((int) info.Job.Code()); + writer.Write(info.Job); + writer.WriteShort(info.Level); + writer.WriteInt(info.MapId); + writer.WriteShort(info.Channel); + writer.WriteUnicodeString(info.Picture); + writer.WriteInt(info.PlotMapId); + writer.WriteInt(info.PlotNumber); + writer.WriteInt(info.ApartmentNumber); + writer.WriteLong(info.PlotExpiryTime); + writer.Write(info.AchievementInfo); + writer.WriteLong(member.JoinTime); + writer.WriteLong(info.LastOnlineTime); + writer.WriteBool(!info.Online); + } +} diff --git a/Maple2.Model/Game/Config/KeyTable.cs b/Maple2.Model/Game/Config/KeyTable.cs index 4cc533b00..232ea056b 100644 --- a/Maple2.Model/Game/Config/KeyTable.cs +++ b/Maple2.Model/Game/Config/KeyTable.cs @@ -1,11 +1,11 @@ -using System.Runtime.InteropServices; - -namespace Maple2.Model.Game; - -[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 21)] -public readonly record struct KeyBind(int KeyCode, int OptionType, long OptionGuid, int Unknown1, byte Priority); - -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 16)] -public readonly record struct QuickSlot(int SkillId, int ItemId = 0, long ItemUid = 0) { - public QuickSlot(Item item) : this(0, item.Id, item.Uid) { } -} +using System.Runtime.InteropServices; + +namespace Maple2.Model.Game; + +[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 21)] +public readonly record struct KeyBind(int KeyCode, int OptionType, long OptionGuid, int Unknown1, byte Priority); + +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 16)] +public readonly record struct QuickSlot(int SkillId, int ItemId = 0, long ItemUid = 0) { + public QuickSlot(Item item) : this(0, item.Id, item.Uid) { } +} diff --git a/Maple2.Model/Game/Config/PetConfig.cs b/Maple2.Model/Game/Config/PetConfig.cs index 5716bd600..0a8c53693 100644 --- a/Maple2.Model/Game/Config/PetConfig.cs +++ b/Maple2.Model/Game/Config/PetConfig.cs @@ -1,33 +1,33 @@ -using System.Runtime.InteropServices; - -namespace Maple2.Model.Game; - -public class PetConfig { - public PetPotionConfig[] PotionConfig; - public PetLootConfig LootConfig; - - public PetConfig(PetPotionConfig[]? potionConfig = null, PetLootConfig? lootConfig = null) { - PotionConfig = potionConfig ?? []; - LootConfig = lootConfig ?? new PetLootConfig(true, true, true, true, true, true, true, false, 1, true); - } - -} - -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 12)] -public readonly record struct PetPotionConfig( - int Index, - float Threshold, - int ItemId); - -[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 13)] -public readonly record struct PetLootConfig( - bool Mesos, - bool Merets, - bool Other, - bool Currency, - bool Equipment, - bool Consumable, - bool Gemstone, - bool Dropped, - int Rarity, - bool Enabled); +using System.Runtime.InteropServices; + +namespace Maple2.Model.Game; + +public class PetConfig { + public PetPotionConfig[] PotionConfig; + public PetLootConfig LootConfig; + + public PetConfig(PetPotionConfig[]? potionConfig = null, PetLootConfig? lootConfig = null) { + PotionConfig = potionConfig ?? []; + LootConfig = lootConfig ?? new PetLootConfig(true, true, true, true, true, true, true, false, 1, true); + } + +} + +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 12)] +public readonly record struct PetPotionConfig( + int Index, + float Threshold, + int ItemId); + +[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 13)] +public readonly record struct PetLootConfig( + bool Mesos, + bool Merets, + bool Other, + bool Currency, + bool Equipment, + bool Consumable, + bool Gemstone, + bool Dropped, + int Rarity, + bool Enabled); diff --git a/Maple2.Model/Game/Config/SkillMacro.cs b/Maple2.Model/Game/Config/SkillMacro.cs index 5938a03c1..be042b4de 100644 --- a/Maple2.Model/Game/Config/SkillMacro.cs +++ b/Maple2.Model/Game/Config/SkillMacro.cs @@ -1,39 +1,39 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class SkillMacro : IByteSerializable, IByteDeserializable { - public string Name { get; private set; } - public long KeyId { get; private set; } - public IReadOnlyCollection Skills => skills; - - private HashSet skills; - - public SkillMacro(string name, long keyId, HashSet? skills = null) { - Name = name; - KeyId = keyId; - this.skills = skills ?? []; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteUnicodeString(Name); - writer.WriteLong(KeyId); - writer.WriteInt(Skills.Count); - - foreach (int skillId in Skills) { - writer.WriteInt(skillId); - } - } - - public void ReadFrom(IByteReader reader) { - skills = []; // Clear any existing settings - - Name = reader.ReadUnicodeString(); - KeyId = reader.ReadLong(); - int count = reader.ReadInt(); - for (int i = 0; i < count; i++) { - skills.Add(reader.ReadInt()); - } - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class SkillMacro : IByteSerializable, IByteDeserializable { + public string Name { get; private set; } + public long KeyId { get; private set; } + public IReadOnlyCollection Skills => skills; + + private HashSet skills; + + public SkillMacro(string name, long keyId, HashSet? skills = null) { + Name = name; + KeyId = keyId; + this.skills = skills ?? []; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteUnicodeString(Name); + writer.WriteLong(KeyId); + writer.WriteInt(Skills.Count); + + foreach (int skillId in Skills) { + writer.WriteInt(skillId); + } + } + + public void ReadFrom(IByteReader reader) { + skills = []; // Clear any existing settings + + Name = reader.ReadUnicodeString(); + KeyId = reader.ReadLong(); + int count = reader.ReadInt(); + for (int i = 0; i < count; i++) { + skills.Add(reader.ReadInt()); + } + } +} diff --git a/Maple2.Model/Game/Config/Wardrobe.cs b/Maple2.Model/Game/Config/Wardrobe.cs index 2265163f9..9414f8f8b 100644 --- a/Maple2.Model/Game/Config/Wardrobe.cs +++ b/Maple2.Model/Game/Config/Wardrobe.cs @@ -1,49 +1,49 @@ -using System.Runtime.InteropServices; -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class Wardrobe : IByteSerializable { - public int Type; - public int KeyId; - public string Name; - public readonly IDictionary Equips; - - public Wardrobe(int type, string name) { - Type = type; - Name = name; - Equips = new Dictionary(); - } - - public void WriteTo(IByteWriter writer) { - writer.WriteInt(Type); - writer.WriteInt(KeyId); - writer.WriteUnicodeString(Name); - - writer.WriteInt(Equips.Count); - foreach (Equip equip in Equips.Values) { - writer.Write(equip); - } - } - - [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 20)] - public readonly struct Equip { - public readonly long ItemUid; - public readonly int ItemId; - private readonly int Slot; - public readonly int Rarity; - - public EquipSlot EquipSlot => (EquipSlot) Slot; - - public Equip(long itemUid, int itemId, EquipSlot slot, int rarity) { - ItemUid = itemUid; - ItemId = itemId; - Slot = (int) slot; - Rarity = rarity; - } - - public override string ToString() => $"WardrobeEquip({ItemUid}, {ItemId}, {Slot}, {Rarity})"; - } -} +using System.Runtime.InteropServices; +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class Wardrobe : IByteSerializable { + public int Type; + public int KeyId; + public string Name; + public readonly IDictionary Equips; + + public Wardrobe(int type, string name) { + Type = type; + Name = name; + Equips = new Dictionary(); + } + + public void WriteTo(IByteWriter writer) { + writer.WriteInt(Type); + writer.WriteInt(KeyId); + writer.WriteUnicodeString(Name); + + writer.WriteInt(Equips.Count); + foreach (Equip equip in Equips.Values) { + writer.Write(equip); + } + } + + [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 20)] + public readonly struct Equip { + public readonly long ItemUid; + public readonly int ItemId; + private readonly int Slot; + public readonly int Rarity; + + public EquipSlot EquipSlot => (EquipSlot) Slot; + + public Equip(long itemUid, int itemId, EquipSlot slot, int rarity) { + ItemUid = itemUid; + ItemId = itemId; + Slot = (int) slot; + Rarity = rarity; + } + + public override string ToString() => $"WardrobeEquip({ItemUid}, {ItemId}, {Slot}, {Rarity})"; + } +} diff --git a/Maple2.Model/Game/Cube/ConfigurableCube.cs b/Maple2.Model/Game/Cube/ConfigurableCube.cs index 45b5018b3..d43aac65a 100644 --- a/Maple2.Model/Game/Cube/ConfigurableCube.cs +++ b/Maple2.Model/Game/Cube/ConfigurableCube.cs @@ -1,41 +1,41 @@ -using Maple2.Model.Common; -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class CubePortalSettings : IByteSerializable { - public string PortalName { get; set; } - public PortalActionType Method { get; set; } - public CubePortalDestination Destination { get; set; } - public string DestinationTarget { get; set; } - public int PortalObjectId { get; set; } - - public CubePortalSettings() { - PortalName = string.Empty; - DestinationTarget = string.Empty; - } - - public CubePortalSettings(Vector3B position) { - PortalName = $"Portal_{Math.Abs(position.X):D2}.{Math.Abs(position.Y):D2}.{Math.Abs(position.Z):D2}"; - DestinationTarget = string.Empty; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteUnicodeString(PortalName); - writer.WriteByte((byte) Method); - writer.Write(Destination); - writer.WriteUnicodeString(DestinationTarget); - } -} - -public class CubeNoticeSettings : IByteSerializable { - public string Notice { get; set; } = string.Empty; - public byte Distance { get; set; } = 1; - - public void WriteTo(IByteWriter writer) { - writer.WriteUnicodeString(Notice); - writer.WriteByte(Distance); - } -} +using Maple2.Model.Common; +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class CubePortalSettings : IByteSerializable { + public string PortalName { get; set; } + public PortalActionType Method { get; set; } + public CubePortalDestination Destination { get; set; } + public string DestinationTarget { get; set; } + public int PortalObjectId { get; set; } + + public CubePortalSettings() { + PortalName = string.Empty; + DestinationTarget = string.Empty; + } + + public CubePortalSettings(Vector3B position) { + PortalName = $"Portal_{Math.Abs(position.X):D2}.{Math.Abs(position.Y):D2}.{Math.Abs(position.Z):D2}"; + DestinationTarget = string.Empty; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteUnicodeString(PortalName); + writer.WriteByte((byte) Method); + writer.Write(Destination); + writer.WriteUnicodeString(DestinationTarget); + } +} + +public class CubeNoticeSettings : IByteSerializable { + public string Notice { get; set; } = string.Empty; + public byte Distance { get; set; } = 1; + + public void WriteTo(IByteWriter writer) { + writer.WriteUnicodeString(Notice); + writer.WriteByte(Distance); + } +} diff --git a/Maple2.Model/Game/Cube/GuideObject.cs b/Maple2.Model/Game/Cube/GuideObject.cs index a81a2c297..3a8154df0 100644 --- a/Maple2.Model/Game/Cube/GuideObject.cs +++ b/Maple2.Model/Game/Cube/GuideObject.cs @@ -1,69 +1,69 @@ -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public enum GuideObjectType : short { - Construction = 0, - Fishing = 1, - SkillGuide = 2, -} - -public interface IGuideObject : IByteSerializable { - public GuideObjectType Type { get; } -} - -public class ConstructionGuideObject : IGuideObject { - public GuideObjectType Type => GuideObjectType.Construction; - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(1); - } -} - -public class FishingGuideObject : IGuideObject { - public GuideObjectType Type => GuideObjectType.Fishing; - - public readonly FishingRodTable.Entry Rod; - public readonly FishTable.Spot Spot; - - public FishingGuideObject(FishingRodTable.Entry rod, FishTable.Spot spot) { - Rod = rod; - Spot = spot; - } - - public void WriteTo(IByteWriter writer) { } -} - -public class SkillMagicControlGuide : IGuideObject { - public GuideObjectType Type => GuideObjectType.SkillGuide; - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(); - writer.WriteInt(); - writer.WriteShort(); - writer.WriteByte(); - writer.WriteByte(); - writer.WriteInt(); - writer.WriteByte(); // count - // for (int i = 0; i < count; i++) { - // writer.WriteLong(); - // writer.WriteByte(); - // } - } -} - -public class BallGuideObject : IGuideObject { - public GuideObjectType Type => GuideObjectType.Construction; - - private readonly float size; - - public BallGuideObject(float size) { - this.size = size; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteFloat(size); - } -} +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public enum GuideObjectType : short { + Construction = 0, + Fishing = 1, + SkillGuide = 2, +} + +public interface IGuideObject : IByteSerializable { + public GuideObjectType Type { get; } +} + +public class ConstructionGuideObject : IGuideObject { + public GuideObjectType Type => GuideObjectType.Construction; + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(1); + } +} + +public class FishingGuideObject : IGuideObject { + public GuideObjectType Type => GuideObjectType.Fishing; + + public readonly FishingRodTable.Entry Rod; + public readonly FishTable.Spot Spot; + + public FishingGuideObject(FishingRodTable.Entry rod, FishTable.Spot spot) { + Rod = rod; + Spot = spot; + } + + public void WriteTo(IByteWriter writer) { } +} + +public class SkillMagicControlGuide : IGuideObject { + public GuideObjectType Type => GuideObjectType.SkillGuide; + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(); + writer.WriteInt(); + writer.WriteShort(); + writer.WriteByte(); + writer.WriteByte(); + writer.WriteInt(); + writer.WriteByte(); // count + // for (int i = 0; i < count; i++) { + // writer.WriteLong(); + // writer.WriteByte(); + // } + } +} + +public class BallGuideObject : IGuideObject { + public GuideObjectType Type => GuideObjectType.Construction; + + private readonly float size; + + public BallGuideObject(float size) { + this.size = size; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteFloat(size); + } +} diff --git a/Maple2.Model/Game/Cube/HeldCube.cs b/Maple2.Model/Game/Cube/HeldCube.cs index 514c1d917..451a7a7e0 100644 --- a/Maple2.Model/Game/Cube/HeldCube.cs +++ b/Maple2.Model/Game/Cube/HeldCube.cs @@ -1,37 +1,37 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game; - -public class HeldCube : IByteSerializable, IByteDeserializable { - public static readonly HeldCube Default = new(); - - public long Id { get; set; } - public int ItemId { get; protected set; } - public ItemType ItemType { get; protected set; } - - public UgcItemLook? Template { get; protected set; } - - public void WriteTo(IByteWriter writer) { - writer.WriteInt(ItemId); - writer.WriteLong(Id); - writer.WriteLong(); // expire timestamp for ugc item - - writer.WriteBool(Template != null); - if (Template != null) { - writer.WriteClass(Template); - } - } - - public void ReadFrom(IByteReader reader) { - ItemId = reader.ReadInt(); - Id = reader.ReadLong(); - reader.ReadLong(); - - bool hasTemplate = reader.ReadBool(); - if (hasTemplate) { - Template = reader.ReadClass(); - } - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game; + +public class HeldCube : IByteSerializable, IByteDeserializable { + public static readonly HeldCube Default = new(); + + public long Id { get; set; } + public int ItemId { get; protected set; } + public ItemType ItemType { get; protected set; } + + public UgcItemLook? Template { get; protected set; } + + public void WriteTo(IByteWriter writer) { + writer.WriteInt(ItemId); + writer.WriteLong(Id); + writer.WriteLong(); // expire timestamp for ugc item + + writer.WriteBool(Template != null); + if (Template != null) { + writer.WriteClass(Template); + } + } + + public void ReadFrom(IByteReader reader) { + ItemId = reader.ReadInt(); + Id = reader.ReadLong(); + reader.ReadLong(); + + bool hasTemplate = reader.ReadBool(); + if (hasTemplate) { + Template = reader.ReadClass(); + } + } +} diff --git a/Maple2.Model/Game/Cube/InteractCube.cs b/Maple2.Model/Game/Cube/InteractCube.cs index 9137b29ac..1541d7a19 100644 --- a/Maple2.Model/Game/Cube/InteractCube.cs +++ b/Maple2.Model/Game/Cube/InteractCube.cs @@ -1,55 +1,55 @@ -using Maple2.Model.Common; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game; - -public class InteractCube : IByteSerializable { - public string Id { get; set; } - public int ObjectCode { get; init; } - public readonly FunctionCubeMetadata Metadata; - public InteractCubeState State { get; set; } - - public Nurturing? Nurturing { get; set; } - public CubePortalSettings? PortalSettings { get; set; } - public CubeNoticeSettings? NoticeSettings { get; set; } - - public long InteractingCharacterId { get; set; } - - public InteractCube(Vector3B position, FunctionCubeMetadata metadata) { - Id = $"4_{position.ConvertToInt()}"; - Metadata = metadata; - ObjectCode = metadata.Id; - State = metadata.DefaultState; - - if (metadata.Nurturing is not null) { - Nurturing = new Nurturing(metadata.Nurturing); - } - - if (metadata.ConfigurableCubeType is ConfigurableCubeType.UGCPortal) { - PortalSettings = new CubePortalSettings(position); - } else if (metadata.ConfigurableCubeType is ConfigurableCubeType.UGCNotice) { - NoticeSettings = new CubeNoticeSettings(); - } - } - - public InteractCube(string id, FunctionCubeMetadata metadata, CubePortalSettings? portalSettings, CubeNoticeSettings? noticeSettings) { - Id = id; - Metadata = metadata; - ObjectCode = metadata.Id; - State = metadata.DefaultState; - PortalSettings = portalSettings; - NoticeSettings = noticeSettings; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteUnicodeString(Id); - writer.Write(State); - if (Nurturing is not null) { - writer.WriteClass(Nurturing); - } - } -} +using Maple2.Model.Common; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game; + +public class InteractCube : IByteSerializable { + public string Id { get; set; } + public int ObjectCode { get; init; } + public readonly FunctionCubeMetadata Metadata; + public InteractCubeState State { get; set; } + + public Nurturing? Nurturing { get; set; } + public CubePortalSettings? PortalSettings { get; set; } + public CubeNoticeSettings? NoticeSettings { get; set; } + + public long InteractingCharacterId { get; set; } + + public InteractCube(Vector3B position, FunctionCubeMetadata metadata) { + Id = $"4_{position.ConvertToInt()}"; + Metadata = metadata; + ObjectCode = metadata.Id; + State = metadata.DefaultState; + + if (metadata.Nurturing is not null) { + Nurturing = new Nurturing(metadata.Nurturing); + } + + if (metadata.ConfigurableCubeType is ConfigurableCubeType.UGCPortal) { + PortalSettings = new CubePortalSettings(position); + } else if (metadata.ConfigurableCubeType is ConfigurableCubeType.UGCNotice) { + NoticeSettings = new CubeNoticeSettings(); + } + } + + public InteractCube(string id, FunctionCubeMetadata metadata, CubePortalSettings? portalSettings, CubeNoticeSettings? noticeSettings) { + Id = id; + Metadata = metadata; + ObjectCode = metadata.Id; + State = metadata.DefaultState; + PortalSettings = portalSettings; + NoticeSettings = noticeSettings; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteUnicodeString(Id); + writer.Write(State); + if (Nurturing is not null) { + writer.WriteClass(Nurturing); + } + } +} diff --git a/Maple2.Model/Game/Cube/LiftableCube.cs b/Maple2.Model/Game/Cube/LiftableCube.cs index 28798ed85..9ad8df527 100644 --- a/Maple2.Model/Game/Cube/LiftableCube.cs +++ b/Maple2.Model/Game/Cube/LiftableCube.cs @@ -1,13 +1,13 @@ -using Maple2.Model.Metadata; - -namespace Maple2.Model.Game; - -public class LiftableCube : HeldCube { - public readonly Liftable Liftable; - - public LiftableCube(Liftable liftable) { - Liftable = liftable; - ItemId = liftable.ItemId; - Id = Random.Shared.NextInt64(); - } -} +using Maple2.Model.Metadata; + +namespace Maple2.Model.Game; + +public class LiftableCube : HeldCube { + public readonly Liftable Liftable; + + public LiftableCube(Liftable liftable) { + Liftable = liftable; + ItemId = liftable.ItemId; + Id = Random.Shared.NextInt64(); + } +} diff --git a/Maple2.Model/Game/Cube/LiftupWeapon.cs b/Maple2.Model/Game/Cube/LiftupWeapon.cs index 77c52b912..f7bc5c2a5 100644 --- a/Maple2.Model/Game/Cube/LiftupWeapon.cs +++ b/Maple2.Model/Game/Cube/LiftupWeapon.cs @@ -1,17 +1,17 @@ -using Maple2.Model.Metadata; - -namespace Maple2.Model.Game; - -public class LiftupWeapon { - public readonly ObjectWeapon Object; - public readonly int ItemId; - public readonly int SkillId; - public readonly short Level; - - public LiftupWeapon(ObjectWeapon @object, int itemId, int skillId, short level) { - Object = @object; - ItemId = itemId; - SkillId = skillId; - Level = level; - } -} +using Maple2.Model.Metadata; + +namespace Maple2.Model.Game; + +public class LiftupWeapon { + public readonly ObjectWeapon Object; + public readonly int ItemId; + public readonly int SkillId; + public readonly short Level; + + public LiftupWeapon(ObjectWeapon @object, int itemId, int skillId, short level) { + Object = @object; + ItemId = itemId; + SkillId = skillId; + Level = level; + } +} diff --git a/Maple2.Model/Game/Cube/Nurturing.cs b/Maple2.Model/Game/Cube/Nurturing.cs index faad1e1e5..96bb35bce 100644 --- a/Maple2.Model/Game/Cube/Nurturing.cs +++ b/Maple2.Model/Game/Cube/Nurturing.cs @@ -1,85 +1,85 @@ -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class Nurturing : IByteSerializable { - public long Exp { get; set; } - public DateTimeOffset LastFeedTime { get; set; } - - public short Stage { get; private set; } - public short ClaimedGiftForStage { get; set; } - public List PlayedBy { get; set; } - private DateTimeOffset CreationTime { get; set; } - - public readonly FunctionCubeMetadata.NurturingData NurturingMetadata; - - public Nurturing(FunctionCubeMetadata.NurturingData metadata) { - CreationTime = DateTimeOffset.Now; - NurturingMetadata = metadata; - Stage = 1; - ClaimedGiftForStage = 1; - PlayedBy = []; - } - - public Nurturing(long exp, short claimedGiftForStage, long[] playedBy, DateTimeOffset creationTime, DateTimeOffset lastFeedTime, FunctionCubeMetadata.NurturingData? metadata) { - NurturingMetadata = metadata ?? throw new ArgumentException("FunctionCubeMetadata does not have a Nurturing metadata."); - - Exp = exp; - Stage = 1; - ClaimedGiftForStage = claimedGiftForStage; - PlayedBy = playedBy.ToList(); - CreationTime = creationTime; - LastFeedTime = lastFeedTime; - - FunctionCubeMetadata.NurturingData.Growth[] requiredGrowth = metadata.RequiredGrowth; - if (exp >= requiredGrowth.Last().Exp) { - Stage = requiredGrowth.Last().Stage; - return; - } - - for (short i = 0; i < requiredGrowth.Length; i++) { - FunctionCubeMetadata.NurturingData.Growth growth = requiredGrowth[i]; - if (exp < growth.Exp) { - Stage = (short) (i + 1); - break; - } - } - } - - public void Feed() { - if (Exp >= NurturingMetadata.RequiredGrowth.Last().Exp) { - return; - } - - Exp += Constant.NurturingEatGrowth; - if (Exp >= NurturingMetadata.RequiredGrowth.First(x => x.Stage == Stage).Exp) { - Stage++; - } - LastFeedTime = DateTimeOffset.Now; - } - - public bool Play(long accountId) { - if (PlayedBy.Count >= Constant.NurturingPlayMaxCount) { - return false; - } - - if (PlayedBy.Contains(accountId)) { - return false; - } - - PlayedBy.Add(accountId); - Feed(); - return true; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(CreationTime.ToUnixTimeSeconds()); - writer.WriteLong(Exp); - writer.WriteShort(Stage); - writer.WriteShort(ClaimedGiftForStage); - writer.WriteShort(); // Unknown - writer.WriteLong(LastFeedTime.ToUnixTimeSeconds()); - } -} +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class Nurturing : IByteSerializable { + public long Exp { get; set; } + public DateTimeOffset LastFeedTime { get; set; } + + public short Stage { get; private set; } + public short ClaimedGiftForStage { get; set; } + public List PlayedBy { get; set; } + private DateTimeOffset CreationTime { get; set; } + + public readonly FunctionCubeMetadata.NurturingData NurturingMetadata; + + public Nurturing(FunctionCubeMetadata.NurturingData metadata) { + CreationTime = DateTimeOffset.Now; + NurturingMetadata = metadata; + Stage = 1; + ClaimedGiftForStage = 1; + PlayedBy = []; + } + + public Nurturing(long exp, short claimedGiftForStage, long[] playedBy, DateTimeOffset creationTime, DateTimeOffset lastFeedTime, FunctionCubeMetadata.NurturingData? metadata) { + NurturingMetadata = metadata ?? throw new ArgumentException("FunctionCubeMetadata does not have a Nurturing metadata."); + + Exp = exp; + Stage = 1; + ClaimedGiftForStage = claimedGiftForStage; + PlayedBy = playedBy.ToList(); + CreationTime = creationTime; + LastFeedTime = lastFeedTime; + + FunctionCubeMetadata.NurturingData.Growth[] requiredGrowth = metadata.RequiredGrowth; + if (exp >= requiredGrowth.Last().Exp) { + Stage = requiredGrowth.Last().Stage; + return; + } + + for (short i = 0; i < requiredGrowth.Length; i++) { + FunctionCubeMetadata.NurturingData.Growth growth = requiredGrowth[i]; + if (exp < growth.Exp) { + Stage = (short) (i + 1); + break; + } + } + } + + public void Feed() { + if (Exp >= NurturingMetadata.RequiredGrowth.Last().Exp) { + return; + } + + Exp += Constant.NurturingEatGrowth; + if (Exp >= NurturingMetadata.RequiredGrowth.First(x => x.Stage == Stage).Exp) { + Stage++; + } + LastFeedTime = DateTimeOffset.Now; + } + + public bool Play(long accountId) { + if (PlayedBy.Count >= Constant.NurturingPlayMaxCount) { + return false; + } + + if (PlayedBy.Contains(accountId)) { + return false; + } + + PlayedBy.Add(accountId); + Feed(); + return true; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(CreationTime.ToUnixTimeSeconds()); + writer.WriteLong(Exp); + writer.WriteShort(Stage); + writer.WriteShort(ClaimedGiftForStage); + writer.WriteShort(); // Unknown + writer.WriteLong(LastFeedTime.ToUnixTimeSeconds()); + } +} diff --git a/Maple2.Model/Game/Cube/PlotCube.cs b/Maple2.Model/Game/Cube/PlotCube.cs index fa645ce7d..4bb7a928d 100644 --- a/Maple2.Model/Game/Cube/PlotCube.cs +++ b/Maple2.Model/Game/Cube/PlotCube.cs @@ -1,25 +1,25 @@ -using Maple2.Model.Common; -using Maple2.Model.Metadata; - -namespace Maple2.Model.Game; - -public class PlotCube : HeldCube { - public enum CubeType { Default, Construction, Liftable }; - public readonly ItemMetadata Metadata; - - public Vector3B Position { get; set; } - public float Rotation { get; set; } - public required CubeType Type { get; set; } - - public int PlotId { get; set; } - - public InteractCube? Interact { get; set; } - - public PlotCube(ItemMetadata metadata, long id = 0, UgcItemLook? template = null) { - ItemId = metadata.Id; - Metadata = metadata; - ItemType = new ItemType(metadata.Id); - Id = id; - Template = template; - } -} +using Maple2.Model.Common; +using Maple2.Model.Metadata; + +namespace Maple2.Model.Game; + +public class PlotCube : HeldCube { + public enum CubeType { Default, Construction, Liftable }; + public readonly ItemMetadata Metadata; + + public Vector3B Position { get; set; } + public float Rotation { get; set; } + public required CubeType Type { get; set; } + + public int PlotId { get; set; } + + public InteractCube? Interact { get; set; } + + public PlotCube(ItemMetadata metadata, long id = 0, UgcItemLook? template = null) { + ItemId = metadata.Id; + Metadata = metadata; + ItemType = new ItemType(metadata.Id); + Id = id; + Template = template; + } +} diff --git a/Maple2.Model/Game/Dungeon/DungeonMission.cs b/Maple2.Model/Game/Dungeon/DungeonMission.cs index 84fd13c57..49de6f344 100644 --- a/Maple2.Model/Game/Dungeon/DungeonMission.cs +++ b/Maple2.Model/Game/Dungeon/DungeonMission.cs @@ -1,34 +1,34 @@ -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game.Dungeon; - -public class DungeonMission : IByteSerializable { - public readonly DungeonMissionMetadata Metadata; - public int Id => Metadata.Id; - public short Score { get; private set; } - public short Counter { get; private set; } - - public bool Update(int counter = 1) { - if (Counter >= Metadata.ApplyCount) { - return false; - } - - Counter += (short) counter; - float percentage = (float) Counter / Metadata.ApplyCount; - Score = (short) (percentage * Metadata.MaxScore); - return true; - } - - public void Complete() { - Counter = Metadata.ApplyCount; - Score = Metadata.MaxScore; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteInt(Id); - writer.WriteShort(Score); - writer.WriteShort(Counter); - } -} +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game.Dungeon; + +public class DungeonMission : IByteSerializable { + public readonly DungeonMissionMetadata Metadata; + public int Id => Metadata.Id; + public short Score { get; private set; } + public short Counter { get; private set; } + + public bool Update(int counter = 1) { + if (Counter >= Metadata.ApplyCount) { + return false; + } + + Counter += (short) counter; + float percentage = (float) Counter / Metadata.ApplyCount; + Score = (short) (percentage * Metadata.MaxScore); + return true; + } + + public void Complete() { + Counter = Metadata.ApplyCount; + Score = Metadata.MaxScore; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteInt(Id); + writer.WriteShort(Score); + writer.WriteShort(Counter); + } +} diff --git a/Maple2.Model/Game/Dungeon/DungeonRankReward.cs b/Maple2.Model/Game/Dungeon/DungeonRankReward.cs index b00fdd4ad..42183293f 100644 --- a/Maple2.Model/Game/Dungeon/DungeonRankReward.cs +++ b/Maple2.Model/Game/Dungeon/DungeonRankReward.cs @@ -1,19 +1,19 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game.Dungeon; - -public class DungeonRankReward : IByteSerializable { - public readonly int Id; - public int RankClaimed { get; set; } - public long UpdatedTimestamp { get; set; } - - public DungeonRankReward(int id) { - Id = id; - } - public void WriteTo(IByteWriter writer) { - writer.WriteInt(Id); - writer.WriteInt(RankClaimed); - writer.WriteLong(UpdatedTimestamp); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game.Dungeon; + +public class DungeonRankReward : IByteSerializable { + public readonly int Id; + public int RankClaimed { get; set; } + public long UpdatedTimestamp { get; set; } + + public DungeonRankReward(int id) { + Id = id; + } + public void WriteTo(IByteWriter writer) { + writer.WriteInt(Id); + writer.WriteInt(RankClaimed); + writer.WriteLong(UpdatedTimestamp); + } +} diff --git a/Maple2.Model/Game/Dungeon/DungeonRecord.cs b/Maple2.Model/Game/Dungeon/DungeonRecord.cs index 66aaf092e..c5351a374 100644 --- a/Maple2.Model/Game/Dungeon/DungeonRecord.cs +++ b/Maple2.Model/Game/Dungeon/DungeonRecord.cs @@ -1,43 +1,43 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game.Dungeon; - -public class DungeonRecord : IByteSerializable { - public readonly int DungeonId; - public byte UnionSubClears { get; set; } - public byte UnionClears { get; set; } - public long UnionSubCooldownTimestamp { get; set; } - public long UnionCooldownTimestamp { get; set; } - public long CooldownTimestamp { get; set; } - public long ClearTimestamp { get; set; } - public int TotalClears { get; set; } - public short LifetimeRecord { get; set; } - public short CurrentRecord { get; set; } - public byte ExtraSubClears { get; set; } - public byte ExtraClears { get; set; } - public DungeonRecordFlag Flag { get; set; } - - public DungeonRecord(int dungeonId) { - DungeonId = dungeonId; - LifetimeRecord = -1; - CurrentRecord = -1; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteInt(DungeonId); - writer.WriteLong(UnionCooldownTimestamp); - writer.WriteByte(UnionClears); - writer.WriteByte(UnionSubClears); - writer.WriteLong(UnionSubCooldownTimestamp); - writer.WriteByte(ExtraSubClears); - writer.WriteByte(ExtraClears); - writer.WriteLong(ClearTimestamp); - writer.WriteInt(TotalClears); - writer.WriteShort(LifetimeRecord); - writer.WriteLong(CooldownTimestamp); - writer.WriteShort(CurrentRecord); - writer.Write(Flag); - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game.Dungeon; + +public class DungeonRecord : IByteSerializable { + public readonly int DungeonId; + public byte UnionSubClears { get; set; } + public byte UnionClears { get; set; } + public long UnionSubCooldownTimestamp { get; set; } + public long UnionCooldownTimestamp { get; set; } + public long CooldownTimestamp { get; set; } + public long ClearTimestamp { get; set; } + public int TotalClears { get; set; } + public short LifetimeRecord { get; set; } + public short CurrentRecord { get; set; } + public byte ExtraSubClears { get; set; } + public byte ExtraClears { get; set; } + public DungeonRecordFlag Flag { get; set; } + + public DungeonRecord(int dungeonId) { + DungeonId = dungeonId; + LifetimeRecord = -1; + CurrentRecord = -1; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteInt(DungeonId); + writer.WriteLong(UnionCooldownTimestamp); + writer.WriteByte(UnionClears); + writer.WriteByte(UnionSubClears); + writer.WriteLong(UnionSubCooldownTimestamp); + writer.WriteByte(ExtraSubClears); + writer.WriteByte(ExtraClears); + writer.WriteLong(ClearTimestamp); + writer.WriteInt(TotalClears); + writer.WriteShort(LifetimeRecord); + writer.WriteLong(CooldownTimestamp); + writer.WriteShort(CurrentRecord); + writer.Write(Flag); + } +} diff --git a/Maple2.Model/Game/Dungeon/DungeonRoomRecord.cs b/Maple2.Model/Game/Dungeon/DungeonRoomRecord.cs index 1e8b3c315..7e0643005 100644 --- a/Maple2.Model/Game/Dungeon/DungeonRoomRecord.cs +++ b/Maple2.Model/Game/Dungeon/DungeonRoomRecord.cs @@ -1,17 +1,17 @@ -using System.Collections.Concurrent; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; - -namespace Maple2.Model.Game.Dungeon; - -public class DungeonRoomRecord { - public ConcurrentDictionary UserResults = []; - public readonly DungeonRoomMetadata Metadata; - public long StartTick { get; set; } - public long EndTick { get; set; } - public DungeonState State { get; set; } = DungeonState.None; - - public DungeonRoomRecord(DungeonRoomMetadata metadata) { - Metadata = metadata; - } -} +using System.Collections.Concurrent; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; + +namespace Maple2.Model.Game.Dungeon; + +public class DungeonRoomRecord { + public ConcurrentDictionary UserResults = []; + public readonly DungeonRoomMetadata Metadata; + public long StartTick { get; set; } + public long EndTick { get; set; } + public DungeonState State { get; set; } = DungeonState.None; + + public DungeonRoomRecord(DungeonRoomMetadata metadata) { + Metadata = metadata; + } +} diff --git a/Maple2.Model/Game/Dungeon/DungeonUserRecord.cs b/Maple2.Model/Game/Dungeon/DungeonUserRecord.cs index cc8b49eb9..d3adabce3 100644 --- a/Maple2.Model/Game/Dungeon/DungeonUserRecord.cs +++ b/Maple2.Model/Game/Dungeon/DungeonUserRecord.cs @@ -1,90 +1,90 @@ -using System.Collections.Concurrent; -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; - -namespace Maple2.Model.Game.Dungeon; - -public class DungeonUserRecord : IUserContentRecord { - public readonly int DungeonId; - public long CharacterId { get; init; } - public bool IsDungeonSuccess; - public bool WithParty { get; set; } - public readonly ConcurrentDictionary AccumulationRecords; - public Dictionary Missions = []; - public DungeonBonusFlag BonusFlag = DungeonBonusFlag.None; - public Dictionary Rewards { get; init; } = []; - public ICollection RewardItems { get; init; } = []; - public Dictionary BonusRewards { get; init; } = []; - public ICollection BonusRewardItems { get; init; } = []; - public int TotalSeconds; - public int Score = -1; - public int HighestScore = -1; - public DungeonBonusFlag Flag = DungeonBonusFlag.None; - public int Round; - - public DungeonUserRecord(int dungeonId, long characterId) { - DungeonId = dungeonId; - CharacterId = characterId; - AccumulationRecords = []; - var accumulationEnumList = new List((DungeonAccumulationRecordType[]) System.Enum.GetValues(typeof(DungeonAccumulationRecordType))); - foreach (DungeonAccumulationRecordType type in accumulationEnumList) { - AccumulationRecords[type] = 0; - } - var rewardTypeEnumList = new List((DungeonRewardType[]) System.Enum.GetValues(typeof(DungeonRewardType))); - foreach (DungeonRewardType type in rewardTypeEnumList) { - Rewards[type] = 0; - BonusRewards[type] = 0; - } - } - - public void Add(RewardRecord record) { - BonusRewards[DungeonRewardType.Exp] += (int) record.Exp; - BonusRewards[DungeonRewardType.Meso] += (int) record.Meso; - BonusRewards[DungeonRewardType.Prestige] += (int) record.PrestigeExp; - - foreach (RewardItem item in record.Items) { - BonusRewardItems.Add(item); - } - } - - public void WriteTo(IByteWriter writer) { - writer.WriteBool(IsDungeonSuccess); - writer.WriteInt(DungeonId); - writer.WriteBool(WithParty); - writer.WriteInt(TotalSeconds); - writer.WriteInt(HighestScore); - writer.WriteInt(Score); - writer.Write(BonusFlag); // Client reads this as 4 separate bytes. - writer.WriteInt(Rewards.Count); - foreach ((DungeonRewardType type, int value) in Rewards) { - writer.Write(type); - writer.WriteInt(value); - } - - writer.WriteInt(RewardItems.Count); - foreach (RewardItem item in RewardItems) { - writer.WriteInt(item.ItemId); - writer.WriteInt(item.Amount); - writer.WriteInt(item.Rarity); - writer.WriteBool(item.Unknown1); - writer.WriteBool(item.Unknown2); - writer.WriteBool(item.Unknown3); - } - - writer.WriteInt(BonusRewards.Count); - foreach ((DungeonRewardType type, int value) in BonusRewards) { - writer.Write(type); - writer.WriteInt(value); - } - - writer.WriteInt(BonusRewardItems.Count); - foreach (RewardItem item in BonusRewardItems) { - writer.WriteInt(item.ItemId); - writer.WriteInt(item.Amount); - writer.WriteInt(item.Rarity); - writer.WriteBool(item.Unknown1); - writer.WriteBool(item.Unknown2); - writer.WriteBool(item.Unknown3); - } - } -} +using System.Collections.Concurrent; +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; + +namespace Maple2.Model.Game.Dungeon; + +public class DungeonUserRecord : IUserContentRecord { + public readonly int DungeonId; + public long CharacterId { get; init; } + public bool IsDungeonSuccess; + public bool WithParty { get; set; } + public readonly ConcurrentDictionary AccumulationRecords; + public Dictionary Missions = []; + public DungeonBonusFlag BonusFlag = DungeonBonusFlag.None; + public Dictionary Rewards { get; init; } = []; + public ICollection RewardItems { get; init; } = []; + public Dictionary BonusRewards { get; init; } = []; + public ICollection BonusRewardItems { get; init; } = []; + public int TotalSeconds; + public int Score = -1; + public int HighestScore = -1; + public DungeonBonusFlag Flag = DungeonBonusFlag.None; + public int Round; + + public DungeonUserRecord(int dungeonId, long characterId) { + DungeonId = dungeonId; + CharacterId = characterId; + AccumulationRecords = []; + var accumulationEnumList = new List((DungeonAccumulationRecordType[]) System.Enum.GetValues(typeof(DungeonAccumulationRecordType))); + foreach (DungeonAccumulationRecordType type in accumulationEnumList) { + AccumulationRecords[type] = 0; + } + var rewardTypeEnumList = new List((DungeonRewardType[]) System.Enum.GetValues(typeof(DungeonRewardType))); + foreach (DungeonRewardType type in rewardTypeEnumList) { + Rewards[type] = 0; + BonusRewards[type] = 0; + } + } + + public void Add(RewardRecord record) { + BonusRewards[DungeonRewardType.Exp] += (int) record.Exp; + BonusRewards[DungeonRewardType.Meso] += (int) record.Meso; + BonusRewards[DungeonRewardType.Prestige] += (int) record.PrestigeExp; + + foreach (RewardItem item in record.Items) { + BonusRewardItems.Add(item); + } + } + + public void WriteTo(IByteWriter writer) { + writer.WriteBool(IsDungeonSuccess); + writer.WriteInt(DungeonId); + writer.WriteBool(WithParty); + writer.WriteInt(TotalSeconds); + writer.WriteInt(HighestScore); + writer.WriteInt(Score); + writer.Write(BonusFlag); // Client reads this as 4 separate bytes. + writer.WriteInt(Rewards.Count); + foreach ((DungeonRewardType type, int value) in Rewards) { + writer.Write(type); + writer.WriteInt(value); + } + + writer.WriteInt(RewardItems.Count); + foreach (RewardItem item in RewardItems) { + writer.WriteInt(item.ItemId); + writer.WriteInt(item.Amount); + writer.WriteInt(item.Rarity); + writer.WriteBool(item.Unknown1); + writer.WriteBool(item.Unknown2); + writer.WriteBool(item.Unknown3); + } + + writer.WriteInt(BonusRewards.Count); + foreach ((DungeonRewardType type, int value) in BonusRewards) { + writer.Write(type); + writer.WriteInt(value); + } + + writer.WriteInt(BonusRewardItems.Count); + foreach (RewardItem item in BonusRewardItems) { + writer.WriteInt(item.ItemId); + writer.WriteInt(item.Amount); + writer.WriteInt(item.Rarity); + writer.WriteBool(item.Unknown1); + writer.WriteBool(item.Unknown2); + writer.WriteBool(item.Unknown3); + } + } +} diff --git a/Maple2.Model/Game/Dungeon/DungeonUserResult.cs b/Maple2.Model/Game/Dungeon/DungeonUserResult.cs index f40ad359d..a4204f1a0 100644 --- a/Maple2.Model/Game/Dungeon/DungeonUserResult.cs +++ b/Maple2.Model/Game/Dungeon/DungeonUserResult.cs @@ -1,18 +1,18 @@ -using System.Runtime.InteropServices; -using Maple2.Model.Enum; - -namespace Maple2.Model.Game.Dungeon; - -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 16)] -public struct DungeonUserResult { - public readonly long CharacterId; - public readonly DungeonAccumulationRecordType RecordType; - public readonly int Value; - public DungeonMissionRank MissionRank = DungeonMissionRank.None; - - public DungeonUserResult(long characterId, DungeonAccumulationRecordType recordType, int value) { - CharacterId = characterId; - RecordType = recordType; - Value = value; - } -} +using System.Runtime.InteropServices; +using Maple2.Model.Enum; + +namespace Maple2.Model.Game.Dungeon; + +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 16)] +public struct DungeonUserResult { + public readonly long CharacterId; + public readonly DungeonAccumulationRecordType RecordType; + public readonly int Value; + public DungeonMissionRank MissionRank = DungeonMissionRank.None; + + public DungeonUserResult(long characterId, DungeonAccumulationRecordType recordType, int value) { + CharacterId = characterId; + RecordType = recordType; + Value = value; + } +} diff --git a/Maple2.Model/Game/Dungeon/IUserContentRecord.cs b/Maple2.Model/Game/Dungeon/IUserContentRecord.cs index 66343ad2f..cff107033 100644 --- a/Maple2.Model/Game/Dungeon/IUserContentRecord.cs +++ b/Maple2.Model/Game/Dungeon/IUserContentRecord.cs @@ -1,10 +1,10 @@ -using Maple2.Model.Enum; -using Maple2.Tools; - -namespace Maple2.Model.Game.Dungeon; - -public interface IUserContentRecord : IByteSerializable { - public long CharacterId { get; } - public Dictionary Rewards { get; } - public ICollection RewardItems { get; } -} +using Maple2.Model.Enum; +using Maple2.Tools; + +namespace Maple2.Model.Game.Dungeon; + +public interface IUserContentRecord : IByteSerializable { + public long CharacterId { get; } + public Dictionary Rewards { get; } + public ICollection RewardItems { get; } +} diff --git a/Maple2.Model/Game/Dungeon/MiniGameUserRecord.cs b/Maple2.Model/Game/Dungeon/MiniGameUserRecord.cs index 7a68f9027..f03b38eab 100644 --- a/Maple2.Model/Game/Dungeon/MiniGameUserRecord.cs +++ b/Maple2.Model/Game/Dungeon/MiniGameUserRecord.cs @@ -1,53 +1,53 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; - -namespace Maple2.Model.Game.Dungeon; - -public class MiniGameUserRecord : IUserContentRecord { - public long CharacterId { get; init; } - public Dictionary Rewards { get; init; } = []; - public ICollection RewardItems { get; init; } = []; - public int ClearedRounds { get; set; } - public required int MinRound { get; init; } - public required int TotalRounds { get; init; } - public required bool ShowResult { get; init; } - - public MiniGameUserRecord(long characterId) { - CharacterId = characterId; - var enumList = new List((DungeonRewardType[]) System.Enum.GetValues(typeof(DungeonRewardType))); - foreach (DungeonRewardType type in enumList) { - Rewards[type] = 0; - } - } - - public void Add(RewardRecord record) { - Rewards[DungeonRewardType.Exp] += (int) record.Exp; - Rewards[DungeonRewardType.Meso] += (int) record.Meso; - Rewards[DungeonRewardType.Prestige] += (int) record.PrestigeExp; - - foreach (RewardItem item in record.Items) { - RewardItems.Add(item); - } - } - - public void WriteTo(IByteWriter writer) { - writer.WriteInt(ClearedRounds); - writer.WriteInt(TotalRounds); - - writer.WriteInt(Rewards.Count); - foreach ((DungeonRewardType type, int value) in Rewards) { - writer.Write(type); - writer.WriteInt(value); - } - - writer.WriteInt(RewardItems.Count); - foreach (RewardItem item in RewardItems) { - writer.WriteInt(item.ItemId); - writer.WriteInt(item.Rarity); - writer.WriteInt(item.Amount); - writer.WriteBool(item.Unknown1); - writer.WriteBool(item.Unknown2); - writer.WriteBool(item.Unknown3); - } - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; + +namespace Maple2.Model.Game.Dungeon; + +public class MiniGameUserRecord : IUserContentRecord { + public long CharacterId { get; init; } + public Dictionary Rewards { get; init; } = []; + public ICollection RewardItems { get; init; } = []; + public int ClearedRounds { get; set; } + public required int MinRound { get; init; } + public required int TotalRounds { get; init; } + public required bool ShowResult { get; init; } + + public MiniGameUserRecord(long characterId) { + CharacterId = characterId; + var enumList = new List((DungeonRewardType[]) System.Enum.GetValues(typeof(DungeonRewardType))); + foreach (DungeonRewardType type in enumList) { + Rewards[type] = 0; + } + } + + public void Add(RewardRecord record) { + Rewards[DungeonRewardType.Exp] += (int) record.Exp; + Rewards[DungeonRewardType.Meso] += (int) record.Meso; + Rewards[DungeonRewardType.Prestige] += (int) record.PrestigeExp; + + foreach (RewardItem item in record.Items) { + RewardItems.Add(item); + } + } + + public void WriteTo(IByteWriter writer) { + writer.WriteInt(ClearedRounds); + writer.WriteInt(TotalRounds); + + writer.WriteInt(Rewards.Count); + foreach ((DungeonRewardType type, int value) in Rewards) { + writer.Write(type); + writer.WriteInt(value); + } + + writer.WriteInt(RewardItems.Count); + foreach (RewardItem item in RewardItems) { + writer.WriteInt(item.ItemId); + writer.WriteInt(item.Rarity); + writer.WriteInt(item.Amount); + writer.WriteBool(item.Unknown1); + writer.WriteBool(item.Unknown2); + writer.WriteBool(item.Unknown3); + } + } +} diff --git a/Maple2.Model/Game/Emote.cs b/Maple2.Model/Game/Emote.cs index 138d121e9..75fe3bdab 100644 --- a/Maple2.Model/Game/Emote.cs +++ b/Maple2.Model/Game/Emote.cs @@ -1,15 +1,15 @@ -using System.Runtime.InteropServices; - -namespace Maple2.Model.Game; - -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 16)] -public readonly struct Emote { - public readonly int Id; - public readonly int Level = 1; - public readonly long ExpiryTime; - - public Emote(int id, long expiryTime = 0) { - Id = id; - ExpiryTime = expiryTime; - } -} +using System.Runtime.InteropServices; + +namespace Maple2.Model.Game; + +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 16)] +public readonly struct Emote { + public readonly int Id; + public readonly int Level = 1; + public readonly long ExpiryTime; + + public Emote(int id, long expiryTime = 0) { + Id = id; + ExpiryTime = expiryTime; + } +} diff --git a/Maple2.Model/Game/EnchantRates.cs b/Maple2.Model/Game/EnchantRates.cs index d9773c537..8c57b427c 100644 --- a/Maple2.Model/Game/EnchantRates.cs +++ b/Maple2.Model/Game/EnchantRates.cs @@ -1,28 +1,28 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -// We store this data as ints, but write to packet as float. -// Using ints for computation avoids rounding errors. -public class EnchantRates : IByteSerializable { - public int Success; - public int Fodder; - public int Charge; - - public int Total => Success + Fodder + Charge; - - public void Clear() { - Success = 0; - Fodder = 0; - Charge = 0; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteFloat(Success); - writer.WriteFloat(); - writer.WriteFloat(); - writer.WriteFloat(Fodder); - writer.WriteFloat(Charge); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +// We store this data as ints, but write to packet as float. +// Using ints for computation avoids rounding errors. +public class EnchantRates : IByteSerializable { + public int Success; + public int Fodder; + public int Charge; + + public int Total => Success + Fodder + Charge; + + public void Clear() { + Success = 0; + Fodder = 0; + Charge = 0; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteFloat(Success); + writer.WriteFloat(); + writer.WriteFloat(); + writer.WriteFloat(Fodder); + writer.WriteFloat(Charge); + } +} diff --git a/Maple2.Model/Game/Event/GameEvent.cs b/Maple2.Model/Game/Event/GameEvent.cs index 2234c8880..816434d32 100644 --- a/Maple2.Model/Game/Event/GameEvent.cs +++ b/Maple2.Model/Game/Event/GameEvent.cs @@ -1,250 +1,250 @@ -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game.Event; - -public class GameEvent : IByteSerializable { - public GameEventMetadata Metadata { get; init; } - public int Id => Metadata.Id; - public string Name => Metadata.Type.ToString(); - public long StartTime => (long) (Metadata.StartTime.ToUniversalTime() - DateTime.UnixEpoch).TotalSeconds; - public long EndTime => (long) (Metadata.EndTime.ToUniversalTime() - DateTime.UnixEpoch).TotalSeconds; - - public GameEvent(GameEventMetadata metadata) { - Metadata = metadata; - } - - public bool IsActive() { - DateTimeOffset now = DateTimeOffset.UtcNow; - if (Metadata.StartTime > now) { - return false; - } - - if (Metadata.EndTime < now) { - return false; - } - - if (Metadata.ActiveDays.Length > 0 && !Metadata.ActiveDays.Contains(now.DayOfWeek)) { - return false; - } - - if (Metadata.StartPartTime != TimeSpan.Zero && Metadata.StartPartTime > now.TimeOfDay) { - return false; - } - - if (Metadata.EndPartTime != TimeSpan.Zero && Metadata.EndPartTime < now.TimeOfDay) { - return false; - } - - return true; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteUnicodeString(Name); - switch (Metadata.Data) { - case StringBoard stringBoard: - writer.WriteInt(Id); - writer.WriteInt(stringBoard.StringId); - writer.WriteUnicodeString(stringBoard.Text); - break; - case StringBoardLink stringBoardLink: - writer.WriteInt(Id); - writer.WriteUnicodeString(stringBoardLink.Link); - break; - case SaleChat saleChat: - writer.WriteInt(Id); - writer.WriteInt(saleChat.WorldChatDiscount); - writer.WriteInt(saleChat.ChannelChatDiscount); - break; - case EventFieldPopup eventFieldPopup: - writer.WriteInt(Id); - writer.WriteInt(eventFieldPopup.MapId); - break; - case TrafficOptimizer trafficOptimizer: - writer.WriteInt(Id); - writer.WriteInt(trafficOptimizer.GuideObjectSyncInterval); - writer.WriteInt(trafficOptimizer.RideSyncInterval); - writer.WriteInt(100); - writer.WriteInt(); - writer.WriteInt(trafficOptimizer.LinearMovementInterval); - writer.WriteInt(trafficOptimizer.UserSyncInterval); - writer.WriteInt(100); - break; - case BlueMarble blueMarble: - writer.WriteInt(Id); - writer.WriteInt(blueMarble.Rounds.Length); - foreach (BlueMarble.Round round in blueMarble.Rounds) { - writer.WriteInt(round.RoundCount); - writer.WriteInt(round.Item.ItemId); - writer.WriteByte((byte) round.Item.Rarity); - writer.WriteInt(round.Item.Amount); - } - break; - case AttendGift attendGift: - writer.WriteInt(Id); - writer.WriteLong(StartTime); - writer.WriteLong(EndTime); - writer.WriteUnicodeString(attendGift.Name); - writer.WriteString(attendGift.Link); - writer.WriteByte(); - writer.WriteBool(true); // disable claim button - writer.WriteInt(attendGift.RequiredPlaySeconds); - writer.WriteByte(); - writer.WriteInt(); - - var currencyType = AttendGiftCurrencyType.None; - writer.Write(currencyType); - if (currencyType != AttendGiftCurrencyType.None) { - writer.WriteInt(); // Skip Days Allowed - writer.WriteLong(); // Skip Day Cost - writer.WriteInt(); - } - - writer.WriteInt(attendGift.Items.Length); - foreach (RewardItem item in attendGift.Items) { - writer.Write(item); - } - break; - case MeretMarketNotice notice: - writer.WriteUnicodeString(notice.Text); - break; - case Rps rps: - writer.WriteInt(Id); - writer.WriteUnicodeString(rps.ActionsHtml); - writer.WriteInt(rps.Rewards.Length); - foreach (Rps.RewardData reward in rps.Rewards) { - writer.WriteInt(reward.PlayCount); - foreach (RewardItem item in reward.Rewards) { - writer.Write(item); - } - } - writer.WriteInt(rps.GameTicketId); - writer.WriteInt(Id); - writer.WriteLong(EndTime); - break; - case LobbyMap lobbyMap: - writer.WriteInt(Id); - writer.WriteInt(lobbyMap.MapId); - break; - case ReturnUser returnUser: - writer.WriteInt(Id); - writer.WriteInt(returnUser.SeasonId); // season? - writer.WriteLong(StartTime); - writer.WriteLong(EndTime); - writer.WriteInt(returnUser.QuestIds.Length); - foreach (int questId in returnUser.QuestIds) { - writer.WriteInt(questId); - } - break; - case LoginNotice: - break; - case FieldEffect fieldEffect: - writer.WriteInt(Id); - writer.WriteByte((byte) fieldEffect.MapIds.Length); - foreach (int mapId in fieldEffect.MapIds) { - writer.WriteInt(mapId); - } - break; - case QuestTag questTag: - writer.WriteInt(Id); - writer.WriteUnicodeString(questTag.Tag); - writer.WriteLong(StartTime); - writer.WriteLong(EndTime); - break; - case DTReward dtReward: - writer.WriteInt(Id); - writer.WriteUnicodeString(Metadata.Value1); - writer.WriteUnicodeString(Metadata.Value2); - writer.WriteUnicodeString(Metadata.Value3); - writer.WriteUnicodeString(Metadata.Value4); - break; - case ConstructShowItem constructShowItem: - writer.WriteInt(Id); - writer.WriteInt(constructShowItem.CategoryId); - writer.WriteUnicodeString(constructShowItem.CategoryName); - writer.WriteShort(); // likely value3 but unknown what it means - writer.WriteShort((short) constructShowItem.ItemIds.Length); - foreach (int itemId in constructShowItem.ItemIds) { - writer.WriteInt(itemId); - } - break; - case MassiveConstructionEvent massiveConstructionEvent: - writer.WriteInt(Id); - writer.WriteInt(massiveConstructionEvent.MapIds.Length); - foreach (int mapId in massiveConstructionEvent.MapIds) { - writer.WriteInt(mapId); - } - writer.WriteInt(); // this is a certain state. 1 = active, 0 = inactive ? - writer.WriteInt(); // account ids enabled to build in the specified maps - /* foreach (int accountId in accountIds) { - writer.WriteLong(accountId); - } */ - break; - case UGCMapContractSale ugcMapContractSale: - writer.WriteInt(Id); - writer.WriteInt(ugcMapContractSale.DiscountAmount); - break; - case UGCMapExtensionSale ugcMapExtensionSale: - writer.WriteInt(Id); - writer.WriteInt(ugcMapExtensionSale.DiscountAmount); - break; - case Gallery gallery: - writer.WriteInt(Id); - writer.WriteShort((short) gallery.QuestIds.Length); - foreach (int questId in gallery.QuestIds) { - writer.WriteInt(questId); - } - writer.WriteShort((short) gallery.RewardItems.Length); - foreach (RewardItem rewardItem in gallery.RewardItems) { - writer.Write(rewardItem); - } - writer.WriteInt(gallery.RevealDayLimit); - writer.WriteUnicodeString(gallery.Image); - writer.WriteLong(EndTime); - break; - case BingoEvent bingo: - writer.WriteInt(Id); - writer.WriteInt(bingo.Rewards.Length); - foreach (BingoEvent.BingoReward bingoReward in bingo.Rewards) { - writer.WriteInt(bingoReward.Items.Length); - foreach (RewardItem rewardItem in bingoReward.Items) { - writer.Write(rewardItem); - } - } - writer.WriteInt(bingo.PencilItemId); - writer.WriteInt(bingo.PencilPlusItemId); - break; - case TimeRunEvent timeRun: - writer.WriteInt(Id); - writer.WriteInt(timeRun.StartItemId); // Guess - - writer.WriteInt(timeRun.Quests.Sum(q => q.Distance)); - writer.WriteInt(timeRun.Quests.Length); - foreach (TimeRunEvent.Quest quest in timeRun.Quests) { - writer.WriteInt(quest.Id); - writer.WriteInt(quest.Distance); - writer.WriteShort((short) quest.OpeningDay); - } - - writer.Write(timeRun.FinalReward); - writer.WriteInt(timeRun.StepRewards.Count); - foreach ((int steps, RewardItem rewardItem) in timeRun.StepRewards) { - writer.WriteInt(steps); - writer.Write(rewardItem); - } - break; - case SaleAutoFishing fishing: - writer.WriteInt(Id); - writer.WriteInt(fishing.Discount); - writer.WriteUnicodeString(fishing.ContentType); - break; - case SaleAutoPlayInstrument instrument: - writer.WriteInt(Id); - writer.WriteInt(); - writer.WriteUnicodeString(instrument.ContentType); - break; - } - } -} +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game.Event; + +public class GameEvent : IByteSerializable { + public GameEventMetadata Metadata { get; init; } + public int Id => Metadata.Id; + public string Name => Metadata.Type.ToString(); + public long StartTime => (long) (Metadata.StartTime.ToUniversalTime() - DateTime.UnixEpoch).TotalSeconds; + public long EndTime => (long) (Metadata.EndTime.ToUniversalTime() - DateTime.UnixEpoch).TotalSeconds; + + public GameEvent(GameEventMetadata metadata) { + Metadata = metadata; + } + + public bool IsActive() { + DateTimeOffset now = DateTimeOffset.UtcNow; + if (Metadata.StartTime > now) { + return false; + } + + if (Metadata.EndTime < now) { + return false; + } + + if (Metadata.ActiveDays.Length > 0 && !Metadata.ActiveDays.Contains(now.DayOfWeek)) { + return false; + } + + if (Metadata.StartPartTime != TimeSpan.Zero && Metadata.StartPartTime > now.TimeOfDay) { + return false; + } + + if (Metadata.EndPartTime != TimeSpan.Zero && Metadata.EndPartTime < now.TimeOfDay) { + return false; + } + + return true; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteUnicodeString(Name); + switch (Metadata.Data) { + case StringBoard stringBoard: + writer.WriteInt(Id); + writer.WriteInt(stringBoard.StringId); + writer.WriteUnicodeString(stringBoard.Text); + break; + case StringBoardLink stringBoardLink: + writer.WriteInt(Id); + writer.WriteUnicodeString(stringBoardLink.Link); + break; + case SaleChat saleChat: + writer.WriteInt(Id); + writer.WriteInt(saleChat.WorldChatDiscount); + writer.WriteInt(saleChat.ChannelChatDiscount); + break; + case EventFieldPopup eventFieldPopup: + writer.WriteInt(Id); + writer.WriteInt(eventFieldPopup.MapId); + break; + case TrafficOptimizer trafficOptimizer: + writer.WriteInt(Id); + writer.WriteInt(trafficOptimizer.GuideObjectSyncInterval); + writer.WriteInt(trafficOptimizer.RideSyncInterval); + writer.WriteInt(100); + writer.WriteInt(); + writer.WriteInt(trafficOptimizer.LinearMovementInterval); + writer.WriteInt(trafficOptimizer.UserSyncInterval); + writer.WriteInt(100); + break; + case BlueMarble blueMarble: + writer.WriteInt(Id); + writer.WriteInt(blueMarble.Rounds.Length); + foreach (BlueMarble.Round round in blueMarble.Rounds) { + writer.WriteInt(round.RoundCount); + writer.WriteInt(round.Item.ItemId); + writer.WriteByte((byte) round.Item.Rarity); + writer.WriteInt(round.Item.Amount); + } + break; + case AttendGift attendGift: + writer.WriteInt(Id); + writer.WriteLong(StartTime); + writer.WriteLong(EndTime); + writer.WriteUnicodeString(attendGift.Name); + writer.WriteString(attendGift.Link); + writer.WriteByte(); + writer.WriteBool(true); // disable claim button + writer.WriteInt(attendGift.RequiredPlaySeconds); + writer.WriteByte(); + writer.WriteInt(); + + var currencyType = AttendGiftCurrencyType.None; + writer.Write(currencyType); + if (currencyType != AttendGiftCurrencyType.None) { + writer.WriteInt(); // Skip Days Allowed + writer.WriteLong(); // Skip Day Cost + writer.WriteInt(); + } + + writer.WriteInt(attendGift.Items.Length); + foreach (RewardItem item in attendGift.Items) { + writer.Write(item); + } + break; + case MeretMarketNotice notice: + writer.WriteUnicodeString(notice.Text); + break; + case Rps rps: + writer.WriteInt(Id); + writer.WriteUnicodeString(rps.ActionsHtml); + writer.WriteInt(rps.Rewards.Length); + foreach (Rps.RewardData reward in rps.Rewards) { + writer.WriteInt(reward.PlayCount); + foreach (RewardItem item in reward.Rewards) { + writer.Write(item); + } + } + writer.WriteInt(rps.GameTicketId); + writer.WriteInt(Id); + writer.WriteLong(EndTime); + break; + case LobbyMap lobbyMap: + writer.WriteInt(Id); + writer.WriteInt(lobbyMap.MapId); + break; + case ReturnUser returnUser: + writer.WriteInt(Id); + writer.WriteInt(returnUser.SeasonId); // season? + writer.WriteLong(StartTime); + writer.WriteLong(EndTime); + writer.WriteInt(returnUser.QuestIds.Length); + foreach (int questId in returnUser.QuestIds) { + writer.WriteInt(questId); + } + break; + case LoginNotice: + break; + case FieldEffect fieldEffect: + writer.WriteInt(Id); + writer.WriteByte((byte) fieldEffect.MapIds.Length); + foreach (int mapId in fieldEffect.MapIds) { + writer.WriteInt(mapId); + } + break; + case QuestTag questTag: + writer.WriteInt(Id); + writer.WriteUnicodeString(questTag.Tag); + writer.WriteLong(StartTime); + writer.WriteLong(EndTime); + break; + case DTReward dtReward: + writer.WriteInt(Id); + writer.WriteUnicodeString(Metadata.Value1); + writer.WriteUnicodeString(Metadata.Value2); + writer.WriteUnicodeString(Metadata.Value3); + writer.WriteUnicodeString(Metadata.Value4); + break; + case ConstructShowItem constructShowItem: + writer.WriteInt(Id); + writer.WriteInt(constructShowItem.CategoryId); + writer.WriteUnicodeString(constructShowItem.CategoryName); + writer.WriteShort(); // likely value3 but unknown what it means + writer.WriteShort((short) constructShowItem.ItemIds.Length); + foreach (int itemId in constructShowItem.ItemIds) { + writer.WriteInt(itemId); + } + break; + case MassiveConstructionEvent massiveConstructionEvent: + writer.WriteInt(Id); + writer.WriteInt(massiveConstructionEvent.MapIds.Length); + foreach (int mapId in massiveConstructionEvent.MapIds) { + writer.WriteInt(mapId); + } + writer.WriteInt(); // this is a certain state. 1 = active, 0 = inactive ? + writer.WriteInt(); // account ids enabled to build in the specified maps + /* foreach (int accountId in accountIds) { + writer.WriteLong(accountId); + } */ + break; + case UGCMapContractSale ugcMapContractSale: + writer.WriteInt(Id); + writer.WriteInt(ugcMapContractSale.DiscountAmount); + break; + case UGCMapExtensionSale ugcMapExtensionSale: + writer.WriteInt(Id); + writer.WriteInt(ugcMapExtensionSale.DiscountAmount); + break; + case Gallery gallery: + writer.WriteInt(Id); + writer.WriteShort((short) gallery.QuestIds.Length); + foreach (int questId in gallery.QuestIds) { + writer.WriteInt(questId); + } + writer.WriteShort((short) gallery.RewardItems.Length); + foreach (RewardItem rewardItem in gallery.RewardItems) { + writer.Write(rewardItem); + } + writer.WriteInt(gallery.RevealDayLimit); + writer.WriteUnicodeString(gallery.Image); + writer.WriteLong(EndTime); + break; + case BingoEvent bingo: + writer.WriteInt(Id); + writer.WriteInt(bingo.Rewards.Length); + foreach (BingoEvent.BingoReward bingoReward in bingo.Rewards) { + writer.WriteInt(bingoReward.Items.Length); + foreach (RewardItem rewardItem in bingoReward.Items) { + writer.Write(rewardItem); + } + } + writer.WriteInt(bingo.PencilItemId); + writer.WriteInt(bingo.PencilPlusItemId); + break; + case TimeRunEvent timeRun: + writer.WriteInt(Id); + writer.WriteInt(timeRun.StartItemId); // Guess + + writer.WriteInt(timeRun.Quests.Sum(q => q.Distance)); + writer.WriteInt(timeRun.Quests.Length); + foreach (TimeRunEvent.Quest quest in timeRun.Quests) { + writer.WriteInt(quest.Id); + writer.WriteInt(quest.Distance); + writer.WriteShort((short) quest.OpeningDay); + } + + writer.Write(timeRun.FinalReward); + writer.WriteInt(timeRun.StepRewards.Count); + foreach ((int steps, RewardItem rewardItem) in timeRun.StepRewards) { + writer.WriteInt(steps); + writer.Write(rewardItem); + } + break; + case SaleAutoFishing fishing: + writer.WriteInt(Id); + writer.WriteInt(fishing.Discount); + writer.WriteUnicodeString(fishing.ContentType); + break; + case SaleAutoPlayInstrument instrument: + writer.WriteInt(Id); + writer.WriteInt(); + writer.WriteUnicodeString(instrument.ContentType); + break; + } + } +} diff --git a/Maple2.Model/Game/Field/FieldAccelerationStructure.cs b/Maple2.Model/Game/Field/FieldAccelerationStructure.cs index 3031312ad..24762faaa 100644 --- a/Maple2.Model/Game/Field/FieldAccelerationStructure.cs +++ b/Maple2.Model/Game/Field/FieldAccelerationStructure.cs @@ -1,1012 +1,1012 @@ -using Maple2.Model.Common; -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; -using Maple2.Tools.VectorMath; -using System.Numerics; -using System.Runtime.InteropServices; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.Model.Metadata.FieldEntity; - -namespace Maple2.Model.Game.Field; - -[Flags] -internal enum FieldEntityMembers : byte { - None = 0x0, - Id = 0x1, - Position = 0x2, - Rotation = 0x4, - Scale = 0x8, - Bounds = 0x10, - Llid = 0x20, -} - -/* - * FieldAccelerationStructure is a helper class that specializes in various spatial queries for objects. - * It's designed to maximize performance for finding objects in a 3D space with desired constraints. - * - * Internally it uses specialized storage techniques with fast look up times, but knowledge on these - * techniques isn't necessary for use. The Query functions allow you to find the objects you need without - * knowing any of the implementation details. - * - * You can do general queries or queries for specific entity types for all types of queries available. - * Every entity matching the query constraints will be reported back through a callback, or in a list with - * Query___List() methods. - * - * Available query types include: - * - Cell: Captures all entities in a specific grid cell, or range of cells. Doesn't capture freely floating entities. - * - Point: Captures all entities intersecting with a specific point. - * - CellAtPoint: Captures all entities in the grid cell intersecting with a specific point. Doesn't capture freely floating entities. - * - TreeAtPoint: Captures all entities in the AABB tree intersecting with a specific point. Doesn't capture cells. - * - Box: Captures all entities intersecting with a bounding box. - * - CellsInBox: Captures all entities in grid cells intersecting with a bounding box. Doesn't capture freely floating entities. - * - TreeInBox: Captures all entities in the AABB tree intersecting with a bounding box. Doesn't capture cells. - * - Sphere: Captures all entities intersecting with a sphere. - * - CellsInSphere: Captures all entities in grid cells intersecting with a sphere. Doesn't capture freely floating entities. - * - TreeInSphere: Captures all entities in the AABB tree intersecting with a sphere. Doesn't capture cells. - * - Ray: Captures all entities intersecting with a ray. Object results may not be in order. - * - CellsOnRay: Captures all entities intersecting with a ray. Object results may not be in order. Doesn't capture freely floating entities. - * - TreeOnRay: Captures all entities in the AABB tree intersecting with a ray. Doesn't capture cells. - * - RayCast: Captures all boxes & meshes intersecting with a ray in order until false is returned by the callback. - * - CellRayCast: Captures all boxes & meshes intersecting with a ray in order until -1 is returned by the callback. Doesn't capture freely floating entities. - * - TreeRayCast: Captures all entities in the AABB tree intersecting with a ray in order until -1 is returned by the callback. Doesn't capture cells. - * - Frustum: Captures all entities overlapping with a frustum. Useful for map rendering. - * - CellsInFrustum: Captures all entities overlapping with a frustum. Useful for map rendering. Doesn't capture freely floating entities. - * - TreeInFrustum: Captures all entities overlapping with a frustum. Useful for map rendering. Doesn't capture cells. - * - * Special purpose queries: - * - Spawns: Captures all mob spawn candidates in a sphere. - * - Fluids: Captures all fluids within a bounding box. - * - VibrateObjects: Captures all vibrate objects within a bounding box. - */ -public class FieldAccelerationStructure : IByteSerializable, IByteDeserializable { - public const int AXIS_TRIM_ENTITY_COUNT = 10; - public const float BLOCK_SIZE = (float) Constant.BlockSize; - public const float HALF_BLOCK = 0.5f * BLOCK_SIZE; - - public Vector3S GridSize { get; private set; } = new Vector3S(); - public Vector3S MinIndex { get; private set; } = new Vector3S(); - public Vector3S MaxIndex { get; private set; } = new Vector3S(); - - public ReadOnlySpan AlignedEntities => CollectionsMarshal.AsSpan(alignedEntities); - public ReadOnlySpan AlignedTrimmedEntities => CollectionsMarshal.AsSpan(alignedTrimmedEntities); - public ReadOnlySpan UnalignedEntities => CollectionsMarshal.AsSpan(unalignedEntities); - // Make a list of vibrate objects on the field with the same size & order as this list - // Then in queries use field.VibrateObjects[vibrateEntity.VibrateIndex] to retrieve the right one - public ReadOnlySpan VibrateEntities => CollectionsMarshal.AsSpan(vibrateEntities); - - private readonly List alignedEntities = []; - private readonly List alignedTrimmedEntities = []; - private List unalignedEntities = []; // TODO: add AABB tree implementation for querying unaligned objects - private readonly List vibrateEntities = []; - private int[,,] cellGrid = new int[0, 0, 0]; - - public ulong GridBytesWritten { get; private set; } = 0; - - public FieldAccelerationStructure() { } - - public static Vector3S PointToCell(Vector3 point) { - point *= (1 / BLOCK_SIZE); - return new Vector3S((short) Math.Floor(point.X + 0.5f), (short) Math.Floor(point.Y + 0.5f), (short) Math.Floor(point.Z)); - } - - #region QueryApi - public void QueryCells(Vector3 min, Vector3 max, Action callback) { - Vector3S minIndex = PointToCell(min) - MinIndex; - Vector3S maxIndex = PointToCell(max) - MinIndex; - - for (short x = short.Max(0, minIndex.X); x < short.Min((short) (maxIndex.X + 1), GridSize.X); ++x) { - for (short y = short.Max(0, minIndex.Y); y < short.Min((short) (maxIndex.Y + 1), GridSize.Y); ++y) { - for (short z = short.Max(0, minIndex.Z); z < short.Min((short) (maxIndex.Z + 1), GridSize.Z); ++z) { - (byte count, int startIndex) = GetCellInfo(cellGrid[x, y, z]); - - for (byte i = 0; i < count; ++i) { - callback(alignedEntities[startIndex + i]); - } - } - } - } - - // TODO: query aabb tree - foreach (FieldEntity entity in alignedTrimmedEntities) { - if (entity.Bounds.Intersects(new BoundingBox3(min, max))) { - if (entity is FieldCellEntities cell) { - foreach (FieldEntity child in cell.Entities) { - callback(child); - } - - continue; - } - - callback(entity); - } - } - } - - public void QueryTreeInBox(Vector3 min, Vector3 max, Action callback) { - // TODO: query aabb tree - foreach (FieldEntity entity in unalignedEntities) { - if (entity.Bounds.Intersects(new BoundingBox3(min, max))) { - callback(entity); - } - } - } - - public void QueryBox(Vector3 min, Vector3 max, Action callback) { - QueryCells(min, max, callback); - QueryTreeInBox(min, max, callback); - } - - public void CellsInSphere(Vector3 center, float radius, Action callback) { - QueryCells(center - new Vector3(radius, radius, radius), center + new Vector3(radius, radius, radius), entity => { - if (entity.Bounds.IntersectsSphere(center, radius)) { - callback(entity); - } - }); - } - - public void QuerySpawns(Vector3 center, float radius, Action callback) { - CellsInSphere(center, radius, entity => { - if (entity is FieldSpawnTile spawn) { - callback(spawn); - } - }); - } - - public List QuerySpawnsList(Vector3 center, float radius) { - List spawns = []; - - QuerySpawns(center, radius, spawns.Add); - - return spawns; - } - - public void QueryFluids(BoundingBox3 box, Action callback) { - QueryFluids(box.Min, box.Max, callback); - } - - public List QueryFluidsList(BoundingBox3 box) { - List fluids = []; - - QueryFluids(box, fluids.Add); - - return fluids; - } - - public void QueryFluids(Vector3 min, Vector3 max, Action callback) { - QueryCells(min, max, entity => { - if (entity is FieldFluidEntity { IsSurface: true, IsShallow: false } fluid) { - callback(fluid); - } - }); - } - - public void QuerySellableTiles(Vector3 min, Vector3 max, Action callback) { - QueryCells(min, max, entity => { - if (entity is FieldSellableTile tile) { - callback(tile); - } - }); - } - - /// - /// Finds the first sellable tile at the specified position that matches the predicate. - /// - /// The position to search for the sellable tile. - /// A function to test each sellable tile for a condition. - /// The first sellable tile that matches the predicate, or null if no such tile is found. - public FieldSellableTile? FirstSellableTile(Vector3 position, Func predicate) { - Vector3 size = new Vector3(0, 0, 3000); // Only search in same column, we are looking for ground tiles - Vector3 min = position - 0.5f * size; - Vector3 max = position + 0.5f * size; - FieldSellableTile? result = null; - QueryCells(min, max, entity => { - if (result is not null) return; - if (entity is not FieldSellableTile tile || !predicate(tile)) return; - result = tile; - }); - return result; - } - - public void QuerySellableTilesCenter(Vector3 center, Vector3 size, Action callback) { - QuerySellableTiles(center - 0.5f * size, center + 0.5f * size, callback); - } - - public void QueryBoxCollider(Vector3 center, float radius, Action callback) { - QueryCells(center - new Vector3(radius, radius, radius), center + new Vector3(radius, radius, radius), entity => { - if (entity is FieldBoxColliderEntity collider) { - callback(collider); - } - }); - } - - public void FindBlockUnderPlayer(Vector3 position, Action callback) { - position = position with { - Z = position.Z - BLOCK_SIZE, - }; - position = position.Align(); - QueryCells(position, position, callback); - } - - public List QueryFluidsList(Vector3 min, Vector3 max) { - List fluids = []; - - QueryFluids(min, max, fluids.Add); - - return fluids; - } - - public void QueryFluidsCenter(Vector3 center, Vector3 size, Action callback) { - QueryFluids(center - 0.5f * size, center + 0.5f * size, callback); - } - - public List QueryFluidsCenterList(Vector3 center, Vector3 size) { - List fluids = []; - - QueryFluidsCenter(center, size, fluids.Add); - - return fluids; - } - - public void QueryVibrateObjects(BoundingBox3 box, Action callback) { - QueryVibrateObjects(box.Min, box.Max, callback); - } - - public List QueryVibrateObjectsList(BoundingBox3 box) { - List vibrateObjects = []; - - QueryVibrateObjects(box, vibrateObjects.Add); - - return vibrateObjects; - } - - public void QueryVibrateObjects(Vector3 min, Vector3 max, Action callback) { - QueryBox(min, max, entity => { - if (entity is FieldVibrateEntity vibrateObject) { - callback(vibrateObject); - } - }); - } - - public List QueryVibrateObjectsList(Vector3 min, Vector3 max) { - List vibrateObjects = []; - - QueryVibrateObjects(min, max, vibrateObjects.Add); - - return vibrateObjects; - } - - public void QueryVibrateObjectsCenter(Vector3 center, Vector3 size, Action callback) { - QueryVibrateObjects(center - 0.5f * size, center + 0.5f * size, callback); - } - - public List QueryVibrateObjectsCenterList(Vector3 center, Vector3 size) { - List vibrateObjects = []; - - QueryVibrateObjectsCenter(center, size, vibrateObjects.Add); - - return vibrateObjects; - } - - public FieldVibrateEntity? GetVibrateEntity(string entityId) { - return vibrateEntities.FirstOrDefault(vibrateEntity => vibrateEntity.Id.Id == entityId); - } - #endregion - - #region Initialization - // used to guarantee deterministic output when parsing maps - private static void SortEntityList(List entityList) { - if (entityList.Count <= 1) { - return; - } - - entityList.Sort((entity1, entity2) => { - int comparison = entity1.Id.High.CompareTo(entity2.Id.High); - - if (comparison == 0) { - return entity1.Id.Low.CompareTo(entity2.Id.Low); - } - - return comparison; - }); - } - - private static void GenerateSpawnLocations(Dictionary> gridAlignedEntities, Vector3S minIndex, Vector3S maxIndex, List unalignedEntities) { - Dictionary occupancyMap = new(); - List<(Vector3S index, FieldSpawnTile tile)> spawnTiles = []; - - foreach ((Vector3S coord, List entityList) in gridAlignedEntities) { - if (!occupancyMap.TryGetValue(coord, out (bool isOccupied, bool isGround) occupancy)) { - occupancy = (false, false); - } - - foreach (FieldEntity entity in entityList) { - if (entity is FieldBoxColliderEntity boxEntity && !boxEntity.IsWhiteBox) { - occupancyMap[coord] = (true, true); - - continue; - } - - if (entity is FieldVibrateEntity) { - continue; - } - - occupancyMap[coord] = (true, occupancy.isGround); - - } - } - - foreach (FieldEntity entity in unalignedEntities) { - Vector3 minPosition = (1 / BLOCK_SIZE) * entity.Bounds.Min; - Vector3 maxPosition = (1 / BLOCK_SIZE) * entity.Bounds.Max; - Vector3S minCubeIndex = PointToCell(minPosition); - Vector3S maxCubeIndex = PointToCell(maxPosition); - - for (short x = minCubeIndex.X; x <= maxCubeIndex.X; x++) { - for (short y = minCubeIndex.Y; y <= maxCubeIndex.Y; y++) { - for (short z = minCubeIndex.Z; z <= maxCubeIndex.Z; z++) { - var coord = new Vector3S(x, y, z); - - if (!occupancyMap.TryGetValue(coord, out (bool isOccupied, bool isGround) occupancy)) { - occupancy = (false, false); - } - - occupancyMap[coord] = (true, occupancy.isGround); - } - } - } - } - - for (short x = minIndex.X; x <= maxIndex.X; x++) { - for (short y = minIndex.Y; y <= maxIndex.Y; y++) { - for (short z = (short) (minIndex.Z + 1); z <= maxIndex.Z; z++) { - var coord = new Vector3S(x, y, z); - var groundCoord = new Vector3S(x, y, (short) (z - 1)); - - if (!occupancyMap.TryGetValue(coord, out (bool isOccupied, bool isGround) occupancy)) { - occupancy = (false, false); - } - - if (!occupancyMap.TryGetValue(groundCoord, out (bool isOccupied, bool isGround) groundOccupancy)) { - groundOccupancy = (false, false); - } - - if (!occupancy.isGround && !occupancy.isOccupied && groundOccupancy.isGround) { - if (!gridAlignedEntities.TryGetValue(coord, out List? entities)) { - entities = []; - - gridAlignedEntities.Add(coord, entities); - } - - Vector3 cellPosition = BLOCK_SIZE * coord.Vector3; - var bounds = new BoundingBox3(cellPosition - new Vector3(HALF_BLOCK, HALF_BLOCK, 0), cellPosition + new Vector3(HALF_BLOCK, HALF_BLOCK, BLOCK_SIZE)); - - entities.Add(new FieldSpawnTile( - Id: new FieldEntityId(0, 0, string.Empty), - Position: cellPosition, - Rotation: Vector3.Zero, - Scale: 1, - Bounds: bounds)); - } - - bool isSurface = !occupancy.isOccupied; - bool isShallow = isSurface && groundOccupancy.isGround; - - if ((!isSurface || isShallow) && gridAlignedEntities.TryGetValue(groundCoord, out List? entityList)) { - for (int i = 0; i < entityList.Count; ++i) { - FieldEntity entity = entityList[i]; - - if (entity is FieldFluidEntity fluid) { - entityList[i] = new FieldFluidEntity( - Id: fluid.Id, - Position: fluid.Position, - Rotation: fluid.Rotation, - Scale: fluid.Scale, - LiquidType: fluid.LiquidType, - Bounds: fluid.Bounds, - MeshLlid: fluid.MeshLlid, - IsShallow: isShallow, - IsSurface: isSurface, - MapAttribute: fluid.MapAttribute); - } - } - } - } - } - } - } - - public void TrimGridSize(Dictionary> gridAlignedEntities, ref Vector3S minIndex, ref Vector3S maxIndex, List unalignedEntities) { - Vector3S gridSize = maxIndex - minIndex + new Vector3S(1, 1, 1); - int cellCount = gridSize.X * gridSize.Y * (gridSize.Z - 1); - int[] axisXCount = new int[gridSize.X]; - int[] axisYCount = new int[gridSize.Y]; - int[] axisZCount = new int[gridSize.Z]; - - foreach ((Vector3S coord, List entityList) in gridAlignedEntities) { - Vector3S index = coord - minIndex; - - axisXCount[index.X] += entityList.Count; - axisYCount[index.Y] += entityList.Count; - axisZCount[index.Z] += entityList.Count; - } - - int trimmedCount = 0; - short trimmedMinX = 0; - short trimmedMinY = 0; - short trimmedMinZ = 0; - short trimmedMaxX = (short) (maxIndex.X - minIndex.X); - short trimmedMaxY = (short) (maxIndex.Y - minIndex.Y); - short trimmedMaxZ = (short) (maxIndex.Z - minIndex.Z); - - for (int i = 0, cumulative = 0; i < axisXCount.Length; i++) { - int currentAxisCount = axisXCount[i]; - int remaining = gridAlignedEntities.Count - cumulative; - cumulative += currentAxisCount; - - // only cull isolated cells - if (currentAxisCount != 0) { - continue; - } - - if (cumulative < AXIS_TRIM_ENTITY_COUNT) { - trimmedMinX = (short) i; - } - - if (remaining < AXIS_TRIM_ENTITY_COUNT) { - trimmedMaxX = (short) (i - 1); - - break; - } - } - - for (int i = 0, cumulative = 0; i < axisYCount.Length; i++) { - int currentAxisCount = axisYCount[i]; - int remaining = gridAlignedEntities.Count - cumulative; - cumulative += currentAxisCount; - - // only cull isolated cells - if (currentAxisCount != 0) { - continue; - } - - if (cumulative < AXIS_TRIM_ENTITY_COUNT) { - trimmedMinY = (short) i; - } - - if (remaining < AXIS_TRIM_ENTITY_COUNT) { - trimmedMaxY = (short) (i - 1); - - break; - } - } - - for (int i = 0, cumulative = 0; i < axisZCount.Length; i++) { - int currentAxisCount = axisZCount[i]; - int remaining = gridAlignedEntities.Count - cumulative; - cumulative += currentAxisCount; - - // only cull isolated cells - if (currentAxisCount != 0) { - continue; - } - - if (cumulative < AXIS_TRIM_ENTITY_COUNT) { - trimmedMinZ = (short) i; - } - - if (remaining < AXIS_TRIM_ENTITY_COUNT) { - trimmedMaxZ = (short) (i - 1); - - break; - } - } - - Vector3S newMinIndex = new Vector3S(trimmedMinX, trimmedMinY, trimmedMinZ) + minIndex; - Vector3S newMaxIndex = new Vector3S(trimmedMaxX, trimmedMaxY, trimmedMaxZ) + minIndex; - Vector3S newGridSize = newMaxIndex - newMinIndex + new Vector3S(1, 1, 1); - int newCellCount = newGridSize.X * newGridSize.Y * newGridSize.Z; - int culledCells = cellCount - newCellCount; - - foreach ((Vector3S coord, List entityList) in gridAlignedEntities) { - Vector3S index = coord - minIndex; - - bool isTrimmed = index.X < trimmedMinX; - isTrimmed |= index.X > trimmedMaxX; - isTrimmed |= index.Y < trimmedMinY; - isTrimmed |= index.Y > trimmedMaxY; - isTrimmed |= index.Z < trimmedMinZ; - isTrimmed |= index.Z > trimmedMaxZ; - - if (isTrimmed && entityList.Count > 0) { - trimmedCount += entityList.Count; - - Vector3 cellPosition = BLOCK_SIZE * coord.Vector3; - BoundingBox3 bounds = entityList.First().Bounds; - - foreach (FieldEntity entity in entityList) { - bounds = bounds.Expand(entity.Bounds); - } - - if (entityList.Count == 1) { - alignedTrimmedEntities.Add(entityList.First()); - - continue; - } - - SortEntityList(entityList); - - // limit the number of nodes in the AABB tree by bundling cells together - FieldCellEntities cell = new FieldCellEntities( - Id: new FieldEntityId(0, 0, string.Empty), - Position: cellPosition, - Rotation: new Vector3(0, 0, 0), - Scale: 1, - Bounds: bounds, - Entities: entityList); - - alignedTrimmedEntities.Add(cell); - } - } - } - - // cell grid contains ints that contain both list start index & entity count for the cell - // top byte is used for entity count, the 3 least significant bytes are used for list start index: CC II II II - // compare cell data with 0 to check if it is empty: 00 00 00 00 - public static (byte count, int startIndex) GetCellInfo(int cellData) { - byte count = (byte) (cellData >> 24); - int startIndex = cellData & 0xFFFFFF; - - return (count, startIndex); - } - - public static int WriteCellInfo(int count, int startIndex) { - return ((count & 0xFF) << 24) | (startIndex & 0xFFFFFF); - } - - public void AddVibrateEntities(List entities) { - foreach (FieldEntity entity in entities) { - if (entity is FieldVibrateEntity vibrate) { - vibrateEntities[vibrate.VibrateIndex] = vibrate; - } - - if (entity is FieldCellEntities cell) { - AddVibrateEntities(cell.Entities); - } - } - } - - public void AddEntities(Dictionary> gridAlignedEntities, Vector3S minIndex, Vector3S maxIndex, List unalignedEntities, int vibrateCount) { - if (minIndex.X == short.MaxValue) { - minIndex = new Vector3S(0, 0, 0); - maxIndex = new Vector3S(0, 0, 0); - } - - for (int i = 0; i < vibrateCount; ++i) { - vibrateEntities.Add(new FieldVibrateEntity( - Id: new FieldEntityId(0, 0, string.Empty), - Position: new Vector3(0, 0, 0), - Rotation: new Vector3(0, 0, 0), - Scale: 1, - Bounds: new BoundingBox3(), - BreakDefense: 0, - BreakTick: 0, - VibrateIndex: i)); - } - - GenerateSpawnLocations(gridAlignedEntities, minIndex, maxIndex, unalignedEntities); - TrimGridSize(gridAlignedEntities, ref minIndex, ref maxIndex, unalignedEntities); - - GridSize = maxIndex - minIndex + new Vector3S(1, 1, 1); - MinIndex = minIndex; - MaxIndex = maxIndex; - alignedEntities.Clear(); - this.unalignedEntities = unalignedEntities; - cellGrid = new int[GridSize.X, GridSize.Y, GridSize.Z]; - - // opting to order list entries by z index first, then by y, and last by x, the same way the memory will be laid out - // this will dramatically speed up both load times and cell access times by storing them in a defragmented format from the start - // the reason why is to reduce cache misses - for (short x = minIndex.X; x <= maxIndex.X; x++) { - for (short y = minIndex.Y; y <= maxIndex.Y; y++) { - for (short z = minIndex.Z; z <= maxIndex.Z; z++) { - Vector3S coord = new Vector3S(x, y, z); - - if (!gridAlignedEntities.TryGetValue(coord, out List? entities) || entities.Count == 0) { - continue; - } - - SortEntityList(entities); - - Vector3S index = coord - minIndex; - - cellGrid[index.X, index.Y, index.Z] = WriteCellInfo(entities.Count, alignedEntities.Count); - alignedEntities.AddRange(entities); - } - } - } - - SortEntityList(alignedTrimmedEntities); - SortEntityList(unalignedEntities); - - AddVibrateEntities(alignedEntities); - AddVibrateEntities(alignedTrimmedEntities); - AddVibrateEntities(unalignedEntities); - - GenerateAabbTree(); - } - - public void GenerateAabbTree() { - // TODO: generate AABB tree from unaligned objects list - } - - public void WriteTo(IByteWriter writer) { - writer.Write(GridSize); - writer.Write(MinIndex); - writer.Write(vibrateEntities.Count); - - for (short x = 0; x < GridSize.X; x++) { - for (short y = 0; y < GridSize.Y; y++) { - for (short z = 0; z < GridSize.Z; z++) { - if (cellGrid[x, y, z] != 0) { - writer.WriteInt(cellGrid[x, y, z]); - - continue; - } - - int emptyCount = 0; - - while (z < GridSize.Z && cellGrid[x, y, z] == 0) { - ++emptyCount; - ++z; - } - - // use list start index as empty count for byte streams - writer.WriteInt(WriteCellInfo(0, emptyCount)); - - --z; // don't skip first occupied cell - } - } - } - - if (writer is ByteWriter byteWriter) { - GridBytesWritten = (ulong) byteWriter.Length; - } - - writer.WriteInt(alignedEntities.Count); - - foreach (FieldEntity entity in alignedEntities) { - WriteTo(entity, writer); - } - - writer.WriteInt(alignedTrimmedEntities.Count); - - foreach (FieldEntity entity in alignedTrimmedEntities) { - WriteTo(entity, writer); - } - - writer.WriteInt(unalignedEntities.Count); - - foreach (FieldEntity entity in unalignedEntities) { - WriteTo(entity, writer); - } - } - #endregion - - #region Serialization - private Vector3S GetWorldGridIndex(Vector3 position) { - int x = (int) Math.Round(position.X) / Constant.BlockSize; - int y = (int) Math.Round(position.Y) / Constant.BlockSize; - int z = (int) Math.Round(position.Z) / Constant.BlockSize; - - return new Vector3S((short) x, (short) y, (short) z); - } - - private bool IsGridAligned(Vector3 position) { - int x = (int) Math.Round(position.X) / Constant.BlockSize; - int y = (int) Math.Round(position.Y) / Constant.BlockSize; - int z = (int) Math.Round(position.Z) / Constant.BlockSize; - - return position.IsNearlyEqual(BLOCK_SIZE * new Vector3(x, y, z), 0.1f); - } - - private bool IsCellBounds(Vector3 position, BoundingBox3 bounds) { - if (!IsGridAligned(position)) { - return false; - } - - bool isMinOnCell = bounds.Min.IsNearlyEqual(position - new Vector3(HALF_BLOCK, HALF_BLOCK, 0), 0.1f); - bool isMaxOnCell = bounds.Max.IsNearlyEqual(position + new Vector3(HALF_BLOCK, HALF_BLOCK, BLOCK_SIZE), 0.1f); - - return isMinOnCell && isMaxOnCell; - } - - public void WriteTo(FieldEntity entity, IByteWriter writer) { - FieldEntityType type = entity switch { - FieldVibrateEntity => FieldEntityType.Vibrate, - FieldSpawnTile => FieldEntityType.SpawnTile, - FieldBoxColliderEntity => FieldEntityType.BoxCollider, - FieldFluidEntity => FieldEntityType.Fluid, - FieldMeshColliderEntity => FieldEntityType.MeshCollider, - FieldCellEntities => FieldEntityType.Cell, - FieldSellableTile => FieldEntityType.SellableTile, - _ => FieldEntityType.Unknown, - }; - - FieldEntityMembers memberFlags = FieldEntityMembers.None; - - memberFlags |= (entity.Id.High == 0 && entity.Id.Low == 0) ? 0 : FieldEntityMembers.Id; - memberFlags |= IsGridAligned(entity.Position) ? 0 : FieldEntityMembers.Position; - memberFlags |= entity.Rotation.IsNearlyEqual(new Vector3(0, 0, 0), 1e-3f) ? 0 : FieldEntityMembers.Rotation; - memberFlags |= entity.Scale.IsNearlyEqual(1, 1e-3f) ? 0 : FieldEntityMembers.Scale; - memberFlags |= IsCellBounds(entity.Position, entity.Bounds) ? 0 : FieldEntityMembers.Bounds; - - switch (entity) { - case FieldMeshColliderEntity meshCollider: - memberFlags |= (meshCollider.MeshLlid == 0) ? 0 : FieldEntityMembers.Llid; - break; - default: - break; - } - - writer.Write(type); - writer.Write(memberFlags); - - if ((memberFlags & FieldEntityMembers.Id) != 0) { - writer.Write(entity.Id.High); - writer.Write(entity.Id.Low); - writer.WriteString(entity.Id.Id); - } - - if ((memberFlags & FieldEntityMembers.Position) != 0) { - writer.Write(entity.Position); - } else { - writer.Write(GetWorldGridIndex(entity.Position)); - } - - if ((memberFlags & FieldEntityMembers.Rotation) != 0) { - writer.Write(entity.Rotation); - } - - if ((memberFlags & FieldEntityMembers.Scale) != 0) { - writer.Write(entity.Scale); - } - - if ((memberFlags & FieldEntityMembers.Bounds) != 0) { - writer.Write(entity.Bounds.Min); - writer.Write(entity.Bounds.Max); - } - - switch (entity) { - case FieldVibrateEntity vibrateEntity: - writer.Write(vibrateEntity.VibrateIndex); - writer.WriteInt(vibrateEntity.BreakDefense); - writer.WriteInt(vibrateEntity.BreakTick); - break; - case FieldSpawnTile spawnTile: - break; - case FieldBoxColliderEntity boxCollider: - writer.Write(boxCollider.Size); - writer.Write(boxCollider.IsWhiteBox); - writer.Write(boxCollider.IsFluid); - writer.Write(boxCollider.MapAttribute); - break; - case FieldMeshColliderEntity meshCollider: - if ((memberFlags & FieldEntityMembers.Llid) != 0) { - writer.Write(meshCollider.MeshLlid); - } - writer.Write(meshCollider.MapAttribute); - if (entity is FieldFluidEntity fluid) { - writer.Write(fluid.LiquidType); - writer.Write(fluid.IsShallow); - writer.Write(fluid.IsSurface); - } - break; - case FieldCellEntities cell: - writer.WriteInt(cell.Entities.Count); - foreach (FieldEntity childEntity in cell.Entities) { - WriteTo(childEntity, writer); - } - break; - case FieldSellableTile sellableTile: - writer.Write(sellableTile.SellableGroup); - break; - default: - throw new InvalidDataException($"Writing unhandled field entity type: {entity.GetType().FullName}"); - } - } - - public void ReadFrom(IByteReader reader) { - GridSize = reader.Read(); - MinIndex = reader.Read(); - MaxIndex = MinIndex + GridSize - new Vector3S(1, 1, 1); - cellGrid = new int[GridSize.X, GridSize.Y, GridSize.Z]; - - alignedEntities.Clear(); - alignedTrimmedEntities.Clear(); - unalignedEntities.Clear(); - vibrateEntities.Clear(); - - int vibrateCount = reader.Read(); - - for (int i = 0; i < vibrateCount; ++i) { - vibrateEntities.Add(new FieldVibrateEntity( - Id: new FieldEntityId(0, 0, string.Empty), - Position: new Vector3(0, 0, 0), - Rotation: new Vector3(0, 0, 0), - Scale: 1, - Bounds: new BoundingBox3(), - BreakDefense: 0, - BreakTick: 0, - VibrateIndex: i)); - } - - for (short x = 0; x < GridSize.X; x++) { - for (short y = 0; y < GridSize.Y; y++) { - for (short z = 0; z < GridSize.Z; z++) { - int cellData = reader.ReadInt(); - (byte count, int startIndex) cell = GetCellInfo(cellData); - - if (cell.count == 0) { - // use list start index as empty count for byte streams - z += (short) (cell.startIndex - 1); - - continue; - } - - cellGrid[x, y, z] = cellData; - } - } - } - - int alignedEntityCount = reader.ReadInt(); - - for (int i = 0; i < alignedEntityCount; ++i) { - alignedEntities.Add(ReadEntity(reader)); - } - - int alignedTrimmedEntityCount = reader.ReadInt(); - - for (int i = 0; i < alignedTrimmedEntityCount; ++i) { - alignedTrimmedEntities.Add(ReadEntity(reader)); - } - - int unalignedEntityCount = reader.ReadInt(); - - for (int i = 0; i < unalignedEntityCount; ++i) { - unalignedEntities.Add(ReadEntity(reader)); - } - - GenerateAabbTree(); - - AddVibrateEntities(alignedEntities); - AddVibrateEntities(alignedTrimmedEntities); - AddVibrateEntities(unalignedEntities); - } - - public FieldEntity ReadEntity(IByteReader reader) { - var type = reader.Read(); - var memberFlags = reader.Read(); - - var id = new FieldEntityId(0, 0, string.Empty); - Vector3 position; - var rotation = new Vector3(0, 0, 0); - float scale = 1; - BoundingBox3 bounds; - uint llid = 0; - - if ((memberFlags & FieldEntityMembers.Id) != 0) { - id = new FieldEntityId(reader.Read(), reader.Read(), reader.ReadString()); - } - - if ((memberFlags & FieldEntityMembers.Position) != 0) { - position = reader.Read(); - } else { - position = BLOCK_SIZE * reader.Read().Vector3; - } - - if ((memberFlags & FieldEntityMembers.Rotation) != 0) { - rotation = reader.Read(); - } - - if ((memberFlags & FieldEntityMembers.Scale) != 0) { - scale = reader.ReadFloat(); - } - - if ((memberFlags & FieldEntityMembers.Bounds) != 0) { - bounds = new BoundingBox3( - min: reader.Read(), - max: reader.Read()); - } else { - bounds = new BoundingBox3( - min: position - new Vector3(HALF_BLOCK, HALF_BLOCK, 0), - max: position + new Vector3(HALF_BLOCK, HALF_BLOCK, BLOCK_SIZE)); - } - - switch (type) { - case FieldEntityType.Vibrate: - return new FieldVibrateEntity( - Id: id, - Position: position, - Rotation: rotation, - Scale: scale, - Bounds: bounds, - VibrateIndex: reader.ReadInt(), - BreakDefense: reader.ReadInt(), - BreakTick: reader.ReadInt()); - case FieldEntityType.SpawnTile: - return new FieldSpawnTile( - Id: id, - Position: position, - Rotation: rotation, - Scale: scale, - Bounds: bounds); - case FieldEntityType.BoxCollider: - return new FieldBoxColliderEntity( - Id: id, - Position: position, - Rotation: rotation, - Scale: scale, - Bounds: bounds, - Size: reader.Read(), - IsWhiteBox: reader.Read(), - IsFluid: reader.Read(), - MapAttribute: reader.Read()); - case FieldEntityType.MeshCollider: - if ((memberFlags & FieldEntityMembers.Llid) != 0) { - llid = reader.Read(); - } - - return new FieldMeshColliderEntity( - Id: id, - Position: position, - Rotation: rotation, - Scale: scale, - Bounds: bounds, - MeshLlid: llid, - MapAttribute: reader.Read()); - case FieldEntityType.Fluid: - if ((memberFlags & FieldEntityMembers.Llid) != 0) { - llid = reader.Read(); - } - var mapAttribute = reader.Read(); - return new FieldFluidEntity( - Id: id, - Position: position, - Rotation: rotation, - Scale: scale, - LiquidType: reader.Read(), - Bounds: bounds, - MeshLlid: llid, - IsShallow: reader.Read(), - IsSurface: reader.Read(), - MapAttribute: mapAttribute); - case FieldEntityType.Cell: - int childCount = reader.ReadInt(); - var children = new List(); - for (int i = 0; i < childCount; ++i) { - children.Add(ReadEntity(reader)); - } - return new FieldCellEntities( - Id: id, - Position: position, - Rotation: rotation, - Scale: scale, - Bounds: bounds, - Entities: children); - case FieldEntityType.SellableTile: - return new FieldSellableTile( - Id: id, - Position: position, - Rotation: rotation, - Scale: scale, - Bounds: bounds, - SellableGroup: reader.Read()); - default: - throw new InvalidDataException($"Reading unhandled field entity type: {type}"); - } - } - #endregion - -} +using Maple2.Model.Common; +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; +using Maple2.Tools.VectorMath; +using System.Numerics; +using System.Runtime.InteropServices; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.Model.Metadata.FieldEntity; + +namespace Maple2.Model.Game.Field; + +[Flags] +internal enum FieldEntityMembers : byte { + None = 0x0, + Id = 0x1, + Position = 0x2, + Rotation = 0x4, + Scale = 0x8, + Bounds = 0x10, + Llid = 0x20, +} + +/* + * FieldAccelerationStructure is a helper class that specializes in various spatial queries for objects. + * It's designed to maximize performance for finding objects in a 3D space with desired constraints. + * + * Internally it uses specialized storage techniques with fast look up times, but knowledge on these + * techniques isn't necessary for use. The Query functions allow you to find the objects you need without + * knowing any of the implementation details. + * + * You can do general queries or queries for specific entity types for all types of queries available. + * Every entity matching the query constraints will be reported back through a callback, or in a list with + * Query___List() methods. + * + * Available query types include: + * - Cell: Captures all entities in a specific grid cell, or range of cells. Doesn't capture freely floating entities. + * - Point: Captures all entities intersecting with a specific point. + * - CellAtPoint: Captures all entities in the grid cell intersecting with a specific point. Doesn't capture freely floating entities. + * - TreeAtPoint: Captures all entities in the AABB tree intersecting with a specific point. Doesn't capture cells. + * - Box: Captures all entities intersecting with a bounding box. + * - CellsInBox: Captures all entities in grid cells intersecting with a bounding box. Doesn't capture freely floating entities. + * - TreeInBox: Captures all entities in the AABB tree intersecting with a bounding box. Doesn't capture cells. + * - Sphere: Captures all entities intersecting with a sphere. + * - CellsInSphere: Captures all entities in grid cells intersecting with a sphere. Doesn't capture freely floating entities. + * - TreeInSphere: Captures all entities in the AABB tree intersecting with a sphere. Doesn't capture cells. + * - Ray: Captures all entities intersecting with a ray. Object results may not be in order. + * - CellsOnRay: Captures all entities intersecting with a ray. Object results may not be in order. Doesn't capture freely floating entities. + * - TreeOnRay: Captures all entities in the AABB tree intersecting with a ray. Doesn't capture cells. + * - RayCast: Captures all boxes & meshes intersecting with a ray in order until false is returned by the callback. + * - CellRayCast: Captures all boxes & meshes intersecting with a ray in order until -1 is returned by the callback. Doesn't capture freely floating entities. + * - TreeRayCast: Captures all entities in the AABB tree intersecting with a ray in order until -1 is returned by the callback. Doesn't capture cells. + * - Frustum: Captures all entities overlapping with a frustum. Useful for map rendering. + * - CellsInFrustum: Captures all entities overlapping with a frustum. Useful for map rendering. Doesn't capture freely floating entities. + * - TreeInFrustum: Captures all entities overlapping with a frustum. Useful for map rendering. Doesn't capture cells. + * + * Special purpose queries: + * - Spawns: Captures all mob spawn candidates in a sphere. + * - Fluids: Captures all fluids within a bounding box. + * - VibrateObjects: Captures all vibrate objects within a bounding box. + */ +public class FieldAccelerationStructure : IByteSerializable, IByteDeserializable { + public const int AXIS_TRIM_ENTITY_COUNT = 10; + public const float BLOCK_SIZE = (float) Constant.BlockSize; + public const float HALF_BLOCK = 0.5f * BLOCK_SIZE; + + public Vector3S GridSize { get; private set; } = new Vector3S(); + public Vector3S MinIndex { get; private set; } = new Vector3S(); + public Vector3S MaxIndex { get; private set; } = new Vector3S(); + + public ReadOnlySpan AlignedEntities => CollectionsMarshal.AsSpan(alignedEntities); + public ReadOnlySpan AlignedTrimmedEntities => CollectionsMarshal.AsSpan(alignedTrimmedEntities); + public ReadOnlySpan UnalignedEntities => CollectionsMarshal.AsSpan(unalignedEntities); + // Make a list of vibrate objects on the field with the same size & order as this list + // Then in queries use field.VibrateObjects[vibrateEntity.VibrateIndex] to retrieve the right one + public ReadOnlySpan VibrateEntities => CollectionsMarshal.AsSpan(vibrateEntities); + + private readonly List alignedEntities = []; + private readonly List alignedTrimmedEntities = []; + private List unalignedEntities = []; // TODO: add AABB tree implementation for querying unaligned objects + private readonly List vibrateEntities = []; + private int[,,] cellGrid = new int[0, 0, 0]; + + public ulong GridBytesWritten { get; private set; } = 0; + + public FieldAccelerationStructure() { } + + public static Vector3S PointToCell(Vector3 point) { + point *= (1 / BLOCK_SIZE); + return new Vector3S((short) Math.Floor(point.X + 0.5f), (short) Math.Floor(point.Y + 0.5f), (short) Math.Floor(point.Z)); + } + + #region QueryApi + public void QueryCells(Vector3 min, Vector3 max, Action callback) { + Vector3S minIndex = PointToCell(min) - MinIndex; + Vector3S maxIndex = PointToCell(max) - MinIndex; + + for (short x = short.Max(0, minIndex.X); x < short.Min((short) (maxIndex.X + 1), GridSize.X); ++x) { + for (short y = short.Max(0, minIndex.Y); y < short.Min((short) (maxIndex.Y + 1), GridSize.Y); ++y) { + for (short z = short.Max(0, minIndex.Z); z < short.Min((short) (maxIndex.Z + 1), GridSize.Z); ++z) { + (byte count, int startIndex) = GetCellInfo(cellGrid[x, y, z]); + + for (byte i = 0; i < count; ++i) { + callback(alignedEntities[startIndex + i]); + } + } + } + } + + // TODO: query aabb tree + foreach (FieldEntity entity in alignedTrimmedEntities) { + if (entity.Bounds.Intersects(new BoundingBox3(min, max))) { + if (entity is FieldCellEntities cell) { + foreach (FieldEntity child in cell.Entities) { + callback(child); + } + + continue; + } + + callback(entity); + } + } + } + + public void QueryTreeInBox(Vector3 min, Vector3 max, Action callback) { + // TODO: query aabb tree + foreach (FieldEntity entity in unalignedEntities) { + if (entity.Bounds.Intersects(new BoundingBox3(min, max))) { + callback(entity); + } + } + } + + public void QueryBox(Vector3 min, Vector3 max, Action callback) { + QueryCells(min, max, callback); + QueryTreeInBox(min, max, callback); + } + + public void CellsInSphere(Vector3 center, float radius, Action callback) { + QueryCells(center - new Vector3(radius, radius, radius), center + new Vector3(radius, radius, radius), entity => { + if (entity.Bounds.IntersectsSphere(center, radius)) { + callback(entity); + } + }); + } + + public void QuerySpawns(Vector3 center, float radius, Action callback) { + CellsInSphere(center, radius, entity => { + if (entity is FieldSpawnTile spawn) { + callback(spawn); + } + }); + } + + public List QuerySpawnsList(Vector3 center, float radius) { + List spawns = []; + + QuerySpawns(center, radius, spawns.Add); + + return spawns; + } + + public void QueryFluids(BoundingBox3 box, Action callback) { + QueryFluids(box.Min, box.Max, callback); + } + + public List QueryFluidsList(BoundingBox3 box) { + List fluids = []; + + QueryFluids(box, fluids.Add); + + return fluids; + } + + public void QueryFluids(Vector3 min, Vector3 max, Action callback) { + QueryCells(min, max, entity => { + if (entity is FieldFluidEntity { IsSurface: true, IsShallow: false } fluid) { + callback(fluid); + } + }); + } + + public void QuerySellableTiles(Vector3 min, Vector3 max, Action callback) { + QueryCells(min, max, entity => { + if (entity is FieldSellableTile tile) { + callback(tile); + } + }); + } + + /// + /// Finds the first sellable tile at the specified position that matches the predicate. + /// + /// The position to search for the sellable tile. + /// A function to test each sellable tile for a condition. + /// The first sellable tile that matches the predicate, or null if no such tile is found. + public FieldSellableTile? FirstSellableTile(Vector3 position, Func predicate) { + Vector3 size = new Vector3(0, 0, 3000); // Only search in same column, we are looking for ground tiles + Vector3 min = position - 0.5f * size; + Vector3 max = position + 0.5f * size; + FieldSellableTile? result = null; + QueryCells(min, max, entity => { + if (result is not null) return; + if (entity is not FieldSellableTile tile || !predicate(tile)) return; + result = tile; + }); + return result; + } + + public void QuerySellableTilesCenter(Vector3 center, Vector3 size, Action callback) { + QuerySellableTiles(center - 0.5f * size, center + 0.5f * size, callback); + } + + public void QueryBoxCollider(Vector3 center, float radius, Action callback) { + QueryCells(center - new Vector3(radius, radius, radius), center + new Vector3(radius, radius, radius), entity => { + if (entity is FieldBoxColliderEntity collider) { + callback(collider); + } + }); + } + + public void FindBlockUnderPlayer(Vector3 position, Action callback) { + position = position with { + Z = position.Z - BLOCK_SIZE, + }; + position = position.Align(); + QueryCells(position, position, callback); + } + + public List QueryFluidsList(Vector3 min, Vector3 max) { + List fluids = []; + + QueryFluids(min, max, fluids.Add); + + return fluids; + } + + public void QueryFluidsCenter(Vector3 center, Vector3 size, Action callback) { + QueryFluids(center - 0.5f * size, center + 0.5f * size, callback); + } + + public List QueryFluidsCenterList(Vector3 center, Vector3 size) { + List fluids = []; + + QueryFluidsCenter(center, size, fluids.Add); + + return fluids; + } + + public void QueryVibrateObjects(BoundingBox3 box, Action callback) { + QueryVibrateObjects(box.Min, box.Max, callback); + } + + public List QueryVibrateObjectsList(BoundingBox3 box) { + List vibrateObjects = []; + + QueryVibrateObjects(box, vibrateObjects.Add); + + return vibrateObjects; + } + + public void QueryVibrateObjects(Vector3 min, Vector3 max, Action callback) { + QueryBox(min, max, entity => { + if (entity is FieldVibrateEntity vibrateObject) { + callback(vibrateObject); + } + }); + } + + public List QueryVibrateObjectsList(Vector3 min, Vector3 max) { + List vibrateObjects = []; + + QueryVibrateObjects(min, max, vibrateObjects.Add); + + return vibrateObjects; + } + + public void QueryVibrateObjectsCenter(Vector3 center, Vector3 size, Action callback) { + QueryVibrateObjects(center - 0.5f * size, center + 0.5f * size, callback); + } + + public List QueryVibrateObjectsCenterList(Vector3 center, Vector3 size) { + List vibrateObjects = []; + + QueryVibrateObjectsCenter(center, size, vibrateObjects.Add); + + return vibrateObjects; + } + + public FieldVibrateEntity? GetVibrateEntity(string entityId) { + return vibrateEntities.FirstOrDefault(vibrateEntity => vibrateEntity.Id.Id == entityId); + } + #endregion + + #region Initialization + // used to guarantee deterministic output when parsing maps + private static void SortEntityList(List entityList) { + if (entityList.Count <= 1) { + return; + } + + entityList.Sort((entity1, entity2) => { + int comparison = entity1.Id.High.CompareTo(entity2.Id.High); + + if (comparison == 0) { + return entity1.Id.Low.CompareTo(entity2.Id.Low); + } + + return comparison; + }); + } + + private static void GenerateSpawnLocations(Dictionary> gridAlignedEntities, Vector3S minIndex, Vector3S maxIndex, List unalignedEntities) { + Dictionary occupancyMap = new(); + List<(Vector3S index, FieldSpawnTile tile)> spawnTiles = []; + + foreach ((Vector3S coord, List entityList) in gridAlignedEntities) { + if (!occupancyMap.TryGetValue(coord, out (bool isOccupied, bool isGround) occupancy)) { + occupancy = (false, false); + } + + foreach (FieldEntity entity in entityList) { + if (entity is FieldBoxColliderEntity boxEntity && !boxEntity.IsWhiteBox) { + occupancyMap[coord] = (true, true); + + continue; + } + + if (entity is FieldVibrateEntity) { + continue; + } + + occupancyMap[coord] = (true, occupancy.isGround); + + } + } + + foreach (FieldEntity entity in unalignedEntities) { + Vector3 minPosition = (1 / BLOCK_SIZE) * entity.Bounds.Min; + Vector3 maxPosition = (1 / BLOCK_SIZE) * entity.Bounds.Max; + Vector3S minCubeIndex = PointToCell(minPosition); + Vector3S maxCubeIndex = PointToCell(maxPosition); + + for (short x = minCubeIndex.X; x <= maxCubeIndex.X; x++) { + for (short y = minCubeIndex.Y; y <= maxCubeIndex.Y; y++) { + for (short z = minCubeIndex.Z; z <= maxCubeIndex.Z; z++) { + var coord = new Vector3S(x, y, z); + + if (!occupancyMap.TryGetValue(coord, out (bool isOccupied, bool isGround) occupancy)) { + occupancy = (false, false); + } + + occupancyMap[coord] = (true, occupancy.isGround); + } + } + } + } + + for (short x = minIndex.X; x <= maxIndex.X; x++) { + for (short y = minIndex.Y; y <= maxIndex.Y; y++) { + for (short z = (short) (minIndex.Z + 1); z <= maxIndex.Z; z++) { + var coord = new Vector3S(x, y, z); + var groundCoord = new Vector3S(x, y, (short) (z - 1)); + + if (!occupancyMap.TryGetValue(coord, out (bool isOccupied, bool isGround) occupancy)) { + occupancy = (false, false); + } + + if (!occupancyMap.TryGetValue(groundCoord, out (bool isOccupied, bool isGround) groundOccupancy)) { + groundOccupancy = (false, false); + } + + if (!occupancy.isGround && !occupancy.isOccupied && groundOccupancy.isGround) { + if (!gridAlignedEntities.TryGetValue(coord, out List? entities)) { + entities = []; + + gridAlignedEntities.Add(coord, entities); + } + + Vector3 cellPosition = BLOCK_SIZE * coord.Vector3; + var bounds = new BoundingBox3(cellPosition - new Vector3(HALF_BLOCK, HALF_BLOCK, 0), cellPosition + new Vector3(HALF_BLOCK, HALF_BLOCK, BLOCK_SIZE)); + + entities.Add(new FieldSpawnTile( + Id: new FieldEntityId(0, 0, string.Empty), + Position: cellPosition, + Rotation: Vector3.Zero, + Scale: 1, + Bounds: bounds)); + } + + bool isSurface = !occupancy.isOccupied; + bool isShallow = isSurface && groundOccupancy.isGround; + + if ((!isSurface || isShallow) && gridAlignedEntities.TryGetValue(groundCoord, out List? entityList)) { + for (int i = 0; i < entityList.Count; ++i) { + FieldEntity entity = entityList[i]; + + if (entity is FieldFluidEntity fluid) { + entityList[i] = new FieldFluidEntity( + Id: fluid.Id, + Position: fluid.Position, + Rotation: fluid.Rotation, + Scale: fluid.Scale, + LiquidType: fluid.LiquidType, + Bounds: fluid.Bounds, + MeshLlid: fluid.MeshLlid, + IsShallow: isShallow, + IsSurface: isSurface, + MapAttribute: fluid.MapAttribute); + } + } + } + } + } + } + } + + public void TrimGridSize(Dictionary> gridAlignedEntities, ref Vector3S minIndex, ref Vector3S maxIndex, List unalignedEntities) { + Vector3S gridSize = maxIndex - minIndex + new Vector3S(1, 1, 1); + int cellCount = gridSize.X * gridSize.Y * (gridSize.Z - 1); + int[] axisXCount = new int[gridSize.X]; + int[] axisYCount = new int[gridSize.Y]; + int[] axisZCount = new int[gridSize.Z]; + + foreach ((Vector3S coord, List entityList) in gridAlignedEntities) { + Vector3S index = coord - minIndex; + + axisXCount[index.X] += entityList.Count; + axisYCount[index.Y] += entityList.Count; + axisZCount[index.Z] += entityList.Count; + } + + int trimmedCount = 0; + short trimmedMinX = 0; + short trimmedMinY = 0; + short trimmedMinZ = 0; + short trimmedMaxX = (short) (maxIndex.X - minIndex.X); + short trimmedMaxY = (short) (maxIndex.Y - minIndex.Y); + short trimmedMaxZ = (short) (maxIndex.Z - minIndex.Z); + + for (int i = 0, cumulative = 0; i < axisXCount.Length; i++) { + int currentAxisCount = axisXCount[i]; + int remaining = gridAlignedEntities.Count - cumulative; + cumulative += currentAxisCount; + + // only cull isolated cells + if (currentAxisCount != 0) { + continue; + } + + if (cumulative < AXIS_TRIM_ENTITY_COUNT) { + trimmedMinX = (short) i; + } + + if (remaining < AXIS_TRIM_ENTITY_COUNT) { + trimmedMaxX = (short) (i - 1); + + break; + } + } + + for (int i = 0, cumulative = 0; i < axisYCount.Length; i++) { + int currentAxisCount = axisYCount[i]; + int remaining = gridAlignedEntities.Count - cumulative; + cumulative += currentAxisCount; + + // only cull isolated cells + if (currentAxisCount != 0) { + continue; + } + + if (cumulative < AXIS_TRIM_ENTITY_COUNT) { + trimmedMinY = (short) i; + } + + if (remaining < AXIS_TRIM_ENTITY_COUNT) { + trimmedMaxY = (short) (i - 1); + + break; + } + } + + for (int i = 0, cumulative = 0; i < axisZCount.Length; i++) { + int currentAxisCount = axisZCount[i]; + int remaining = gridAlignedEntities.Count - cumulative; + cumulative += currentAxisCount; + + // only cull isolated cells + if (currentAxisCount != 0) { + continue; + } + + if (cumulative < AXIS_TRIM_ENTITY_COUNT) { + trimmedMinZ = (short) i; + } + + if (remaining < AXIS_TRIM_ENTITY_COUNT) { + trimmedMaxZ = (short) (i - 1); + + break; + } + } + + Vector3S newMinIndex = new Vector3S(trimmedMinX, trimmedMinY, trimmedMinZ) + minIndex; + Vector3S newMaxIndex = new Vector3S(trimmedMaxX, trimmedMaxY, trimmedMaxZ) + minIndex; + Vector3S newGridSize = newMaxIndex - newMinIndex + new Vector3S(1, 1, 1); + int newCellCount = newGridSize.X * newGridSize.Y * newGridSize.Z; + int culledCells = cellCount - newCellCount; + + foreach ((Vector3S coord, List entityList) in gridAlignedEntities) { + Vector3S index = coord - minIndex; + + bool isTrimmed = index.X < trimmedMinX; + isTrimmed |= index.X > trimmedMaxX; + isTrimmed |= index.Y < trimmedMinY; + isTrimmed |= index.Y > trimmedMaxY; + isTrimmed |= index.Z < trimmedMinZ; + isTrimmed |= index.Z > trimmedMaxZ; + + if (isTrimmed && entityList.Count > 0) { + trimmedCount += entityList.Count; + + Vector3 cellPosition = BLOCK_SIZE * coord.Vector3; + BoundingBox3 bounds = entityList.First().Bounds; + + foreach (FieldEntity entity in entityList) { + bounds = bounds.Expand(entity.Bounds); + } + + if (entityList.Count == 1) { + alignedTrimmedEntities.Add(entityList.First()); + + continue; + } + + SortEntityList(entityList); + + // limit the number of nodes in the AABB tree by bundling cells together + FieldCellEntities cell = new FieldCellEntities( + Id: new FieldEntityId(0, 0, string.Empty), + Position: cellPosition, + Rotation: new Vector3(0, 0, 0), + Scale: 1, + Bounds: bounds, + Entities: entityList); + + alignedTrimmedEntities.Add(cell); + } + } + } + + // cell grid contains ints that contain both list start index & entity count for the cell + // top byte is used for entity count, the 3 least significant bytes are used for list start index: CC II II II + // compare cell data with 0 to check if it is empty: 00 00 00 00 + public static (byte count, int startIndex) GetCellInfo(int cellData) { + byte count = (byte) (cellData >> 24); + int startIndex = cellData & 0xFFFFFF; + + return (count, startIndex); + } + + public static int WriteCellInfo(int count, int startIndex) { + return ((count & 0xFF) << 24) | (startIndex & 0xFFFFFF); + } + + public void AddVibrateEntities(List entities) { + foreach (FieldEntity entity in entities) { + if (entity is FieldVibrateEntity vibrate) { + vibrateEntities[vibrate.VibrateIndex] = vibrate; + } + + if (entity is FieldCellEntities cell) { + AddVibrateEntities(cell.Entities); + } + } + } + + public void AddEntities(Dictionary> gridAlignedEntities, Vector3S minIndex, Vector3S maxIndex, List unalignedEntities, int vibrateCount) { + if (minIndex.X == short.MaxValue) { + minIndex = new Vector3S(0, 0, 0); + maxIndex = new Vector3S(0, 0, 0); + } + + for (int i = 0; i < vibrateCount; ++i) { + vibrateEntities.Add(new FieldVibrateEntity( + Id: new FieldEntityId(0, 0, string.Empty), + Position: new Vector3(0, 0, 0), + Rotation: new Vector3(0, 0, 0), + Scale: 1, + Bounds: new BoundingBox3(), + BreakDefense: 0, + BreakTick: 0, + VibrateIndex: i)); + } + + GenerateSpawnLocations(gridAlignedEntities, minIndex, maxIndex, unalignedEntities); + TrimGridSize(gridAlignedEntities, ref minIndex, ref maxIndex, unalignedEntities); + + GridSize = maxIndex - minIndex + new Vector3S(1, 1, 1); + MinIndex = minIndex; + MaxIndex = maxIndex; + alignedEntities.Clear(); + this.unalignedEntities = unalignedEntities; + cellGrid = new int[GridSize.X, GridSize.Y, GridSize.Z]; + + // opting to order list entries by z index first, then by y, and last by x, the same way the memory will be laid out + // this will dramatically speed up both load times and cell access times by storing them in a defragmented format from the start + // the reason why is to reduce cache misses + for (short x = minIndex.X; x <= maxIndex.X; x++) { + for (short y = minIndex.Y; y <= maxIndex.Y; y++) { + for (short z = minIndex.Z; z <= maxIndex.Z; z++) { + Vector3S coord = new Vector3S(x, y, z); + + if (!gridAlignedEntities.TryGetValue(coord, out List? entities) || entities.Count == 0) { + continue; + } + + SortEntityList(entities); + + Vector3S index = coord - minIndex; + + cellGrid[index.X, index.Y, index.Z] = WriteCellInfo(entities.Count, alignedEntities.Count); + alignedEntities.AddRange(entities); + } + } + } + + SortEntityList(alignedTrimmedEntities); + SortEntityList(unalignedEntities); + + AddVibrateEntities(alignedEntities); + AddVibrateEntities(alignedTrimmedEntities); + AddVibrateEntities(unalignedEntities); + + GenerateAabbTree(); + } + + public void GenerateAabbTree() { + // TODO: generate AABB tree from unaligned objects list + } + + public void WriteTo(IByteWriter writer) { + writer.Write(GridSize); + writer.Write(MinIndex); + writer.Write(vibrateEntities.Count); + + for (short x = 0; x < GridSize.X; x++) { + for (short y = 0; y < GridSize.Y; y++) { + for (short z = 0; z < GridSize.Z; z++) { + if (cellGrid[x, y, z] != 0) { + writer.WriteInt(cellGrid[x, y, z]); + + continue; + } + + int emptyCount = 0; + + while (z < GridSize.Z && cellGrid[x, y, z] == 0) { + ++emptyCount; + ++z; + } + + // use list start index as empty count for byte streams + writer.WriteInt(WriteCellInfo(0, emptyCount)); + + --z; // don't skip first occupied cell + } + } + } + + if (writer is ByteWriter byteWriter) { + GridBytesWritten = (ulong) byteWriter.Length; + } + + writer.WriteInt(alignedEntities.Count); + + foreach (FieldEntity entity in alignedEntities) { + WriteTo(entity, writer); + } + + writer.WriteInt(alignedTrimmedEntities.Count); + + foreach (FieldEntity entity in alignedTrimmedEntities) { + WriteTo(entity, writer); + } + + writer.WriteInt(unalignedEntities.Count); + + foreach (FieldEntity entity in unalignedEntities) { + WriteTo(entity, writer); + } + } + #endregion + + #region Serialization + private Vector3S GetWorldGridIndex(Vector3 position) { + int x = (int) Math.Round(position.X) / Constant.BlockSize; + int y = (int) Math.Round(position.Y) / Constant.BlockSize; + int z = (int) Math.Round(position.Z) / Constant.BlockSize; + + return new Vector3S((short) x, (short) y, (short) z); + } + + private bool IsGridAligned(Vector3 position) { + int x = (int) Math.Round(position.X) / Constant.BlockSize; + int y = (int) Math.Round(position.Y) / Constant.BlockSize; + int z = (int) Math.Round(position.Z) / Constant.BlockSize; + + return position.IsNearlyEqual(BLOCK_SIZE * new Vector3(x, y, z), 0.1f); + } + + private bool IsCellBounds(Vector3 position, BoundingBox3 bounds) { + if (!IsGridAligned(position)) { + return false; + } + + bool isMinOnCell = bounds.Min.IsNearlyEqual(position - new Vector3(HALF_BLOCK, HALF_BLOCK, 0), 0.1f); + bool isMaxOnCell = bounds.Max.IsNearlyEqual(position + new Vector3(HALF_BLOCK, HALF_BLOCK, BLOCK_SIZE), 0.1f); + + return isMinOnCell && isMaxOnCell; + } + + public void WriteTo(FieldEntity entity, IByteWriter writer) { + FieldEntityType type = entity switch { + FieldVibrateEntity => FieldEntityType.Vibrate, + FieldSpawnTile => FieldEntityType.SpawnTile, + FieldBoxColliderEntity => FieldEntityType.BoxCollider, + FieldFluidEntity => FieldEntityType.Fluid, + FieldMeshColliderEntity => FieldEntityType.MeshCollider, + FieldCellEntities => FieldEntityType.Cell, + FieldSellableTile => FieldEntityType.SellableTile, + _ => FieldEntityType.Unknown, + }; + + FieldEntityMembers memberFlags = FieldEntityMembers.None; + + memberFlags |= (entity.Id.High == 0 && entity.Id.Low == 0) ? 0 : FieldEntityMembers.Id; + memberFlags |= IsGridAligned(entity.Position) ? 0 : FieldEntityMembers.Position; + memberFlags |= entity.Rotation.IsNearlyEqual(new Vector3(0, 0, 0), 1e-3f) ? 0 : FieldEntityMembers.Rotation; + memberFlags |= entity.Scale.IsNearlyEqual(1, 1e-3f) ? 0 : FieldEntityMembers.Scale; + memberFlags |= IsCellBounds(entity.Position, entity.Bounds) ? 0 : FieldEntityMembers.Bounds; + + switch (entity) { + case FieldMeshColliderEntity meshCollider: + memberFlags |= (meshCollider.MeshLlid == 0) ? 0 : FieldEntityMembers.Llid; + break; + default: + break; + } + + writer.Write(type); + writer.Write(memberFlags); + + if ((memberFlags & FieldEntityMembers.Id) != 0) { + writer.Write(entity.Id.High); + writer.Write(entity.Id.Low); + writer.WriteString(entity.Id.Id); + } + + if ((memberFlags & FieldEntityMembers.Position) != 0) { + writer.Write(entity.Position); + } else { + writer.Write(GetWorldGridIndex(entity.Position)); + } + + if ((memberFlags & FieldEntityMembers.Rotation) != 0) { + writer.Write(entity.Rotation); + } + + if ((memberFlags & FieldEntityMembers.Scale) != 0) { + writer.Write(entity.Scale); + } + + if ((memberFlags & FieldEntityMembers.Bounds) != 0) { + writer.Write(entity.Bounds.Min); + writer.Write(entity.Bounds.Max); + } + + switch (entity) { + case FieldVibrateEntity vibrateEntity: + writer.Write(vibrateEntity.VibrateIndex); + writer.WriteInt(vibrateEntity.BreakDefense); + writer.WriteInt(vibrateEntity.BreakTick); + break; + case FieldSpawnTile spawnTile: + break; + case FieldBoxColliderEntity boxCollider: + writer.Write(boxCollider.Size); + writer.Write(boxCollider.IsWhiteBox); + writer.Write(boxCollider.IsFluid); + writer.Write(boxCollider.MapAttribute); + break; + case FieldMeshColliderEntity meshCollider: + if ((memberFlags & FieldEntityMembers.Llid) != 0) { + writer.Write(meshCollider.MeshLlid); + } + writer.Write(meshCollider.MapAttribute); + if (entity is FieldFluidEntity fluid) { + writer.Write(fluid.LiquidType); + writer.Write(fluid.IsShallow); + writer.Write(fluid.IsSurface); + } + break; + case FieldCellEntities cell: + writer.WriteInt(cell.Entities.Count); + foreach (FieldEntity childEntity in cell.Entities) { + WriteTo(childEntity, writer); + } + break; + case FieldSellableTile sellableTile: + writer.Write(sellableTile.SellableGroup); + break; + default: + throw new InvalidDataException($"Writing unhandled field entity type: {entity.GetType().FullName}"); + } + } + + public void ReadFrom(IByteReader reader) { + GridSize = reader.Read(); + MinIndex = reader.Read(); + MaxIndex = MinIndex + GridSize - new Vector3S(1, 1, 1); + cellGrid = new int[GridSize.X, GridSize.Y, GridSize.Z]; + + alignedEntities.Clear(); + alignedTrimmedEntities.Clear(); + unalignedEntities.Clear(); + vibrateEntities.Clear(); + + int vibrateCount = reader.Read(); + + for (int i = 0; i < vibrateCount; ++i) { + vibrateEntities.Add(new FieldVibrateEntity( + Id: new FieldEntityId(0, 0, string.Empty), + Position: new Vector3(0, 0, 0), + Rotation: new Vector3(0, 0, 0), + Scale: 1, + Bounds: new BoundingBox3(), + BreakDefense: 0, + BreakTick: 0, + VibrateIndex: i)); + } + + for (short x = 0; x < GridSize.X; x++) { + for (short y = 0; y < GridSize.Y; y++) { + for (short z = 0; z < GridSize.Z; z++) { + int cellData = reader.ReadInt(); + (byte count, int startIndex) cell = GetCellInfo(cellData); + + if (cell.count == 0) { + // use list start index as empty count for byte streams + z += (short) (cell.startIndex - 1); + + continue; + } + + cellGrid[x, y, z] = cellData; + } + } + } + + int alignedEntityCount = reader.ReadInt(); + + for (int i = 0; i < alignedEntityCount; ++i) { + alignedEntities.Add(ReadEntity(reader)); + } + + int alignedTrimmedEntityCount = reader.ReadInt(); + + for (int i = 0; i < alignedTrimmedEntityCount; ++i) { + alignedTrimmedEntities.Add(ReadEntity(reader)); + } + + int unalignedEntityCount = reader.ReadInt(); + + for (int i = 0; i < unalignedEntityCount; ++i) { + unalignedEntities.Add(ReadEntity(reader)); + } + + GenerateAabbTree(); + + AddVibrateEntities(alignedEntities); + AddVibrateEntities(alignedTrimmedEntities); + AddVibrateEntities(unalignedEntities); + } + + public FieldEntity ReadEntity(IByteReader reader) { + var type = reader.Read(); + var memberFlags = reader.Read(); + + var id = new FieldEntityId(0, 0, string.Empty); + Vector3 position; + var rotation = new Vector3(0, 0, 0); + float scale = 1; + BoundingBox3 bounds; + uint llid = 0; + + if ((memberFlags & FieldEntityMembers.Id) != 0) { + id = new FieldEntityId(reader.Read(), reader.Read(), reader.ReadString()); + } + + if ((memberFlags & FieldEntityMembers.Position) != 0) { + position = reader.Read(); + } else { + position = BLOCK_SIZE * reader.Read().Vector3; + } + + if ((memberFlags & FieldEntityMembers.Rotation) != 0) { + rotation = reader.Read(); + } + + if ((memberFlags & FieldEntityMembers.Scale) != 0) { + scale = reader.ReadFloat(); + } + + if ((memberFlags & FieldEntityMembers.Bounds) != 0) { + bounds = new BoundingBox3( + min: reader.Read(), + max: reader.Read()); + } else { + bounds = new BoundingBox3( + min: position - new Vector3(HALF_BLOCK, HALF_BLOCK, 0), + max: position + new Vector3(HALF_BLOCK, HALF_BLOCK, BLOCK_SIZE)); + } + + switch (type) { + case FieldEntityType.Vibrate: + return new FieldVibrateEntity( + Id: id, + Position: position, + Rotation: rotation, + Scale: scale, + Bounds: bounds, + VibrateIndex: reader.ReadInt(), + BreakDefense: reader.ReadInt(), + BreakTick: reader.ReadInt()); + case FieldEntityType.SpawnTile: + return new FieldSpawnTile( + Id: id, + Position: position, + Rotation: rotation, + Scale: scale, + Bounds: bounds); + case FieldEntityType.BoxCollider: + return new FieldBoxColliderEntity( + Id: id, + Position: position, + Rotation: rotation, + Scale: scale, + Bounds: bounds, + Size: reader.Read(), + IsWhiteBox: reader.Read(), + IsFluid: reader.Read(), + MapAttribute: reader.Read()); + case FieldEntityType.MeshCollider: + if ((memberFlags & FieldEntityMembers.Llid) != 0) { + llid = reader.Read(); + } + + return new FieldMeshColliderEntity( + Id: id, + Position: position, + Rotation: rotation, + Scale: scale, + Bounds: bounds, + MeshLlid: llid, + MapAttribute: reader.Read()); + case FieldEntityType.Fluid: + if ((memberFlags & FieldEntityMembers.Llid) != 0) { + llid = reader.Read(); + } + var mapAttribute = reader.Read(); + return new FieldFluidEntity( + Id: id, + Position: position, + Rotation: rotation, + Scale: scale, + LiquidType: reader.Read(), + Bounds: bounds, + MeshLlid: llid, + IsShallow: reader.Read(), + IsSurface: reader.Read(), + MapAttribute: mapAttribute); + case FieldEntityType.Cell: + int childCount = reader.ReadInt(); + var children = new List(); + for (int i = 0; i < childCount; ++i) { + children.Add(ReadEntity(reader)); + } + return new FieldCellEntities( + Id: id, + Position: position, + Rotation: rotation, + Scale: scale, + Bounds: bounds, + Entities: children); + case FieldEntityType.SellableTile: + return new FieldSellableTile( + Id: id, + Position: position, + Rotation: rotation, + Scale: scale, + Bounds: bounds, + SellableGroup: reader.Read()); + default: + throw new InvalidDataException($"Reading unhandled field entity type: {type}"); + } + } + #endregion + +} diff --git a/Maple2.Model/Game/FishEntry.cs b/Maple2.Model/Game/FishEntry.cs index 45fadfc55..64332bd44 100644 --- a/Maple2.Model/Game/FishEntry.cs +++ b/Maple2.Model/Game/FishEntry.cs @@ -1,22 +1,22 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class FishEntry : IByteSerializable { - public int Id; - public int TotalCaught; - public int TotalPrizeFish; - public int LargestSize; - - public FishEntry(int id) { - Id = id; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteInt(Id); - writer.WriteInt(TotalCaught); - writer.WriteInt(TotalPrizeFish); - writer.WriteInt(LargestSize); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class FishEntry : IByteSerializable { + public int Id; + public int TotalCaught; + public int TotalPrizeFish; + public int LargestSize; + + public FishEntry(int id) { + Id = id; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteInt(Id); + writer.WriteInt(TotalCaught); + writer.WriteInt(TotalPrizeFish); + writer.WriteInt(LargestSize); + } +} diff --git a/Maple2.Model/Game/GlobalPortal.cs b/Maple2.Model/Game/GlobalPortal.cs index 40cba1374..7f827c5d9 100644 --- a/Maple2.Model/Game/GlobalPortal.cs +++ b/Maple2.Model/Game/GlobalPortal.cs @@ -1,15 +1,15 @@ -using Maple2.Model.Metadata; - -namespace Maple2.Model.Game; - -public class GlobalPortal { - public int MetadataId => Metadata.Id; - public int Id; - public GlobalPortalMetadata Metadata; - public long EndTick; - - public GlobalPortal(GlobalPortalMetadata metadata, int id) { - Metadata = metadata; - Id = id; - } -} +using Maple2.Model.Metadata; + +namespace Maple2.Model.Game; + +public class GlobalPortal { + public int MetadataId => Metadata.Id; + public int Id; + public GlobalPortalMetadata Metadata; + public long EndTick; + + public GlobalPortal(GlobalPortalMetadata metadata, int id) { + Metadata = metadata; + Id = id; + } +} diff --git a/Maple2.Model/Game/GroupChat/GroupChat.cs b/Maple2.Model/Game/GroupChat/GroupChat.cs index 3a71e419e..19c1fc7dc 100644 --- a/Maple2.Model/Game/GroupChat/GroupChat.cs +++ b/Maple2.Model/Game/GroupChat/GroupChat.cs @@ -1,15 +1,15 @@ -using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; - -namespace Maple2.Model.Game.GroupChat; - -public class GroupChat { - public required int Id { get; init; } - public readonly ConcurrentDictionary Members; - - [SetsRequiredMembers] - public GroupChat(int id) { - Id = id; - Members = new ConcurrentDictionary(); - } -} +using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; + +namespace Maple2.Model.Game.GroupChat; + +public class GroupChat { + public required int Id { get; init; } + public readonly ConcurrentDictionary Members; + + [SetsRequiredMembers] + public GroupChat(int id) { + Id = id; + Members = new ConcurrentDictionary(); + } +} diff --git a/Maple2.Model/Game/GroupChat/GroupChatMember.cs b/Maple2.Model/Game/GroupChat/GroupChatMember.cs index 802da8534..3edd8a6a2 100644 --- a/Maple2.Model/Game/GroupChat/GroupChatMember.cs +++ b/Maple2.Model/Game/GroupChat/GroupChatMember.cs @@ -1,15 +1,15 @@ -namespace Maple2.Model.Game.GroupChat; - -public class GroupChatMember : IDisposable { - public required PlayerInfo Info; - public long CharacterId => Info.CharacterId; - public string Name => Info.Name; - - public CancellationTokenSource? TokenSource; - - public void Dispose() { - TokenSource?.Cancel(); - TokenSource?.Dispose(); - TokenSource = null; - } -} +namespace Maple2.Model.Game.GroupChat; + +public class GroupChatMember : IDisposable { + public required PlayerInfo Info; + public long CharacterId => Info.CharacterId; + public string Name => Info.Name; + + public CancellationTokenSource? TokenSource; + + public void Dispose() { + TokenSource?.Cancel(); + TokenSource?.Dispose(); + TokenSource = null; + } +} diff --git a/Maple2.Model/Game/Guild/Guild.cs b/Maple2.Model/Game/Guild/Guild.cs index 98fe754e6..eb482ffc8 100644 --- a/Maple2.Model/Game/Guild/Guild.cs +++ b/Maple2.Model/Game/Guild/Guild.cs @@ -1,138 +1,138 @@ -using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game; - -public class Guild : IByteSerializable { - public byte Capacity = 60; - - public required long Id { get; init; } - public required string Name; - public required long LeaderAccountId; - public required long LeaderCharacterId; - public required string LeaderName; - - public string Emblem = string.Empty; - public string Notice = string.Empty; - public long CreationTime; - public AchievementInfo AchievementInfo; - public GuildFocus Focus; - public int Experience; - public int Funds; - public int HouseRank; - public int HouseTheme; - - public readonly ConcurrentDictionary Members; - public required IList Ranks { get; init; } - public required IList Buffs { get; init; } - public required IList Events { get; init; } - public required IList Posters { get; init; } - public required IList Npcs { get; init; } - public required IList Bank { get; init; } - - [SetsRequiredMembers] - public Guild(long id, string name, long leaderAccountId, long leaderCharacterId, string leaderName) { - Id = id; - Name = name; - LeaderAccountId = leaderAccountId; - LeaderCharacterId = leaderCharacterId; - LeaderName = leaderName; - - Members = new ConcurrentDictionary(); - Ranks = new List(); - Buffs = new List(); - Events = new List(); - Posters = new List(); - Npcs = new List(); - Bank = new List(); - } - - [SetsRequiredMembers] - public Guild(long id, string name, GuildMember leader) : this(id, name, leader.AccountId, leader.CharacterId, leader.Name) { } - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(Id); - writer.WriteUnicodeString(Name); - writer.WriteUnicodeString(Emblem); - writer.WriteByte(Capacity); - writer.WriteUnicodeString(); - writer.WriteUnicodeString(Notice); - writer.WriteLong(LeaderAccountId); - writer.WriteLong(LeaderCharacterId); - writer.WriteUnicodeString(LeaderName); - writer.WriteLong(CreationTime); - writer.WriteByte(1); - writer.WriteInt(1000); - writer.WriteInt(); - writer.WriteInt(); - writer.WriteInt(); - writer.WriteInt(); - writer.WriteInt(); - writer.WriteInt(); - writer.WriteInt(); - writer.WriteByte(1); - writer.Write(Focus); - writer.WriteInt(Experience); - writer.WriteInt(Funds); - writer.WriteBool(false); - writer.WriteInt(); - - writer.WriteByte((byte) Members.Count); - foreach (GuildMember member in Members.Values) { - writer.WriteClass(member); - } - writer.WriteByte((byte) Ranks.Count); - foreach (GuildRank rank in Ranks) { - writer.WriteClass(rank); - } - writer.WriteByte((byte) Buffs.Count); - foreach (GuildBuff buff in Buffs) { - writer.Write(buff); - } - writer.WriteByte((byte) Events.Count); - foreach (GuildEvent @event in Events) { - writer.Write(@event); - } - - writer.WriteInt(HouseRank); - writer.WriteInt(HouseTheme); - - writer.WriteInt(Posters.Count); - foreach (GuildPoster poster in Posters) { - writer.WriteClass(poster); - } - writer.WriteByte((byte) Npcs.Count); - foreach (GuildNpc npc in Npcs) { - writer.Write(npc); - } - - writer.WriteBool(false); // GuildNpcShopProducts - - writer.WriteInt(Bank.Count); - foreach (RewardItem item in Bank) { - writer.Write(item); - } - - writer.WriteInt(); - writer.WriteUnicodeString(); - writer.WriteLong(); - writer.WriteLong(); - for (int i = 0; i < 7; i++) { - writer.WriteInt(); - } - } -} - -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 16)] -public readonly record struct GuildBuff(int Id, int Level, long ExpiryTime); - -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] -public readonly record struct GuildEvent(int Id, int Value); - -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] -public readonly record struct GuildNpc(GuildNpcType Type, int Level); +using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game; + +public class Guild : IByteSerializable { + public byte Capacity = 60; + + public required long Id { get; init; } + public required string Name; + public required long LeaderAccountId; + public required long LeaderCharacterId; + public required string LeaderName; + + public string Emblem = string.Empty; + public string Notice = string.Empty; + public long CreationTime; + public AchievementInfo AchievementInfo; + public GuildFocus Focus; + public int Experience; + public int Funds; + public int HouseRank; + public int HouseTheme; + + public readonly ConcurrentDictionary Members; + public required IList Ranks { get; init; } + public required IList Buffs { get; init; } + public required IList Events { get; init; } + public required IList Posters { get; init; } + public required IList Npcs { get; init; } + public required IList Bank { get; init; } + + [SetsRequiredMembers] + public Guild(long id, string name, long leaderAccountId, long leaderCharacterId, string leaderName) { + Id = id; + Name = name; + LeaderAccountId = leaderAccountId; + LeaderCharacterId = leaderCharacterId; + LeaderName = leaderName; + + Members = new ConcurrentDictionary(); + Ranks = new List(); + Buffs = new List(); + Events = new List(); + Posters = new List(); + Npcs = new List(); + Bank = new List(); + } + + [SetsRequiredMembers] + public Guild(long id, string name, GuildMember leader) : this(id, name, leader.AccountId, leader.CharacterId, leader.Name) { } + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(Id); + writer.WriteUnicodeString(Name); + writer.WriteUnicodeString(Emblem); + writer.WriteByte(Capacity); + writer.WriteUnicodeString(); + writer.WriteUnicodeString(Notice); + writer.WriteLong(LeaderAccountId); + writer.WriteLong(LeaderCharacterId); + writer.WriteUnicodeString(LeaderName); + writer.WriteLong(CreationTime); + writer.WriteByte(1); + writer.WriteInt(1000); + writer.WriteInt(); + writer.WriteInt(); + writer.WriteInt(); + writer.WriteInt(); + writer.WriteInt(); + writer.WriteInt(); + writer.WriteInt(); + writer.WriteByte(1); + writer.Write(Focus); + writer.WriteInt(Experience); + writer.WriteInt(Funds); + writer.WriteBool(false); + writer.WriteInt(); + + writer.WriteByte((byte) Members.Count); + foreach (GuildMember member in Members.Values) { + writer.WriteClass(member); + } + writer.WriteByte((byte) Ranks.Count); + foreach (GuildRank rank in Ranks) { + writer.WriteClass(rank); + } + writer.WriteByte((byte) Buffs.Count); + foreach (GuildBuff buff in Buffs) { + writer.Write(buff); + } + writer.WriteByte((byte) Events.Count); + foreach (GuildEvent @event in Events) { + writer.Write(@event); + } + + writer.WriteInt(HouseRank); + writer.WriteInt(HouseTheme); + + writer.WriteInt(Posters.Count); + foreach (GuildPoster poster in Posters) { + writer.WriteClass(poster); + } + writer.WriteByte((byte) Npcs.Count); + foreach (GuildNpc npc in Npcs) { + writer.Write(npc); + } + + writer.WriteBool(false); // GuildNpcShopProducts + + writer.WriteInt(Bank.Count); + foreach (RewardItem item in Bank) { + writer.Write(item); + } + + writer.WriteInt(); + writer.WriteUnicodeString(); + writer.WriteLong(); + writer.WriteLong(); + for (int i = 0; i < 7; i++) { + writer.WriteInt(); + } + } +} + +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 16)] +public readonly record struct GuildBuff(int Id, int Level, long ExpiryTime); + +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] +public readonly record struct GuildEvent(int Id, int Value); + +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] +public readonly record struct GuildNpc(GuildNpcType Type, int Level); diff --git a/Maple2.Model/Game/Guild/GuildApplication.cs b/Maple2.Model/Game/Guild/GuildApplication.cs index b6b25acfe..5e69953f5 100644 --- a/Maple2.Model/Game/Guild/GuildApplication.cs +++ b/Maple2.Model/Game/Guild/GuildApplication.cs @@ -1,26 +1,26 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class GuildApplication : IByteSerializable { - public required long Id { get; init; } - public required Guild Guild; - public required PlayerInfo Applicant; - public long CreationTime; - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(Id); - writer.WriteLong(Guild.Id); - writer.WriteLong(Applicant.CharacterId); - writer.WriteLong(Applicant.AccountId); - writer.WriteUnicodeString(Applicant.Name); - writer.WriteUnicodeString(Applicant.Picture); - writer.Write(Applicant.Job); - writer.WriteInt((int) Applicant.Job.Code()); - writer.WriteInt(Applicant.Level); - writer.Write(Applicant.AchievementInfo); - writer.WriteLong(CreationTime); - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class GuildApplication : IByteSerializable { + public required long Id { get; init; } + public required Guild Guild; + public required PlayerInfo Applicant; + public long CreationTime; + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(Id); + writer.WriteLong(Guild.Id); + writer.WriteLong(Applicant.CharacterId); + writer.WriteLong(Applicant.AccountId); + writer.WriteUnicodeString(Applicant.Name); + writer.WriteUnicodeString(Applicant.Picture); + writer.Write(Applicant.Job); + writer.WriteInt((int) Applicant.Job.Code()); + writer.WriteInt(Applicant.Level); + writer.Write(Applicant.AchievementInfo); + writer.WriteLong(CreationTime); + } +} diff --git a/Maple2.Model/Game/Guild/GuildInvite.cs b/Maple2.Model/Game/Guild/GuildInvite.cs index 9d0a8b784..1f213e4c4 100644 --- a/Maple2.Model/Game/Guild/GuildInvite.cs +++ b/Maple2.Model/Game/Guild/GuildInvite.cs @@ -1,40 +1,40 @@ -using System.ComponentModel; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class GuildInvite : IByteSerializable, IByteDeserializable { - public enum Response : byte { - Accept = 0, - [Description("{0} has rejected the guild invitation.")] - RejectInvite = 1, - [Description("{0} cannot receive the guild invitation at the moment.")] - RejectLogout = 2, - [Description("{0} cannot receive the guild invitation at the moment.")] - RejectTimeout = 3, - [Description("Enter at least 2 letters.")] - InvalidName = byte.MaxValue, - } - - public long GuildId { get; set; } - public string GuildName { get; set; } = string.Empty; - public string SenderName { get; set; } = string.Empty; - public string ReceiverName { get; set; } = string.Empty; - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(GuildId); - writer.WriteUnicodeString(GuildName); - writer.WriteUnicodeString(); - writer.WriteUnicodeString(SenderName); - writer.WriteUnicodeString(ReceiverName); - } - - public void ReadFrom(IByteReader reader) { - GuildId = reader.ReadLong(); - GuildName = reader.ReadUnicodeString(); - reader.ReadUnicodeString(); - SenderName = reader.ReadUnicodeString(); - ReceiverName = reader.ReadUnicodeString(); - } -} +using System.ComponentModel; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class GuildInvite : IByteSerializable, IByteDeserializable { + public enum Response : byte { + Accept = 0, + [Description("{0} has rejected the guild invitation.")] + RejectInvite = 1, + [Description("{0} cannot receive the guild invitation at the moment.")] + RejectLogout = 2, + [Description("{0} cannot receive the guild invitation at the moment.")] + RejectTimeout = 3, + [Description("Enter at least 2 letters.")] + InvalidName = byte.MaxValue, + } + + public long GuildId { get; set; } + public string GuildName { get; set; } = string.Empty; + public string SenderName { get; set; } = string.Empty; + public string ReceiverName { get; set; } = string.Empty; + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(GuildId); + writer.WriteUnicodeString(GuildName); + writer.WriteUnicodeString(); + writer.WriteUnicodeString(SenderName); + writer.WriteUnicodeString(ReceiverName); + } + + public void ReadFrom(IByteReader reader) { + GuildId = reader.ReadLong(); + GuildName = reader.ReadUnicodeString(); + reader.ReadUnicodeString(); + SenderName = reader.ReadUnicodeString(); + ReceiverName = reader.ReadUnicodeString(); + } +} diff --git a/Maple2.Model/Game/Guild/GuildMember.cs b/Maple2.Model/Game/Guild/GuildMember.cs index 10a40cc19..e77e0cc4c 100644 --- a/Maple2.Model/Game/Guild/GuildMember.cs +++ b/Maple2.Model/Game/Guild/GuildMember.cs @@ -1,78 +1,78 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class GuildMember : IByteSerializable, IDisposable { - private const byte TYPE = 3; - - public long GuildId { get; init; } - - public required PlayerInfo Info; - - public string Message = string.Empty; - public byte Rank; - public long JoinTime; - public long CheckinTime; - public long DonationTime; - public int WeeklyContribution; - public int TotalContribution; - public int DailyDonationCount; - - public long AccountId => Info.AccountId; - public long CharacterId => Info.CharacterId; - public string Name => Info.Name; - - public CancellationTokenSource? TokenSource; - - public void WriteTo(IByteWriter writer) { - writer.WriteByte(TYPE); - writer.WriteByte(Rank); - writer.WriteLong(CharacterId); - - WriteInfo(writer, Info); - - writer.WriteUnicodeString(Message); - writer.WriteLong(JoinTime); - writer.WriteLong(Info.LastOnlineTime); - writer.WriteLong(CheckinTime); - writer.WriteInt(); - writer.WriteInt(); - writer.WriteInt(WeeklyContribution); - writer.WriteInt(TotalContribution); - writer.WriteInt(DailyDonationCount); - writer.WriteLong(DonationTime); - writer.WriteInt(); - // for (int i = 0; i < count; i++) { - // writer.WriteInt(); - // writer.WriteInt(); - // } - writer.WriteBool(!Info.Online); - } - - public static void WriteInfo(IByteWriter writer, PlayerInfo info) { - writer.WriteLong(info.AccountId); - writer.WriteLong(info.CharacterId); - writer.WriteUnicodeString(info.Name); - writer.Write(info.Gender); - writer.WriteInt((int) info.Job.Code()); - writer.Write(info.Job); - writer.WriteShort(info.Level); - writer.WriteInt(info.GearScore); - writer.WriteInt(info.MapId); - writer.WriteShort(info.Channel); - writer.WriteUnicodeString(info.Picture); - writer.WriteInt(info.PlotMapId); - writer.WriteInt(info.PlotNumber); - writer.WriteInt(info.ApartmentNumber); - writer.WriteLong(info.PlotExpiryTime); - writer.Write(info.AchievementInfo); - } - - public void Dispose() { - TokenSource?.Cancel(); - TokenSource?.Dispose(); - TokenSource = null; - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class GuildMember : IByteSerializable, IDisposable { + private const byte TYPE = 3; + + public long GuildId { get; init; } + + public required PlayerInfo Info; + + public string Message = string.Empty; + public byte Rank; + public long JoinTime; + public long CheckinTime; + public long DonationTime; + public int WeeklyContribution; + public int TotalContribution; + public int DailyDonationCount; + + public long AccountId => Info.AccountId; + public long CharacterId => Info.CharacterId; + public string Name => Info.Name; + + public CancellationTokenSource? TokenSource; + + public void WriteTo(IByteWriter writer) { + writer.WriteByte(TYPE); + writer.WriteByte(Rank); + writer.WriteLong(CharacterId); + + WriteInfo(writer, Info); + + writer.WriteUnicodeString(Message); + writer.WriteLong(JoinTime); + writer.WriteLong(Info.LastOnlineTime); + writer.WriteLong(CheckinTime); + writer.WriteInt(); + writer.WriteInt(); + writer.WriteInt(WeeklyContribution); + writer.WriteInt(TotalContribution); + writer.WriteInt(DailyDonationCount); + writer.WriteLong(DonationTime); + writer.WriteInt(); + // for (int i = 0; i < count; i++) { + // writer.WriteInt(); + // writer.WriteInt(); + // } + writer.WriteBool(!Info.Online); + } + + public static void WriteInfo(IByteWriter writer, PlayerInfo info) { + writer.WriteLong(info.AccountId); + writer.WriteLong(info.CharacterId); + writer.WriteUnicodeString(info.Name); + writer.Write(info.Gender); + writer.WriteInt((int) info.Job.Code()); + writer.Write(info.Job); + writer.WriteShort(info.Level); + writer.WriteInt(info.GearScore); + writer.WriteInt(info.MapId); + writer.WriteShort(info.Channel); + writer.WriteUnicodeString(info.Picture); + writer.WriteInt(info.PlotMapId); + writer.WriteInt(info.PlotNumber); + writer.WriteInt(info.ApartmentNumber); + writer.WriteLong(info.PlotExpiryTime); + writer.Write(info.AchievementInfo); + } + + public void Dispose() { + TokenSource?.Cancel(); + TokenSource?.Dispose(); + TokenSource = null; + } +} diff --git a/Maple2.Model/Game/Guild/GuildPoster.cs b/Maple2.Model/Game/Guild/GuildPoster.cs index 329c05580..2cbc73ca2 100644 --- a/Maple2.Model/Game/Guild/GuildPoster.cs +++ b/Maple2.Model/Game/Guild/GuildPoster.cs @@ -1,19 +1,19 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class GuildPoster : IByteSerializable { - public int Id; - public string Picture = string.Empty; - public long OwnerId; - public string OwnerName = string.Empty; - public long ResourceId; - - public void WriteTo(IByteWriter writer) { - writer.WriteInt(Id); - writer.WriteUnicodeString(Picture); - writer.WriteLong(OwnerId); - writer.WriteUnicodeString(OwnerName); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class GuildPoster : IByteSerializable { + public int Id; + public string Picture = string.Empty; + public long OwnerId; + public string OwnerName = string.Empty; + public long ResourceId; + + public void WriteTo(IByteWriter writer) { + writer.WriteInt(Id); + writer.WriteUnicodeString(Picture); + writer.WriteLong(OwnerId); + writer.WriteUnicodeString(OwnerName); + } +} diff --git a/Maple2.Model/Game/Guild/GuildRank.cs b/Maple2.Model/Game/Guild/GuildRank.cs index f97fad69b..b72e26c79 100644 --- a/Maple2.Model/Game/Guild/GuildRank.cs +++ b/Maple2.Model/Game/Guild/GuildRank.cs @@ -1,23 +1,23 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class GuildRank : IByteSerializable, IByteDeserializable { - public required byte Id; - public required string Name; - public GuildPermission Permission = GuildPermission.Default; - - public void WriteTo(IByteWriter writer) { - writer.WriteByte(Id); - writer.WriteUnicodeString(Name); - writer.Write(Permission); - } - - public void ReadFrom(IByteReader reader) { - Id = reader.ReadByte(); - Name = reader.ReadUnicodeString(); - Permission = reader.Read(); - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class GuildRank : IByteSerializable, IByteDeserializable { + public required byte Id; + public required string Name; + public GuildPermission Permission = GuildPermission.Default; + + public void WriteTo(IByteWriter writer) { + writer.WriteByte(Id); + writer.WriteUnicodeString(Name); + writer.Write(Permission); + } + + public void ReadFrom(IByteReader reader) { + Id = reader.ReadByte(); + Name = reader.ReadUnicodeString(); + Permission = reader.Read(); + } +} diff --git a/Maple2.Model/Game/IFieldProperty.cs b/Maple2.Model/Game/IFieldProperty.cs index 7aad5bcff..4302da1b2 100644 --- a/Maple2.Model/Game/IFieldProperty.cs +++ b/Maple2.Model/Game/IFieldProperty.cs @@ -1,150 +1,150 @@ -using Maple2.Model.Common; -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public interface IFieldProperty : IByteSerializable { - public FieldProperty Type { get; } -} - -public class FieldPropertyGravity : IFieldProperty, IByteDeserializable { - public FieldProperty Type => FieldProperty.Gravity; - - public float Gravity { get; private set; } - - public FieldPropertyGravity(float gravity) { - Gravity = gravity; - } - - public void WriteTo(IByteWriter writer) { - writer.Write(Type); - writer.WriteFloat(Gravity); - } - - public void ReadFrom(IByteReader reader) { - reader.ReadByte(); - Gravity = reader.ReadFloat(); - } -} - -public class FieldPropertyMusicConcert : IFieldProperty { - public FieldProperty Type => FieldProperty.MusicConcert; - - public long CharacterId { get; init; } - public int Unknown { get; init; } // ServerTicks? - - public void WriteTo(IByteWriter writer) { - writer.Write(Type); - writer.WriteLong(CharacterId); - writer.WriteInt(Unknown); - } -} - -public class FieldPropertyHidePlayer : IFieldProperty { - public FieldProperty Type => FieldProperty.HidePlayer; - - public void WriteTo(IByteWriter writer) { - writer.Write(Type); - } -} - -public class FieldPropertyLockPlayer : IFieldProperty { - public FieldProperty Type => FieldProperty.LockPlayer; - - public void WriteTo(IByteWriter writer) { - writer.Write(Type); - } -} - -public class FieldPropertyUserTagSymbol : IFieldProperty { - public FieldProperty Type => FieldProperty.UserTagSymbol; - - public required string Symbol1 { get; init; } - public required string Symbol2 { get; init; } - - public void WriteTo(IByteWriter writer) { - writer.Write(Type); - writer.WriteUnicodeString(Symbol1); - writer.WriteUnicodeString(Symbol2); - } -} - -public class FieldPropertySightRange : IFieldProperty { - public FieldProperty Type => FieldProperty.SightRange; - - public float Range { get; set; } = 450; - public float[] Fades { get; init; } = new float[3]; - public bool Unknown { get; set; } - public byte Opacity { get; set; } - public bool Opaque { get; set; } = false; - - public void WriteTo(IByteWriter writer) { - writer.Write(Type); - writer.WriteFloat(Range); - foreach (float fade in Fades) { - writer.WriteFloat(fade); - } - writer.WriteBool(Unknown); - writer.WriteByte(Opacity); - writer.WriteBool(Opaque); - } -} - -public class FieldPropertyWeather : IFieldProperty { - public FieldProperty Type => FieldProperty.Weather; - - public WeatherType WeatherType { get; init; } - - public void WriteTo(IByteWriter writer) { - writer.Write(Type); - writer.Write(WeatherType); - } -} - -public class FieldPropertyAmbientLight : IFieldProperty { - public FieldProperty Type => FieldProperty.AmbientLight; - - public Byte3 Color { get; init; } - - public void WriteTo(IByteWriter writer) { - writer.Write(Type); - writer.Write(Color); - } -} - -public class FieldPropertyDirectionalLight : IFieldProperty { - public FieldProperty Type => FieldProperty.DirectionalLight; - - public Byte3 DiffuseColor { get; init; } - public Byte3 SpecularColor { get; init; } - - public void WriteTo(IByteWriter writer) { - writer.Write(Type); - writer.Write(DiffuseColor); - writer.Write(SpecularColor); - } -} - -public class FieldPropertyLocalCamera : IFieldProperty { - public FieldProperty Type => FieldProperty.LocalCamera; - - public bool Enabled { get; init; } - - public void WriteTo(IByteWriter writer) { - writer.Write(Type); - writer.WriteBool(Enabled); - } -} - -public class FieldPropertyPhotoStudio : IFieldProperty { - public FieldProperty Type => FieldProperty.PhotoStudio; - - public bool Enabled { get; init; } - - public void WriteTo(IByteWriter writer) { - writer.Write(Type); - writer.WriteBool(Enabled); - } -} +using Maple2.Model.Common; +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public interface IFieldProperty : IByteSerializable { + public FieldProperty Type { get; } +} + +public class FieldPropertyGravity : IFieldProperty, IByteDeserializable { + public FieldProperty Type => FieldProperty.Gravity; + + public float Gravity { get; private set; } + + public FieldPropertyGravity(float gravity) { + Gravity = gravity; + } + + public void WriteTo(IByteWriter writer) { + writer.Write(Type); + writer.WriteFloat(Gravity); + } + + public void ReadFrom(IByteReader reader) { + reader.ReadByte(); + Gravity = reader.ReadFloat(); + } +} + +public class FieldPropertyMusicConcert : IFieldProperty { + public FieldProperty Type => FieldProperty.MusicConcert; + + public long CharacterId { get; init; } + public int Unknown { get; init; } // ServerTicks? + + public void WriteTo(IByteWriter writer) { + writer.Write(Type); + writer.WriteLong(CharacterId); + writer.WriteInt(Unknown); + } +} + +public class FieldPropertyHidePlayer : IFieldProperty { + public FieldProperty Type => FieldProperty.HidePlayer; + + public void WriteTo(IByteWriter writer) { + writer.Write(Type); + } +} + +public class FieldPropertyLockPlayer : IFieldProperty { + public FieldProperty Type => FieldProperty.LockPlayer; + + public void WriteTo(IByteWriter writer) { + writer.Write(Type); + } +} + +public class FieldPropertyUserTagSymbol : IFieldProperty { + public FieldProperty Type => FieldProperty.UserTagSymbol; + + public required string Symbol1 { get; init; } + public required string Symbol2 { get; init; } + + public void WriteTo(IByteWriter writer) { + writer.Write(Type); + writer.WriteUnicodeString(Symbol1); + writer.WriteUnicodeString(Symbol2); + } +} + +public class FieldPropertySightRange : IFieldProperty { + public FieldProperty Type => FieldProperty.SightRange; + + public float Range { get; set; } = 450; + public float[] Fades { get; init; } = new float[3]; + public bool Unknown { get; set; } + public byte Opacity { get; set; } + public bool Opaque { get; set; } = false; + + public void WriteTo(IByteWriter writer) { + writer.Write(Type); + writer.WriteFloat(Range); + foreach (float fade in Fades) { + writer.WriteFloat(fade); + } + writer.WriteBool(Unknown); + writer.WriteByte(Opacity); + writer.WriteBool(Opaque); + } +} + +public class FieldPropertyWeather : IFieldProperty { + public FieldProperty Type => FieldProperty.Weather; + + public WeatherType WeatherType { get; init; } + + public void WriteTo(IByteWriter writer) { + writer.Write(Type); + writer.Write(WeatherType); + } +} + +public class FieldPropertyAmbientLight : IFieldProperty { + public FieldProperty Type => FieldProperty.AmbientLight; + + public Byte3 Color { get; init; } + + public void WriteTo(IByteWriter writer) { + writer.Write(Type); + writer.Write(Color); + } +} + +public class FieldPropertyDirectionalLight : IFieldProperty { + public FieldProperty Type => FieldProperty.DirectionalLight; + + public Byte3 DiffuseColor { get; init; } + public Byte3 SpecularColor { get; init; } + + public void WriteTo(IByteWriter writer) { + writer.Write(Type); + writer.Write(DiffuseColor); + writer.Write(SpecularColor); + } +} + +public class FieldPropertyLocalCamera : IFieldProperty { + public FieldProperty Type => FieldProperty.LocalCamera; + + public bool Enabled { get; init; } + + public void WriteTo(IByteWriter writer) { + writer.Write(Type); + writer.WriteBool(Enabled); + } +} + +public class FieldPropertyPhotoStudio : IFieldProperty { + public FieldProperty Type => FieldProperty.PhotoStudio; + + public bool Enabled { get; init; } + + public void WriteTo(IByteWriter writer) { + writer.Write(Type); + writer.WriteBool(Enabled); + } +} diff --git a/Maple2.Model/Game/InteractObject.cs b/Maple2.Model/Game/InteractObject.cs index 625f9c7f7..6913ef656 100644 --- a/Maple2.Model/Game/InteractObject.cs +++ b/Maple2.Model/Game/InteractObject.cs @@ -1,126 +1,126 @@ -using System.Numerics; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public interface IInteractObject : IByteSerializable { - public InteractType Type { get; } - public string EntityId { get; } - public int Id { get; } -} - -public abstract class InteractObject : IInteractObject where T : InteractObject { - public abstract InteractType Type { get; } - - protected T Metadata { get; init; } - public int Id { get; init; } - public string Model { get; init; } = ""; - public string Asset { get; init; } = ""; - public string NormalState { get; init; } = ""; - public string Reactable { get; init; } = ""; - public float Scale { get; init; } = 1f; - - public string EntityId { get; init; } - - protected InteractObject(string entityId, T metadata) { - EntityId = entityId; - Metadata = metadata; - Id = metadata.InteractId; - } - - public virtual void WriteTo(IByteWriter writer) { - writer.WriteString(EntityId); - writer.Write(InteractState.Reactable); - writer.Write(Type); - writer.WriteInt(Id); - writer.Write(Metadata.Position); - writer.Write(Metadata.Rotation); - writer.WriteUnicodeString(Model); // e.g. InteractMeshObject - writer.WriteUnicodeString(Asset); // e.g. interaction_chestA_02 - writer.WriteUnicodeString(NormalState); // e.g. Opened_A - writer.WriteUnicodeString(Reactable); // e.g. Idle_A - writer.WriteFloat(Scale); - writer.WriteBool(false); - } -} - -public sealed class InteractMeshObject(string entityId, Ms2InteractMesh metadata) : InteractObject(entityId, metadata) { - public override InteractType Type => InteractType.Mesh; - -} - -public sealed class InteractTelescopeObject(string entityId, Ms2Telescope metadata) : InteractObject(entityId, metadata) { - public override InteractType Type => InteractType.Telescope; - -} - -// sw_co_fi_funct_roulette_A01_ -// co_fi_funct_roulette_A01_ -// co_in_funct_extract_A01_ -public sealed class InteractUiObject(string entityId, Ms2SimpleUiObject metadata) : InteractObject(entityId, metadata) { - public override InteractType Type => InteractType.Ui; - -} - -// public sealed class InteractWebObject : InteractObject { -// public override InteractType Type => InteractType.Web; -// -// public InteractWebObject(string entityId, InteractObject metadata) : base(entityId, metadata) { } -// } - -public sealed class InteractDisplayImage(string entityId, Ms2InteractDisplay metadata) : InteractObject(entityId, metadata) { - public override InteractType Type => InteractType.DisplayImage; - -} - -public sealed class InteractGatheringObject(string entityId, Ms2InteractActor metadata) : InteractObject(entityId, metadata) { - public override InteractType Type => InteractType.Gathering; - - public int Count; - -} - -public sealed class InteractGuildPosterObject(string entityId, Ms2InteractDisplay metadata) : InteractObject(entityId, metadata) { - public override InteractType Type => InteractType.GuildPoster; - -} - -public sealed class InteractBillBoardObject : InteractObject { - public override InteractType Type => InteractType.BillBoard; - - public long OwnerAccountId { get; init; } - public long OwnerCharacterId { get; init; } - public string OwnerName { get; init; } - public string OwnerPicture { get; init; } - public short OwnerLevel { get; init; } - public JobCode OwnerJobCode { get; init; } - public string Title { get; init; } = ""; - public string Description { get; init; } = ""; - public bool PublicHouse { get; init; } - public long CreationTime { get; init; } - public long ExpirationTime { get; init; } - - public InteractBillBoardObject(string entityId, Ms2InteractMesh metadata, Character owner) : base(entityId, metadata) { - OwnerAccountId = owner.AccountId; - OwnerCharacterId = owner.Id; - OwnerName = owner.Name; - OwnerPicture = owner.Picture; - OwnerLevel = owner.Level; - OwnerJobCode = owner.Job.Code(); - } - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteLong(OwnerCharacterId); - writer.WriteUnicodeString(OwnerName); - } -} - -// public sealed class InteractWatchTowerObject : InteractObject { -// public override InteractType Type => InteractType.WatchTower; -// -// public InteractWatchTowerObject(string entityId, InteractObject metadata) : base(entityId, metadata) { } -// } +using System.Numerics; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public interface IInteractObject : IByteSerializable { + public InteractType Type { get; } + public string EntityId { get; } + public int Id { get; } +} + +public abstract class InteractObject : IInteractObject where T : InteractObject { + public abstract InteractType Type { get; } + + protected T Metadata { get; init; } + public int Id { get; init; } + public string Model { get; init; } = ""; + public string Asset { get; init; } = ""; + public string NormalState { get; init; } = ""; + public string Reactable { get; init; } = ""; + public float Scale { get; init; } = 1f; + + public string EntityId { get; init; } + + protected InteractObject(string entityId, T metadata) { + EntityId = entityId; + Metadata = metadata; + Id = metadata.InteractId; + } + + public virtual void WriteTo(IByteWriter writer) { + writer.WriteString(EntityId); + writer.Write(InteractState.Reactable); + writer.Write(Type); + writer.WriteInt(Id); + writer.Write(Metadata.Position); + writer.Write(Metadata.Rotation); + writer.WriteUnicodeString(Model); // e.g. InteractMeshObject + writer.WriteUnicodeString(Asset); // e.g. interaction_chestA_02 + writer.WriteUnicodeString(NormalState); // e.g. Opened_A + writer.WriteUnicodeString(Reactable); // e.g. Idle_A + writer.WriteFloat(Scale); + writer.WriteBool(false); + } +} + +public sealed class InteractMeshObject(string entityId, Ms2InteractMesh metadata) : InteractObject(entityId, metadata) { + public override InteractType Type => InteractType.Mesh; + +} + +public sealed class InteractTelescopeObject(string entityId, Ms2Telescope metadata) : InteractObject(entityId, metadata) { + public override InteractType Type => InteractType.Telescope; + +} + +// sw_co_fi_funct_roulette_A01_ +// co_fi_funct_roulette_A01_ +// co_in_funct_extract_A01_ +public sealed class InteractUiObject(string entityId, Ms2SimpleUiObject metadata) : InteractObject(entityId, metadata) { + public override InteractType Type => InteractType.Ui; + +} + +// public sealed class InteractWebObject : InteractObject { +// public override InteractType Type => InteractType.Web; +// +// public InteractWebObject(string entityId, InteractObject metadata) : base(entityId, metadata) { } +// } + +public sealed class InteractDisplayImage(string entityId, Ms2InteractDisplay metadata) : InteractObject(entityId, metadata) { + public override InteractType Type => InteractType.DisplayImage; + +} + +public sealed class InteractGatheringObject(string entityId, Ms2InteractActor metadata) : InteractObject(entityId, metadata) { + public override InteractType Type => InteractType.Gathering; + + public int Count; + +} + +public sealed class InteractGuildPosterObject(string entityId, Ms2InteractDisplay metadata) : InteractObject(entityId, metadata) { + public override InteractType Type => InteractType.GuildPoster; + +} + +public sealed class InteractBillBoardObject : InteractObject { + public override InteractType Type => InteractType.BillBoard; + + public long OwnerAccountId { get; init; } + public long OwnerCharacterId { get; init; } + public string OwnerName { get; init; } + public string OwnerPicture { get; init; } + public short OwnerLevel { get; init; } + public JobCode OwnerJobCode { get; init; } + public string Title { get; init; } = ""; + public string Description { get; init; } = ""; + public bool PublicHouse { get; init; } + public long CreationTime { get; init; } + public long ExpirationTime { get; init; } + + public InteractBillBoardObject(string entityId, Ms2InteractMesh metadata, Character owner) : base(entityId, metadata) { + OwnerAccountId = owner.AccountId; + OwnerCharacterId = owner.Id; + OwnerName = owner.Name; + OwnerPicture = owner.Picture; + OwnerLevel = owner.Level; + OwnerJobCode = owner.Job.Code(); + } + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteLong(OwnerCharacterId); + writer.WriteUnicodeString(OwnerName); + } +} + +// public sealed class InteractWatchTowerObject : InteractObject { +// public override InteractType Type => InteractType.WatchTower; +// +// public InteractWatchTowerObject(string entityId, InteractObject metadata) : base(entityId, metadata) { } +// } diff --git a/Maple2.Model/Game/InterfaceText.cs b/Maple2.Model/Game/InterfaceText.cs index cafee79fe..086e806bd 100644 --- a/Maple2.Model/Game/InterfaceText.cs +++ b/Maple2.Model/Game/InterfaceText.cs @@ -1,47 +1,47 @@ -using System.Web; -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class InterfaceText : IByteSerializable { - private readonly bool isLocalized; - private readonly int unknown; - private readonly StringCode code; - private readonly string[] args; - private readonly string message; - - public InterfaceText(string message, bool htmlEncoded = false) { - isLocalized = false; - unknown = message.StartsWith("s_") ? 5 : 0; - - args = []; - this.message = htmlEncoded ? message : HttpUtility.HtmlEncode(message); - } - - public InterfaceText(StringCode code, params string[] args) { - isLocalized = true; - unknown = 1; - - this.code = code; - this.args = args; - message = string.Empty; - } - - public static implicit operator InterfaceText(StringCode code) => new InterfaceText(code); - - public void WriteTo(IByteWriter writer) { - writer.WriteBool(isLocalized); - writer.WriteInt(unknown); - if (isLocalized) { - writer.Write(code); - writer.WriteInt(args.Length); - foreach (string arg in args) { - writer.WriteUnicodeString(arg); - } - } else { - writer.WriteUnicodeString(message); - } - } -} +using System.Web; +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class InterfaceText : IByteSerializable { + private readonly bool isLocalized; + private readonly int unknown; + private readonly StringCode code; + private readonly string[] args; + private readonly string message; + + public InterfaceText(string message, bool htmlEncoded = false) { + isLocalized = false; + unknown = message.StartsWith("s_") ? 5 : 0; + + args = []; + this.message = htmlEncoded ? message : HttpUtility.HtmlEncode(message); + } + + public InterfaceText(StringCode code, params string[] args) { + isLocalized = true; + unknown = 1; + + this.code = code; + this.args = args; + message = string.Empty; + } + + public static implicit operator InterfaceText(StringCode code) => new InterfaceText(code); + + public void WriteTo(IByteWriter writer) { + writer.WriteBool(isLocalized); + writer.WriteInt(unknown); + if (isLocalized) { + writer.Write(code); + writer.WriteInt(args.Length); + foreach (string arg in args) { + writer.WriteUnicodeString(arg); + } + } else { + writer.WriteUnicodeString(message); + } + } +} diff --git a/Maple2.Model/Game/Item/IngredientInfo.cs b/Maple2.Model/Game/Item/IngredientInfo.cs index 3966d2a68..768c7cfa4 100644 --- a/Maple2.Model/Game/Item/IngredientInfo.cs +++ b/Maple2.Model/Game/Item/IngredientInfo.cs @@ -1,20 +1,20 @@ -using System.Runtime.InteropServices; -using Maple2.Model.Enum; - -namespace Maple2.Model.Game; - -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 12)] -public readonly struct IngredientInfo { - public readonly int Unknown; - public readonly ItemTag Tag; - public readonly int Amount; - - public IngredientInfo(ItemTag tag, int amount) { - Tag = tag; - Amount = amount; - } - - public static IngredientInfo operator *(in IngredientInfo self, double ratio) { - return new IngredientInfo(self.Tag, (int) Math.Round(self.Amount * ratio)); - } -} +using System.Runtime.InteropServices; +using Maple2.Model.Enum; + +namespace Maple2.Model.Game; + +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 12)] +public readonly struct IngredientInfo { + public readonly int Unknown; + public readonly ItemTag Tag; + public readonly int Amount; + + public IngredientInfo(ItemTag tag, int amount) { + Tag = tag; + Amount = amount; + } + + public static IngredientInfo operator *(in IngredientInfo self, double ratio) { + return new IngredientInfo(self.Tag, (int) Math.Round(self.Amount * ratio)); + } +} diff --git a/Maple2.Model/Game/Item/Item.cs b/Maple2.Model/Game/Item/Item.cs index eb49af3a8..2f92ba59e 100644 --- a/Maple2.Model/Game/Item/Item.cs +++ b/Maple2.Model/Game/Item/Item.cs @@ -1,269 +1,269 @@ -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game; - -public class Item : IByteSerializable, IByteDeserializable { - public readonly ItemMetadata Metadata; - public readonly InventoryType Inventory; - public readonly ItemType Type; - - public long Uid { get; init; } - public int Rarity { get; init; } - public short Slot = -1; - public ItemGroup Group = ItemGroup.Default; - - public int Id => Metadata.Id; - public int Amount; - - public long CreationTime; - public long ExpiryTime; - - public int TimeChangedOption; - public int RemainUses; - public bool IsLocked; - public long UnlockTime; - public short GlamorForges; - public int GachaDismantleId; - - public ItemAppearance? Appearance; - public ItemStats? Stats; - public ItemEnchant? Enchant; - public ItemLimitBreak? LimitBreak; - - public ItemTransfer? Transfer; - public ItemSocket? Socket; - public ItemCoupleInfo? CoupleInfo; - public ItemBinding? Binding; - - #region Special Types - public UgcItemLook? Template; - public ItemBlueprint? Blueprint; - public ItemPet? Pet; - public ItemCustomMusicScore? Music; - public ItemBadge? Badge; - #endregion - - public Item(ItemMetadata metadata, int rarity = 1, int amount = 1, bool initialize = true) { - Metadata = metadata; - Rarity = rarity; - Amount = amount; - Inventory = Metadata.Inventory(); - Type = new ItemType(metadata.Id); - - // Skip initialization of fields, this is done if we will initialize separately. - if (!initialize) { - return; - } - - GlamorForges = (short) Metadata.Limit.GlamorForgeCount; - Appearance = Metadata.SlotNames.FirstOrDefault(EquipSlot.Unknown) switch { - EquipSlot.HR => new HairAppearance(default), - EquipSlot.FD => new DecalAppearance(default), - EquipSlot.CP => new CapAppearance(default), - _ => new ItemAppearance(default), - }; - - Transfer = new ItemTransfer(GetTransferFlag(), Metadata.Property.TradableCount); - Enchant = new ItemEnchant(tradeable: Transfer.Flag.HasFlag(TransferFlag.Trade) || Transfer.Flag.HasFlag(TransferFlag.LimitTrade)); - - ExpiryTime = GetExpiryTime(); - - if (Metadata.Music != null) { - RemainUses = Metadata.Music.PlayCount; - } - - // Template? or Blueprint - if (Metadata.Mesh != string.Empty || Metadata.Property.Type == 22) { - Template = new UgcItemLook(); - Blueprint = new ItemBlueprint(); - } else if (Inventory == InventoryType.Pets) { - Pet = new ItemPet(); - } else if (Metadata.Music?.IsCustomNote == true) { - Music = new ItemCustomMusicScore(); - } else if (Inventory == InventoryType.Badge) { - Badge = new ItemBadge(Id); - } - } - - public Item Clone() { - return new Item(Metadata, Rarity, Amount, false) { - Uid = Uid, - CreationTime = CreationTime, - ExpiryTime = ExpiryTime, - TimeChangedOption = TimeChangedOption, - RemainUses = RemainUses, - IsLocked = IsLocked, - UnlockTime = UnlockTime, - GlamorForges = GlamorForges, - GachaDismantleId = GachaDismantleId, - Appearance = Appearance?.Clone(), - Stats = Stats?.Clone(), - Enchant = Enchant?.Clone(), - LimitBreak = LimitBreak?.Clone(), - Transfer = Transfer?.Clone(), - Socket = Socket?.Clone(), - CoupleInfo = CoupleInfo?.Clone(), - Binding = Binding?.Clone(), - Template = Template?.Clone(), - Blueprint = Blueprint?.Clone(), - Pet = Pet?.Clone(), - Music = Music?.Clone(), - Badge = Badge?.Clone(), - }; - } - - public Item Mutate(ItemMetadata metadata, int? rarity = null) { - return new Item(metadata, rarity ?? Rarity, Amount, false) { - Uid = Uid, - CreationTime = CreationTime, - ExpiryTime = ExpiryTime, - TimeChangedOption = TimeChangedOption, - RemainUses = RemainUses, - IsLocked = IsLocked, - UnlockTime = UnlockTime, - GlamorForges = GlamorForges, - GachaDismantleId = GachaDismantleId, - Appearance = Appearance, - Stats = Stats, - Enchant = Enchant, - LimitBreak = LimitBreak, - Transfer = Transfer, - Socket = Socket, - CoupleInfo = CoupleInfo, - Binding = Binding, - Template = Template, - Blueprint = Blueprint, - Pet = Pet, - Music = Music, - Badge = Badge, - }; - } - - private TransferFlag GetTransferFlag() { - bool zeroTrades = Metadata.Property.TradableCount <= 0; - bool belowRarity = Rarity < Metadata.Limit.TradeMaxRarity; - switch (Metadata.Limit.TransferType) { - case TransferType.Tradable: - if (belowRarity) { - return TransferFlag.Trade | TransferFlag.Split; - } - return zeroTrades ? TransferFlag.None : TransferFlag.LimitTrade; - case TransferType.Untradeable: - return zeroTrades ? TransferFlag.None : TransferFlag.LimitTrade; - case TransferType.BindOnLoot: - case TransferType.BindOnEquip: - case TransferType.BindOnUse: - case TransferType.BindOnTrade: - case TransferType.BindPet: // summon/enchant/reroll - var result = TransferFlag.Bind; - if (zeroTrades) { - if (belowRarity) { - result |= TransferFlag.Trade | TransferFlag.Split; - } - } else { - result |= TransferFlag.LimitTrade; - } - return result; - case TransferType.BlackMarketOnly: - if (!zeroTrades || belowRarity) { - return TransferFlag.Trade; - } - return zeroTrades ? TransferFlag.None : TransferFlag.LimitTrade; - default: - return TransferFlag.None; - } - } - - private long GetExpiryTime() { - if (Metadata.Life.ExpirationTimestamp > 0) { -#if DEBUG - return (long) (DateTime.Now.AddYears(1).ToUniversalTime() - DateTime.UnixEpoch).TotalSeconds; -#else - return Metadata.Life.ExpirationTimestamp; -#endif - } - - if (Metadata.Life.ExpirationDuration > 0) { - return (long) (DateTime.Now.ToUniversalTime() - DateTime.UnixEpoch).TotalSeconds + Metadata.Life.ExpirationDuration; - } - return 0; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteInt(Amount); - writer.WriteInt(); - writer.WriteInt(-1); - writer.WriteLong(CreationTime); - writer.WriteLong(ExpiryTime); - writer.WriteLong(); - writer.WriteInt(TimeChangedOption); - writer.WriteInt(RemainUses); - writer.WriteBool(IsLocked); - writer.WriteLong(UnlockTime); - writer.WriteShort(GlamorForges); - writer.WriteBool(false); - writer.WriteInt(GachaDismantleId); - - writer.WriteClass(Appearance ?? ItemAppearance.Default); - writer.WriteClass(Stats ?? ItemStats.Default); - writer.WriteClass(Enchant ?? ItemEnchant.Default); - writer.WriteClass(LimitBreak ?? ItemLimitBreak.Default); - - if (Template != null && Blueprint != null) { - writer.WriteClass(Template); - writer.WriteClass(Blueprint); - } else if (Pet != null) { - writer.WriteClass(Pet); - } else if (Music != null) { - writer.WriteClass(Music); - } else if (Badge != null) { - writer.WriteClass(Badge); - } - - writer.WriteClass(Transfer ?? ItemTransfer.Default); - writer.WriteClass(Socket ?? ItemSocket.Default); - writer.WriteClass(CoupleInfo ?? ItemCoupleInfo.Default); - writer.WriteClass(Binding ?? ItemBinding.Default); - } - - public void ReadFrom(IByteReader reader) { - Amount = reader.ReadInt(); - reader.ReadInt(); - reader.ReadInt(); - CreationTime = reader.ReadLong(); - ExpiryTime = reader.ReadLong(); - reader.ReadLong(); - TimeChangedOption = reader.ReadInt(); - RemainUses = reader.ReadInt(); - IsLocked = reader.ReadBool(); - UnlockTime = reader.ReadLong(); - GlamorForges = reader.ReadShort(); - reader.ReadBool(); - reader.ReadInt(); - - Appearance = reader.ReadClass(); - Stats = reader.ReadClass(); - Enchant = reader.ReadClass(); - LimitBreak = reader.ReadClass(); - - if (Template != null && Blueprint != null) { - Template = reader.ReadClass(); - Blueprint = reader.ReadClass(); - } else if (Pet != null) { - Pet = reader.ReadClass(); - } else if (Music != null) { - Music = reader.ReadClass(); - } else if (Badge != null) { - Badge = reader.ReadClass(); - } - - Transfer = reader.ReadClass(); - Socket = reader.ReadClass(); - CoupleInfo = reader.ReadClass(); - Binding = reader.ReadClass(); - } -} +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game; + +public class Item : IByteSerializable, IByteDeserializable { + public readonly ItemMetadata Metadata; + public readonly InventoryType Inventory; + public readonly ItemType Type; + + public long Uid { get; init; } + public int Rarity { get; init; } + public short Slot = -1; + public ItemGroup Group = ItemGroup.Default; + + public int Id => Metadata.Id; + public int Amount; + + public long CreationTime; + public long ExpiryTime; + + public int TimeChangedOption; + public int RemainUses; + public bool IsLocked; + public long UnlockTime; + public short GlamorForges; + public int GachaDismantleId; + + public ItemAppearance? Appearance; + public ItemStats? Stats; + public ItemEnchant? Enchant; + public ItemLimitBreak? LimitBreak; + + public ItemTransfer? Transfer; + public ItemSocket? Socket; + public ItemCoupleInfo? CoupleInfo; + public ItemBinding? Binding; + + #region Special Types + public UgcItemLook? Template; + public ItemBlueprint? Blueprint; + public ItemPet? Pet; + public ItemCustomMusicScore? Music; + public ItemBadge? Badge; + #endregion + + public Item(ItemMetadata metadata, int rarity = 1, int amount = 1, bool initialize = true) { + Metadata = metadata; + Rarity = rarity; + Amount = amount; + Inventory = Metadata.Inventory(); + Type = new ItemType(metadata.Id); + + // Skip initialization of fields, this is done if we will initialize separately. + if (!initialize) { + return; + } + + GlamorForges = (short) Metadata.Limit.GlamorForgeCount; + Appearance = Metadata.SlotNames.FirstOrDefault(EquipSlot.Unknown) switch { + EquipSlot.HR => new HairAppearance(default), + EquipSlot.FD => new DecalAppearance(default), + EquipSlot.CP => new CapAppearance(default), + _ => new ItemAppearance(default), + }; + + Transfer = new ItemTransfer(GetTransferFlag(), Metadata.Property.TradableCount); + Enchant = new ItemEnchant(tradeable: Transfer.Flag.HasFlag(TransferFlag.Trade) || Transfer.Flag.HasFlag(TransferFlag.LimitTrade)); + + ExpiryTime = GetExpiryTime(); + + if (Metadata.Music != null) { + RemainUses = Metadata.Music.PlayCount; + } + + // Template? or Blueprint + if (Metadata.Mesh != string.Empty || Metadata.Property.Type == 22) { + Template = new UgcItemLook(); + Blueprint = new ItemBlueprint(); + } else if (Inventory == InventoryType.Pets) { + Pet = new ItemPet(); + } else if (Metadata.Music?.IsCustomNote == true) { + Music = new ItemCustomMusicScore(); + } else if (Inventory == InventoryType.Badge) { + Badge = new ItemBadge(Id); + } + } + + public Item Clone() { + return new Item(Metadata, Rarity, Amount, false) { + Uid = Uid, + CreationTime = CreationTime, + ExpiryTime = ExpiryTime, + TimeChangedOption = TimeChangedOption, + RemainUses = RemainUses, + IsLocked = IsLocked, + UnlockTime = UnlockTime, + GlamorForges = GlamorForges, + GachaDismantleId = GachaDismantleId, + Appearance = Appearance?.Clone(), + Stats = Stats?.Clone(), + Enchant = Enchant?.Clone(), + LimitBreak = LimitBreak?.Clone(), + Transfer = Transfer?.Clone(), + Socket = Socket?.Clone(), + CoupleInfo = CoupleInfo?.Clone(), + Binding = Binding?.Clone(), + Template = Template?.Clone(), + Blueprint = Blueprint?.Clone(), + Pet = Pet?.Clone(), + Music = Music?.Clone(), + Badge = Badge?.Clone(), + }; + } + + public Item Mutate(ItemMetadata metadata, int? rarity = null) { + return new Item(metadata, rarity ?? Rarity, Amount, false) { + Uid = Uid, + CreationTime = CreationTime, + ExpiryTime = ExpiryTime, + TimeChangedOption = TimeChangedOption, + RemainUses = RemainUses, + IsLocked = IsLocked, + UnlockTime = UnlockTime, + GlamorForges = GlamorForges, + GachaDismantleId = GachaDismantleId, + Appearance = Appearance, + Stats = Stats, + Enchant = Enchant, + LimitBreak = LimitBreak, + Transfer = Transfer, + Socket = Socket, + CoupleInfo = CoupleInfo, + Binding = Binding, + Template = Template, + Blueprint = Blueprint, + Pet = Pet, + Music = Music, + Badge = Badge, + }; + } + + private TransferFlag GetTransferFlag() { + bool zeroTrades = Metadata.Property.TradableCount <= 0; + bool belowRarity = Rarity < Metadata.Limit.TradeMaxRarity; + switch (Metadata.Limit.TransferType) { + case TransferType.Tradable: + if (belowRarity) { + return TransferFlag.Trade | TransferFlag.Split; + } + return zeroTrades ? TransferFlag.None : TransferFlag.LimitTrade; + case TransferType.Untradeable: + return zeroTrades ? TransferFlag.None : TransferFlag.LimitTrade; + case TransferType.BindOnLoot: + case TransferType.BindOnEquip: + case TransferType.BindOnUse: + case TransferType.BindOnTrade: + case TransferType.BindPet: // summon/enchant/reroll + var result = TransferFlag.Bind; + if (zeroTrades) { + if (belowRarity) { + result |= TransferFlag.Trade | TransferFlag.Split; + } + } else { + result |= TransferFlag.LimitTrade; + } + return result; + case TransferType.BlackMarketOnly: + if (!zeroTrades || belowRarity) { + return TransferFlag.Trade; + } + return zeroTrades ? TransferFlag.None : TransferFlag.LimitTrade; + default: + return TransferFlag.None; + } + } + + private long GetExpiryTime() { + if (Metadata.Life.ExpirationTimestamp > 0) { +#if DEBUG + return (long) (DateTime.Now.AddYears(1).ToUniversalTime() - DateTime.UnixEpoch).TotalSeconds; +#else + return Metadata.Life.ExpirationTimestamp; +#endif + } + + if (Metadata.Life.ExpirationDuration > 0) { + return (long) (DateTime.Now.ToUniversalTime() - DateTime.UnixEpoch).TotalSeconds + Metadata.Life.ExpirationDuration; + } + return 0; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteInt(Amount); + writer.WriteInt(); + writer.WriteInt(-1); + writer.WriteLong(CreationTime); + writer.WriteLong(ExpiryTime); + writer.WriteLong(); + writer.WriteInt(TimeChangedOption); + writer.WriteInt(RemainUses); + writer.WriteBool(IsLocked); + writer.WriteLong(UnlockTime); + writer.WriteShort(GlamorForges); + writer.WriteBool(false); + writer.WriteInt(GachaDismantleId); + + writer.WriteClass(Appearance ?? ItemAppearance.Default); + writer.WriteClass(Stats ?? ItemStats.Default); + writer.WriteClass(Enchant ?? ItemEnchant.Default); + writer.WriteClass(LimitBreak ?? ItemLimitBreak.Default); + + if (Template != null && Blueprint != null) { + writer.WriteClass(Template); + writer.WriteClass(Blueprint); + } else if (Pet != null) { + writer.WriteClass(Pet); + } else if (Music != null) { + writer.WriteClass(Music); + } else if (Badge != null) { + writer.WriteClass(Badge); + } + + writer.WriteClass(Transfer ?? ItemTransfer.Default); + writer.WriteClass(Socket ?? ItemSocket.Default); + writer.WriteClass(CoupleInfo ?? ItemCoupleInfo.Default); + writer.WriteClass(Binding ?? ItemBinding.Default); + } + + public void ReadFrom(IByteReader reader) { + Amount = reader.ReadInt(); + reader.ReadInt(); + reader.ReadInt(); + CreationTime = reader.ReadLong(); + ExpiryTime = reader.ReadLong(); + reader.ReadLong(); + TimeChangedOption = reader.ReadInt(); + RemainUses = reader.ReadInt(); + IsLocked = reader.ReadBool(); + UnlockTime = reader.ReadLong(); + GlamorForges = reader.ReadShort(); + reader.ReadBool(); + reader.ReadInt(); + + Appearance = reader.ReadClass(); + Stats = reader.ReadClass(); + Enchant = reader.ReadClass(); + LimitBreak = reader.ReadClass(); + + if (Template != null && Blueprint != null) { + Template = reader.ReadClass(); + Blueprint = reader.ReadClass(); + } else if (Pet != null) { + Pet = reader.ReadClass(); + } else if (Music != null) { + Music = reader.ReadClass(); + } else if (Badge != null) { + Badge = reader.ReadClass(); + } + + Transfer = reader.ReadClass(); + Socket = reader.ReadClass(); + CoupleInfo = reader.ReadClass(); + Binding = reader.ReadClass(); + } +} diff --git a/Maple2.Model/Game/Item/ItemAppearance.cs b/Maple2.Model/Game/Item/ItemAppearance.cs index 5c31de19c..0de2c9709 100644 --- a/Maple2.Model/Game/Item/ItemAppearance.cs +++ b/Maple2.Model/Game/Item/ItemAppearance.cs @@ -1,146 +1,146 @@ -using System.Numerics; -using Maple2.Model.Common; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class ItemAppearance : IByteSerializable, IByteDeserializable { - public static readonly ItemAppearance Default = new ItemAppearance(default); - - public EquipColor Color; - - public ItemAppearance(EquipColor color) { - Color = color; - } - - public virtual ItemAppearance Clone() { - return (ItemAppearance) MemberwiseClone(); - } - - public virtual void WriteTo(IByteWriter writer) { - writer.Write(Color); - } - - public virtual void ReadFrom(IByteReader reader) { - Color = reader.Read(); - } -} - -public sealed class HairAppearance : ItemAppearance { - public float BackLength { get; private set; } - public Vector3 BackPosition1 { get; private set; } - public Vector3 BackPosition2 { get; private set; } - public float FrontLength { get; private set; } - public Vector3 FrontPosition1 { get; private set; } - public Vector3 FrontPosition2 { get; private set; } - - public HairAppearance(EquipColor color, float backLength = default, Vector3 backPosition1 = default, - Vector3 backPosition2 = default, float frontLength = default, Vector3 frontPosition1 = default, - Vector3 frontPosition2 = default) : base(color) { - BackLength = backLength; - BackPosition1 = backPosition1; - BackPosition2 = backPosition2; - FrontLength = frontLength; - FrontPosition1 = frontPosition1; - FrontPosition2 = frontPosition2; - } - - public override HairAppearance Clone() { - return (HairAppearance) MemberwiseClone(); - } - - public override void WriteTo(IByteWriter writer) { - writer.Write(Color); - writer.WriteFloat(BackLength); - writer.Write(BackPosition1); - writer.Write(BackPosition2); - writer.WriteFloat(FrontLength); - writer.Write(FrontPosition1); - writer.Write(FrontPosition2); - } - - public override void ReadFrom(IByteReader reader) { - Color = reader.Read(); - BackLength = reader.ReadFloat(); - BackPosition1 = reader.Read(); - BackPosition2 = reader.Read(); - FrontLength = reader.ReadFloat(); - FrontPosition1 = reader.Read(); - FrontPosition2 = reader.Read(); - } -} - -public sealed class DecalAppearance : ItemAppearance { - public float Position1 { get; private set; } - public float Position2 { get; private set; } - public float Position3 { get; private set; } - public float Position4 { get; private set; } - - public DecalAppearance(EquipColor color, float position1 = default, float position2 = default, - float position3 = default, float position4 = default) : base(color) { - Position1 = position1; - Position2 = position2; - Position3 = position3; - Position4 = position4; - } - - public override DecalAppearance Clone() { - return (DecalAppearance) MemberwiseClone(); - } - - public override void WriteTo(IByteWriter writer) { - writer.Write(Color); - writer.WriteFloat(Position1); - writer.WriteFloat(Position2); - writer.WriteFloat(Position3); - writer.WriteFloat(Position4); - } - - public override void ReadFrom(IByteReader reader) { - Color = reader.Read(); - Position1 = reader.ReadFloat(); - Position2 = reader.ReadFloat(); - Position3 = reader.ReadFloat(); - Position4 = reader.ReadFloat(); - } -} - -public sealed class CapAppearance : ItemAppearance { - public Vector3 Position1 { get; private set; } - public Vector3 Position2 { get; private set; } - public Vector3 Position3 { get; private set; } - public Vector3 Position4 { get; private set; } - public float Unknown { get; private set; } - - public CapAppearance(EquipColor color, Vector3 position1 = default, Vector3 position2 = default, - Vector3 position3 = default, Vector3 position4 = default, float unknown = default) : base(color) { - Position1 = position1; - Position2 = position2; - Position3 = position3; - Position4 = position4; - Unknown = unknown; - } - - public override CapAppearance Clone() { - return (CapAppearance) MemberwiseClone(); - } - - public override void WriteTo(IByteWriter writer) { - writer.Write(Color); - writer.Write(Position1); - writer.Write(Position2); - writer.Write(Position3); - writer.Write(Position4); - writer.WriteFloat(Unknown); - } - - public override void ReadFrom(IByteReader reader) { - Color = reader.Read(); - Position1 = reader.Read(); - Position2 = reader.Read(); - Position3 = reader.Read(); - Position4 = reader.Read(); - Unknown = reader.ReadFloat(); - } -} +using System.Numerics; +using Maple2.Model.Common; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class ItemAppearance : IByteSerializable, IByteDeserializable { + public static readonly ItemAppearance Default = new ItemAppearance(default); + + public EquipColor Color; + + public ItemAppearance(EquipColor color) { + Color = color; + } + + public virtual ItemAppearance Clone() { + return (ItemAppearance) MemberwiseClone(); + } + + public virtual void WriteTo(IByteWriter writer) { + writer.Write(Color); + } + + public virtual void ReadFrom(IByteReader reader) { + Color = reader.Read(); + } +} + +public sealed class HairAppearance : ItemAppearance { + public float BackLength { get; private set; } + public Vector3 BackPosition1 { get; private set; } + public Vector3 BackPosition2 { get; private set; } + public float FrontLength { get; private set; } + public Vector3 FrontPosition1 { get; private set; } + public Vector3 FrontPosition2 { get; private set; } + + public HairAppearance(EquipColor color, float backLength = default, Vector3 backPosition1 = default, + Vector3 backPosition2 = default, float frontLength = default, Vector3 frontPosition1 = default, + Vector3 frontPosition2 = default) : base(color) { + BackLength = backLength; + BackPosition1 = backPosition1; + BackPosition2 = backPosition2; + FrontLength = frontLength; + FrontPosition1 = frontPosition1; + FrontPosition2 = frontPosition2; + } + + public override HairAppearance Clone() { + return (HairAppearance) MemberwiseClone(); + } + + public override void WriteTo(IByteWriter writer) { + writer.Write(Color); + writer.WriteFloat(BackLength); + writer.Write(BackPosition1); + writer.Write(BackPosition2); + writer.WriteFloat(FrontLength); + writer.Write(FrontPosition1); + writer.Write(FrontPosition2); + } + + public override void ReadFrom(IByteReader reader) { + Color = reader.Read(); + BackLength = reader.ReadFloat(); + BackPosition1 = reader.Read(); + BackPosition2 = reader.Read(); + FrontLength = reader.ReadFloat(); + FrontPosition1 = reader.Read(); + FrontPosition2 = reader.Read(); + } +} + +public sealed class DecalAppearance : ItemAppearance { + public float Position1 { get; private set; } + public float Position2 { get; private set; } + public float Position3 { get; private set; } + public float Position4 { get; private set; } + + public DecalAppearance(EquipColor color, float position1 = default, float position2 = default, + float position3 = default, float position4 = default) : base(color) { + Position1 = position1; + Position2 = position2; + Position3 = position3; + Position4 = position4; + } + + public override DecalAppearance Clone() { + return (DecalAppearance) MemberwiseClone(); + } + + public override void WriteTo(IByteWriter writer) { + writer.Write(Color); + writer.WriteFloat(Position1); + writer.WriteFloat(Position2); + writer.WriteFloat(Position3); + writer.WriteFloat(Position4); + } + + public override void ReadFrom(IByteReader reader) { + Color = reader.Read(); + Position1 = reader.ReadFloat(); + Position2 = reader.ReadFloat(); + Position3 = reader.ReadFloat(); + Position4 = reader.ReadFloat(); + } +} + +public sealed class CapAppearance : ItemAppearance { + public Vector3 Position1 { get; private set; } + public Vector3 Position2 { get; private set; } + public Vector3 Position3 { get; private set; } + public Vector3 Position4 { get; private set; } + public float Unknown { get; private set; } + + public CapAppearance(EquipColor color, Vector3 position1 = default, Vector3 position2 = default, + Vector3 position3 = default, Vector3 position4 = default, float unknown = default) : base(color) { + Position1 = position1; + Position2 = position2; + Position3 = position3; + Position4 = position4; + Unknown = unknown; + } + + public override CapAppearance Clone() { + return (CapAppearance) MemberwiseClone(); + } + + public override void WriteTo(IByteWriter writer) { + writer.Write(Color); + writer.Write(Position1); + writer.Write(Position2); + writer.Write(Position3); + writer.Write(Position4); + writer.WriteFloat(Unknown); + } + + public override void ReadFrom(IByteReader reader) { + Color = reader.Read(); + Position1 = reader.Read(); + Position2 = reader.Read(); + Position3 = reader.Read(); + Position4 = reader.Read(); + Unknown = reader.ReadFloat(); + } +} diff --git a/Maple2.Model/Game/Item/ItemBadge.cs b/Maple2.Model/Game/Item/ItemBadge.cs index 7efd7801d..2d41c7836 100644 --- a/Maple2.Model/Game/Item/ItemBadge.cs +++ b/Maple2.Model/Game/Item/ItemBadge.cs @@ -1,84 +1,84 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public sealed class ItemBadge : IByteSerializable, IByteDeserializable { - private const int TRANSPARENCY_COUNT = 10; - - public readonly int Id; - public readonly BadgeType Type; - public readonly bool[] Transparency; - public int PetSkinId; - - public ItemBadge(int id, bool[]? transparency = null) { - Id = id; - Type = (Id / 100000) switch { - 701 => (Id % 10) switch { - 0 => BadgeType.PetSkin, - 1 => BadgeType.Transparency, - _ => BadgeType.AutoGather, - }, - 702 => BadgeType.ChatBubble, - 703 => BadgeType.NameTag, - 704 => BadgeType.Damage, - 705 => BadgeType.Tombstone, - 706 => BadgeType.SwimTube, - 707 => BadgeType.Fishing, - 708 => BadgeType.Buddy, - 709 => BadgeType.Effect, - _ => BadgeType.None, - }; - - if (transparency is not { Length: TRANSPARENCY_COUNT }) { - Transparency = new bool[TRANSPARENCY_COUNT]; - } else { - Transparency = transparency; - } - } - - public ItemBadge Clone() { - bool[] transparency = new bool[TRANSPARENCY_COUNT]; - for (int i = 0; i < TRANSPARENCY_COUNT; i++) { - transparency[i] = Transparency[i]; - } - return new ItemBadge(Id, transparency) { - PetSkinId = PetSkinId, - }; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteByte(1); - writer.WriteByte((byte) Type); - writer.WriteUnicodeString(Id.ToString()); - - switch (Type) { - case BadgeType.Transparency: // Flags for each slot - foreach (bool toggle in Transparency) { - writer.WriteBool(toggle); - } - break; - case BadgeType.PetSkin: // PetId for skin - writer.WriteInt(PetSkinId); - break; - } - } - - public void ReadFrom(IByteReader reader) { - reader.ReadByte(); - reader.ReadByte(); // Type - reader.ReadUnicodeString(); // String ItemId - - switch (Type) { - case BadgeType.Transparency: // Flags for each slot - for (int i = 0; i < TRANSPARENCY_COUNT; i++) { - Transparency[i] = reader.ReadBool(); - } - break; - case BadgeType.PetSkin: // PetId for skin - PetSkinId = reader.ReadInt(); - break; - } - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public sealed class ItemBadge : IByteSerializable, IByteDeserializable { + private const int TRANSPARENCY_COUNT = 10; + + public readonly int Id; + public readonly BadgeType Type; + public readonly bool[] Transparency; + public int PetSkinId; + + public ItemBadge(int id, bool[]? transparency = null) { + Id = id; + Type = (Id / 100000) switch { + 701 => (Id % 10) switch { + 0 => BadgeType.PetSkin, + 1 => BadgeType.Transparency, + _ => BadgeType.AutoGather, + }, + 702 => BadgeType.ChatBubble, + 703 => BadgeType.NameTag, + 704 => BadgeType.Damage, + 705 => BadgeType.Tombstone, + 706 => BadgeType.SwimTube, + 707 => BadgeType.Fishing, + 708 => BadgeType.Buddy, + 709 => BadgeType.Effect, + _ => BadgeType.None, + }; + + if (transparency is not { Length: TRANSPARENCY_COUNT }) { + Transparency = new bool[TRANSPARENCY_COUNT]; + } else { + Transparency = transparency; + } + } + + public ItemBadge Clone() { + bool[] transparency = new bool[TRANSPARENCY_COUNT]; + for (int i = 0; i < TRANSPARENCY_COUNT; i++) { + transparency[i] = Transparency[i]; + } + return new ItemBadge(Id, transparency) { + PetSkinId = PetSkinId, + }; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteByte(1); + writer.WriteByte((byte) Type); + writer.WriteUnicodeString(Id.ToString()); + + switch (Type) { + case BadgeType.Transparency: // Flags for each slot + foreach (bool toggle in Transparency) { + writer.WriteBool(toggle); + } + break; + case BadgeType.PetSkin: // PetId for skin + writer.WriteInt(PetSkinId); + break; + } + } + + public void ReadFrom(IByteReader reader) { + reader.ReadByte(); + reader.ReadByte(); // Type + reader.ReadUnicodeString(); // String ItemId + + switch (Type) { + case BadgeType.Transparency: // Flags for each slot + for (int i = 0; i < TRANSPARENCY_COUNT; i++) { + Transparency[i] = reader.ReadBool(); + } + break; + case BadgeType.PetSkin: // PetId for skin + PetSkinId = reader.ReadInt(); + break; + } + } +} diff --git a/Maple2.Model/Game/Item/ItemBinding.cs b/Maple2.Model/Game/Item/ItemBinding.cs index e927a5c39..db25aada8 100644 --- a/Maple2.Model/Game/Item/ItemBinding.cs +++ b/Maple2.Model/Game/Item/ItemBinding.cs @@ -1,40 +1,40 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public sealed class ItemBinding : IByteSerializable, IByteDeserializable { - public static readonly ItemBinding Default = new ItemBinding(); - - public long CharacterId { get; private set; } - public string Name { get; private set; } - - public ItemBinding(long characterId = 0, string name = "") { - CharacterId = characterId; - Name = name; - } - - public ItemBinding Clone() { - return (ItemBinding) MemberwiseClone(); - } - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(CharacterId); - writer.WriteUnicodeString(Name); - } - - public void ReadFrom(IByteReader reader) { - CharacterId = reader.ReadLong(); - Name = reader.ReadUnicodeString(); - } - - public override bool Equals(object? obj) { - if (ReferenceEquals(this, obj)) return true; - if (!(obj is ItemBinding other)) return false; - return CharacterId == other.CharacterId && Name == other.Name; - } - - public override int GetHashCode() { - return HashCode.Combine(CharacterId, Name); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public sealed class ItemBinding : IByteSerializable, IByteDeserializable { + public static readonly ItemBinding Default = new ItemBinding(); + + public long CharacterId { get; private set; } + public string Name { get; private set; } + + public ItemBinding(long characterId = 0, string name = "") { + CharacterId = characterId; + Name = name; + } + + public ItemBinding Clone() { + return (ItemBinding) MemberwiseClone(); + } + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(CharacterId); + writer.WriteUnicodeString(Name); + } + + public void ReadFrom(IByteReader reader) { + CharacterId = reader.ReadLong(); + Name = reader.ReadUnicodeString(); + } + + public override bool Equals(object? obj) { + if (ReferenceEquals(this, obj)) return true; + if (!(obj is ItemBinding other)) return false; + return CharacterId == other.CharacterId && Name == other.Name; + } + + public override int GetHashCode() { + return HashCode.Combine(CharacterId, Name); + } +} diff --git a/Maple2.Model/Game/Item/ItemBlueprint.cs b/Maple2.Model/Game/Item/ItemBlueprint.cs index 350de5e64..b907b0ace 100644 --- a/Maple2.Model/Game/Item/ItemBlueprint.cs +++ b/Maple2.Model/Game/Item/ItemBlueprint.cs @@ -1,45 +1,45 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public sealed class ItemBlueprint : IByteSerializable, IByteDeserializable { - public long BlueprintUid; - public int Length; - public int Width; - public int Height; - public DateTimeOffset CreationTime; - public BlueprintType Type = BlueprintType.Original; - public long AccountId; - public long CharacterId; - public string CharacterName = ""; - - public ItemBlueprint Clone() { - return (ItemBlueprint) MemberwiseClone(); - } - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(BlueprintUid); - writer.WriteInt(Length); - writer.WriteInt(Width); - writer.WriteInt(Height); - writer.WriteLong(CreationTime.ToUnixTimeSeconds()); - writer.Write(Type); - writer.WriteLong(AccountId); - writer.WriteLong(CharacterId); - writer.WriteUnicodeString(CharacterName); - } - - public void ReadFrom(IByteReader reader) { - BlueprintUid = reader.ReadLong(); - Length = reader.ReadInt(); - Width = reader.ReadInt(); - Height = reader.ReadInt(); - CreationTime = DateTimeOffset.FromUnixTimeSeconds(reader.ReadLong()); - Type = (BlueprintType) reader.ReadInt(); - AccountId = reader.ReadLong(); - CharacterId = reader.ReadLong(); - CharacterName = reader.ReadUnicodeString(); - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public sealed class ItemBlueprint : IByteSerializable, IByteDeserializable { + public long BlueprintUid; + public int Length; + public int Width; + public int Height; + public DateTimeOffset CreationTime; + public BlueprintType Type = BlueprintType.Original; + public long AccountId; + public long CharacterId; + public string CharacterName = ""; + + public ItemBlueprint Clone() { + return (ItemBlueprint) MemberwiseClone(); + } + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(BlueprintUid); + writer.WriteInt(Length); + writer.WriteInt(Width); + writer.WriteInt(Height); + writer.WriteLong(CreationTime.ToUnixTimeSeconds()); + writer.Write(Type); + writer.WriteLong(AccountId); + writer.WriteLong(CharacterId); + writer.WriteUnicodeString(CharacterName); + } + + public void ReadFrom(IByteReader reader) { + BlueprintUid = reader.ReadLong(); + Length = reader.ReadInt(); + Width = reader.ReadInt(); + Height = reader.ReadInt(); + CreationTime = DateTimeOffset.FromUnixTimeSeconds(reader.ReadLong()); + Type = (BlueprintType) reader.ReadInt(); + AccountId = reader.ReadLong(); + CharacterId = reader.ReadLong(); + CharacterName = reader.ReadUnicodeString(); + } +} diff --git a/Maple2.Model/Game/Item/ItemComponent.cs b/Maple2.Model/Game/Item/ItemComponent.cs index 81ef241aa..a78dbcb36 100644 --- a/Maple2.Model/Game/Item/ItemComponent.cs +++ b/Maple2.Model/Game/Item/ItemComponent.cs @@ -1,9 +1,9 @@ -using Maple2.Model.Enum; - -namespace Maple2.Model.Game; - -public record ItemComponent( - int ItemId, - int Rarity, - int Amount, - ItemTag Tag); +using Maple2.Model.Enum; + +namespace Maple2.Model.Game; + +public record ItemComponent( + int ItemId, + int Rarity, + int Amount, + ItemTag Tag); diff --git a/Maple2.Model/Game/Item/ItemCoupleInfo.cs b/Maple2.Model/Game/Item/ItemCoupleInfo.cs index 975b1427f..f3fe2f01e 100644 --- a/Maple2.Model/Game/Item/ItemCoupleInfo.cs +++ b/Maple2.Model/Game/Item/ItemCoupleInfo.cs @@ -1,37 +1,37 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public sealed class ItemCoupleInfo : IByteSerializable, IByteDeserializable { - public static readonly ItemCoupleInfo Default = new ItemCoupleInfo(); - - public long CharacterId { get; private set; } - public string Name { get; private set; } - public bool IsCreator { get; private set; } - - public ItemCoupleInfo(long characterId = 0, string name = "", bool isCreator = false) { - CharacterId = characterId; - Name = name; - IsCreator = isCreator; - } - - public ItemCoupleInfo Clone() { - return (ItemCoupleInfo) MemberwiseClone(); - } - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(CharacterId); - if (CharacterId != 0) { - writer.WriteUnicodeString(Name); - writer.WriteBool(IsCreator); - } - } - - public void ReadFrom(IByteReader reader) { - CharacterId = reader.ReadLong(); - if (CharacterId != 0) { - Name = reader.ReadUnicodeString(); - } - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public sealed class ItemCoupleInfo : IByteSerializable, IByteDeserializable { + public static readonly ItemCoupleInfo Default = new ItemCoupleInfo(); + + public long CharacterId { get; private set; } + public string Name { get; private set; } + public bool IsCreator { get; private set; } + + public ItemCoupleInfo(long characterId = 0, string name = "", bool isCreator = false) { + CharacterId = characterId; + Name = name; + IsCreator = isCreator; + } + + public ItemCoupleInfo Clone() { + return (ItemCoupleInfo) MemberwiseClone(); + } + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(CharacterId); + if (CharacterId != 0) { + writer.WriteUnicodeString(Name); + writer.WriteBool(IsCreator); + } + } + + public void ReadFrom(IByteReader reader) { + CharacterId = reader.ReadLong(); + if (CharacterId != 0) { + Name = reader.ReadUnicodeString(); + } + } +} diff --git a/Maple2.Model/Game/Item/ItemCustomMusicScore.cs b/Maple2.Model/Game/Item/ItemCustomMusicScore.cs index 0c9126b6e..c0d10e64f 100644 --- a/Maple2.Model/Game/Item/ItemCustomMusicScore.cs +++ b/Maple2.Model/Game/Item/ItemCustomMusicScore.cs @@ -1,49 +1,49 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public sealed class ItemCustomMusicScore : IByteSerializable, IByteDeserializable { - public int Length; - public Instrument Instrument; - public string Title; - public string Author; - public long AuthorId; // AccountId - public bool IsLocked; // true=>s_writemusic_error_cant_edit - public string Mml; - - public ItemCustomMusicScore() { - Title = string.Empty; - Author = string.Empty; - Mml = string.Empty; - } - - public ItemCustomMusicScore Clone() { - return (ItemCustomMusicScore) MemberwiseClone(); - } - - public void WriteTo(IByteWriter writer) { - writer.WriteInt(Length); - writer.Write(Instrument); - writer.WriteUnicodeString(Title); - writer.WriteUnicodeString(Author); - writer.WriteInt(1); - writer.WriteLong(AuthorId); - writer.WriteBool(IsLocked); - writer.WriteLong(); - writer.WriteLong(); - } - - public void ReadFrom(IByteReader reader) { - Length = reader.ReadInt(); - Instrument = reader.Read(); - Title = reader.ReadUnicodeString(); - Author = reader.ReadUnicodeString(); - reader.ReadInt(); - AuthorId = reader.ReadLong(); - IsLocked = reader.ReadBool(); - reader.ReadLong(); - reader.ReadLong(); - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public sealed class ItemCustomMusicScore : IByteSerializable, IByteDeserializable { + public int Length; + public Instrument Instrument; + public string Title; + public string Author; + public long AuthorId; // AccountId + public bool IsLocked; // true=>s_writemusic_error_cant_edit + public string Mml; + + public ItemCustomMusicScore() { + Title = string.Empty; + Author = string.Empty; + Mml = string.Empty; + } + + public ItemCustomMusicScore Clone() { + return (ItemCustomMusicScore) MemberwiseClone(); + } + + public void WriteTo(IByteWriter writer) { + writer.WriteInt(Length); + writer.Write(Instrument); + writer.WriteUnicodeString(Title); + writer.WriteUnicodeString(Author); + writer.WriteInt(1); + writer.WriteLong(AuthorId); + writer.WriteBool(IsLocked); + writer.WriteLong(); + writer.WriteLong(); + } + + public void ReadFrom(IByteReader reader) { + Length = reader.ReadInt(); + Instrument = reader.Read(); + Title = reader.ReadUnicodeString(); + Author = reader.ReadUnicodeString(); + reader.ReadInt(); + AuthorId = reader.ReadLong(); + IsLocked = reader.ReadBool(); + reader.ReadLong(); + reader.ReadLong(); + } +} diff --git a/Maple2.Model/Game/Item/ItemEnchant.cs b/Maple2.Model/Game/Item/ItemEnchant.cs index 5118fe0d9..8a3e0973f 100644 --- a/Maple2.Model/Game/Item/ItemEnchant.cs +++ b/Maple2.Model/Game/Item/ItemEnchant.cs @@ -1,66 +1,66 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public sealed class ItemEnchant : IByteSerializable, IByteDeserializable { - public static readonly ItemEnchant Default = new ItemEnchant(); - - public int Enchants { get; set; } - public int EnchantExp { get; set; } - // Enchant based peachy charges, otherwise always require 10 charges - public byte EnchantCharges { get; set; } - public bool Tradeable { get; private set; } - public int Charges { get; set; } - - public readonly Dictionary BasicOptions; - - public ItemEnchant(int enchants = 0, int enchantExp = 0, byte enchantCharges = 1, bool tradeable = true, int charges = 0, - Dictionary? basicOptions = null) { - Enchants = enchants; - EnchantExp = enchantExp; - EnchantCharges = enchantCharges; - Tradeable = tradeable; - Charges = charges; - BasicOptions = basicOptions ?? new Dictionary(); - } - - public ItemEnchant Clone() { - return new ItemEnchant(Enchants, EnchantExp, EnchantCharges, Tradeable, Charges, new Dictionary(BasicOptions)); - } - - public void WriteTo(IByteWriter writer) { - writer.WriteInt(Enchants); - writer.WriteInt(EnchantExp); - writer.WriteByte(EnchantCharges); - writer.WriteLong(); // Destabilized timestamp - writer.WriteInt(); - writer.WriteInt();// Enchantment attempts - writer.WriteBool(Tradeable); - writer.WriteInt(Charges); - - writer.WriteByte((byte) BasicOptions.Count); - foreach ((BasicAttribute type, BasicOption option) in BasicOptions) { - writer.WriteInt((int) type); - writer.Write(option); - } - } - - public void ReadFrom(IByteReader reader) { - Enchants = reader.ReadInt(); - EnchantExp = reader.ReadInt(); - EnchantCharges = reader.ReadByte(); - reader.ReadLong(); - reader.ReadInt(); - reader.ReadInt(); - Tradeable = reader.ReadBool(); - Charges = reader.ReadInt(); - - byte count = reader.ReadByte(); - for (int i = 0; i < count; i++) { - var type = (BasicAttribute) reader.ReadInt(); - BasicOptions[type] = reader.Read(); - } - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public sealed class ItemEnchant : IByteSerializable, IByteDeserializable { + public static readonly ItemEnchant Default = new ItemEnchant(); + + public int Enchants { get; set; } + public int EnchantExp { get; set; } + // Enchant based peachy charges, otherwise always require 10 charges + public byte EnchantCharges { get; set; } + public bool Tradeable { get; private set; } + public int Charges { get; set; } + + public readonly Dictionary BasicOptions; + + public ItemEnchant(int enchants = 0, int enchantExp = 0, byte enchantCharges = 1, bool tradeable = true, int charges = 0, + Dictionary? basicOptions = null) { + Enchants = enchants; + EnchantExp = enchantExp; + EnchantCharges = enchantCharges; + Tradeable = tradeable; + Charges = charges; + BasicOptions = basicOptions ?? new Dictionary(); + } + + public ItemEnchant Clone() { + return new ItemEnchant(Enchants, EnchantExp, EnchantCharges, Tradeable, Charges, new Dictionary(BasicOptions)); + } + + public void WriteTo(IByteWriter writer) { + writer.WriteInt(Enchants); + writer.WriteInt(EnchantExp); + writer.WriteByte(EnchantCharges); + writer.WriteLong(); // Destabilized timestamp + writer.WriteInt(); + writer.WriteInt();// Enchantment attempts + writer.WriteBool(Tradeable); + writer.WriteInt(Charges); + + writer.WriteByte((byte) BasicOptions.Count); + foreach ((BasicAttribute type, BasicOption option) in BasicOptions) { + writer.WriteInt((int) type); + writer.Write(option); + } + } + + public void ReadFrom(IByteReader reader) { + Enchants = reader.ReadInt(); + EnchantExp = reader.ReadInt(); + EnchantCharges = reader.ReadByte(); + reader.ReadLong(); + reader.ReadInt(); + reader.ReadInt(); + Tradeable = reader.ReadBool(); + Charges = reader.ReadInt(); + + byte count = reader.ReadByte(); + for (int i = 0; i < count; i++) { + var type = (BasicAttribute) reader.ReadInt(); + BasicOptions[type] = reader.Read(); + } + } +} diff --git a/Maple2.Model/Game/Item/ItemLimitBreak.cs b/Maple2.Model/Game/Item/ItemLimitBreak.cs index 2578712cc..2cc9a2d1b 100644 --- a/Maple2.Model/Game/Item/ItemLimitBreak.cs +++ b/Maple2.Model/Game/Item/ItemLimitBreak.cs @@ -1,60 +1,60 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public sealed class ItemLimitBreak : IByteSerializable, IByteDeserializable { - public static readonly ItemLimitBreak Default = new ItemLimitBreak(); - - public int Level { get; set; } - public readonly IDictionary BasicOptions; - public readonly IDictionary SpecialOptions; - - public ItemLimitBreak() { - BasicOptions = new Dictionary(); - SpecialOptions = new Dictionary(); - } - - public ItemLimitBreak Clone() { - return new ItemLimitBreak(Level, new Dictionary(BasicOptions), - new Dictionary(SpecialOptions)); - } - - public ItemLimitBreak(int level, IDictionary basicOptions, - IDictionary specialOptions) { - Level = level; - BasicOptions = basicOptions; - SpecialOptions = specialOptions; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteInt(Level); - - writer.WriteInt(BasicOptions.Count); - foreach ((BasicAttribute type, BasicOption option) in BasicOptions) { - writer.WriteShort((short) type); - writer.Write(option); - } - writer.WriteInt(SpecialOptions.Count); - foreach ((SpecialAttribute type, SpecialOption option) in SpecialOptions) { - writer.WriteShort((short) type); - writer.Write(option); - } - } - - public void ReadFrom(IByteReader reader) { - Level = reader.ReadInt(); - - int statCount = reader.ReadInt(); - for (int i = 0; i < statCount; i++) { - var type = (BasicAttribute) reader.ReadShort(); - BasicOptions[type] = reader.Read(); - } - int specialCount = reader.ReadInt(); - for (int i = 0; i < specialCount; i++) { - var type = (SpecialAttribute) reader.ReadShort(); - SpecialOptions[type] = reader.Read(); - } - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public sealed class ItemLimitBreak : IByteSerializable, IByteDeserializable { + public static readonly ItemLimitBreak Default = new ItemLimitBreak(); + + public int Level { get; set; } + public readonly IDictionary BasicOptions; + public readonly IDictionary SpecialOptions; + + public ItemLimitBreak() { + BasicOptions = new Dictionary(); + SpecialOptions = new Dictionary(); + } + + public ItemLimitBreak Clone() { + return new ItemLimitBreak(Level, new Dictionary(BasicOptions), + new Dictionary(SpecialOptions)); + } + + public ItemLimitBreak(int level, IDictionary basicOptions, + IDictionary specialOptions) { + Level = level; + BasicOptions = basicOptions; + SpecialOptions = specialOptions; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteInt(Level); + + writer.WriteInt(BasicOptions.Count); + foreach ((BasicAttribute type, BasicOption option) in BasicOptions) { + writer.WriteShort((short) type); + writer.Write(option); + } + writer.WriteInt(SpecialOptions.Count); + foreach ((SpecialAttribute type, SpecialOption option) in SpecialOptions) { + writer.WriteShort((short) type); + writer.Write(option); + } + } + + public void ReadFrom(IByteReader reader) { + Level = reader.ReadInt(); + + int statCount = reader.ReadInt(); + for (int i = 0; i < statCount; i++) { + var type = (BasicAttribute) reader.ReadShort(); + BasicOptions[type] = reader.Read(); + } + int specialCount = reader.ReadInt(); + for (int i = 0; i < specialCount; i++) { + var type = (SpecialAttribute) reader.ReadShort(); + SpecialOptions[type] = reader.Read(); + } + } +} diff --git a/Maple2.Model/Game/Item/ItemOption.cs b/Maple2.Model/Game/Item/ItemOption.cs index f8369b6b4..69fb1fffa 100644 --- a/Maple2.Model/Game/Item/ItemOption.cs +++ b/Maple2.Model/Game/Item/ItemOption.cs @@ -1,66 +1,66 @@ -using System.Runtime.InteropServices; -using Maple2.Model.Enum; - -namespace Maple2.Model.Game; - -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] -public readonly record struct BasicOption(int Value, float Rate = 0) { - public BasicOption(float percent) : this(0, percent) { } - - public static BasicOption operator +(BasicOption self, BasicOption other) { - return new BasicOption(self.Value + other.Value, self.Rate + other.Rate); - } - - public static BasicOption operator -(BasicOption self, BasicOption other) { - return new BasicOption(Math.Max(self.Value - other.Value, 0), Math.Max(self.Rate - other.Rate, 0)); - } -} - -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] -public readonly record struct SpecialOption(float Rate, float Value = 0) { - public static SpecialOption operator +(SpecialOption self, SpecialOption other) { - return new SpecialOption(self.Rate + other.Rate, self.Value + other.Value); - } - - public static SpecialOption operator -(SpecialOption self, SpecialOption other) { - return new SpecialOption(Math.Max(self.Rate - other.Rate, 0), Math.Max(self.Value - other.Value, 0)); - } -} - -public readonly struct LockOption { - private readonly BasicAttribute? basic; - private readonly SpecialAttribute? special; - private readonly bool lockValue; - - public LockOption(BasicAttribute attribute, bool lockValue = false) { - basic = attribute; - this.lockValue = lockValue; - } - - public LockOption(SpecialAttribute attribute, bool lockValue = false) { - special = attribute; - this.lockValue = lockValue; - } - - public bool TryGet(out BasicAttribute attribute, out bool valueLocked) { - valueLocked = lockValue; - if (basic == null) { - attribute = 0; - return false; - } - - attribute = (BasicAttribute) basic; - return true; - } - - public bool TryGet(out SpecialAttribute attribute, out bool valueLocked) { - valueLocked = lockValue; - if (special == null) { - attribute = 0; - return false; - } - - attribute = (SpecialAttribute) special; - return true; - } -} +using System.Runtime.InteropServices; +using Maple2.Model.Enum; + +namespace Maple2.Model.Game; + +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] +public readonly record struct BasicOption(int Value, float Rate = 0) { + public BasicOption(float percent) : this(0, percent) { } + + public static BasicOption operator +(BasicOption self, BasicOption other) { + return new BasicOption(self.Value + other.Value, self.Rate + other.Rate); + } + + public static BasicOption operator -(BasicOption self, BasicOption other) { + return new BasicOption(Math.Max(self.Value - other.Value, 0), Math.Max(self.Rate - other.Rate, 0)); + } +} + +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] +public readonly record struct SpecialOption(float Rate, float Value = 0) { + public static SpecialOption operator +(SpecialOption self, SpecialOption other) { + return new SpecialOption(self.Rate + other.Rate, self.Value + other.Value); + } + + public static SpecialOption operator -(SpecialOption self, SpecialOption other) { + return new SpecialOption(Math.Max(self.Rate - other.Rate, 0), Math.Max(self.Value - other.Value, 0)); + } +} + +public readonly struct LockOption { + private readonly BasicAttribute? basic; + private readonly SpecialAttribute? special; + private readonly bool lockValue; + + public LockOption(BasicAttribute attribute, bool lockValue = false) { + basic = attribute; + this.lockValue = lockValue; + } + + public LockOption(SpecialAttribute attribute, bool lockValue = false) { + special = attribute; + this.lockValue = lockValue; + } + + public bool TryGet(out BasicAttribute attribute, out bool valueLocked) { + valueLocked = lockValue; + if (basic == null) { + attribute = 0; + return false; + } + + attribute = (BasicAttribute) basic; + return true; + } + + public bool TryGet(out SpecialAttribute attribute, out bool valueLocked) { + valueLocked = lockValue; + if (special == null) { + attribute = 0; + return false; + } + + attribute = (SpecialAttribute) special; + return true; + } +} diff --git a/Maple2.Model/Game/Item/ItemPet.cs b/Maple2.Model/Game/Item/ItemPet.cs index a045b90a3..c4197665e 100644 --- a/Maple2.Model/Game/Item/ItemPet.cs +++ b/Maple2.Model/Game/Item/ItemPet.cs @@ -1,40 +1,40 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public sealed class ItemPet : IByteSerializable, IByteDeserializable { - public string Name; - public long Exp; - public int EvolvePoints; - public short Level; - public bool HasItems; - - public short RenameRemaining; - - public ItemPet() { - Name = string.Empty; - Level = 1; - RenameRemaining = 1; - } - - public ItemPet Clone() { - return (ItemPet) MemberwiseClone(); - } - - public void WriteTo(IByteWriter writer) { - writer.WriteUnicodeString(Name); - writer.WriteLong(Exp); - writer.WriteInt(EvolvePoints); - writer.WriteInt(Level); - writer.WriteBool(HasItems); - } - - public void ReadFrom(IByteReader reader) { - Name = reader.ReadUnicodeString(); - Exp = reader.ReadLong(); - EvolvePoints = reader.ReadInt(); - Level = (short) reader.ReadInt(); - HasItems = reader.ReadBool(); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public sealed class ItemPet : IByteSerializable, IByteDeserializable { + public string Name; + public long Exp; + public int EvolvePoints; + public short Level; + public bool HasItems; + + public short RenameRemaining; + + public ItemPet() { + Name = string.Empty; + Level = 1; + RenameRemaining = 1; + } + + public ItemPet Clone() { + return (ItemPet) MemberwiseClone(); + } + + public void WriteTo(IByteWriter writer) { + writer.WriteUnicodeString(Name); + writer.WriteLong(Exp); + writer.WriteInt(EvolvePoints); + writer.WriteInt(Level); + writer.WriteBool(HasItems); + } + + public void ReadFrom(IByteReader reader) { + Name = reader.ReadUnicodeString(); + Exp = reader.ReadLong(); + EvolvePoints = reader.ReadInt(); + Level = (short) reader.ReadInt(); + HasItems = reader.ReadBool(); + } +} diff --git a/Maple2.Model/Game/Item/ItemSocket.cs b/Maple2.Model/Game/Item/ItemSocket.cs index d0fdb4f88..6600aafde 100644 --- a/Maple2.Model/Game/Item/ItemSocket.cs +++ b/Maple2.Model/Game/Item/ItemSocket.cs @@ -1,110 +1,110 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game; - -public sealed class ItemSocket : IByteSerializable, IByteDeserializable { - public static readonly ItemSocket Default = new ItemSocket(0, 0); - - public byte MaxSlots; - public byte UnlockSlots { - get => (byte) Sockets.Length; - set => Array.Resize(ref Sockets, Math.Min(MaxSlots, value)); - } - - public ItemGemstone?[] Sockets; - - public ItemSocket(byte maxSlots, byte unlocked) { - MaxSlots = maxSlots; - Sockets = new ItemGemstone?[unlocked]; - } - - public ItemSocket(byte maxSlots, ItemGemstone?[] sockets) { - MaxSlots = maxSlots; - Sockets = sockets; - } - - public ItemSocket Clone() { - var sockets = new ItemGemstone?[Sockets.Length]; - for (int i = 0; i < Sockets.Length; i++) { - sockets[i] = Sockets[i]?.Clone(); - } - - return new ItemSocket(MaxSlots, sockets); - } - - public void WriteTo(IByteWriter writer) { - writer.WriteByte(MaxSlots); - writer.WriteByte(UnlockSlots); - for (int i = 0; i < UnlockSlots; i++) { - ItemGemstone? gem = Sockets[i]; - writer.WriteBool(gem != null); - if (gem != null) { - writer.WriteClass(gem); - } - } - } - - public void ReadFrom(IByteReader reader) { - MaxSlots = reader.ReadByte(); - UnlockSlots = reader.ReadByte(); - for (int i = 0; i < UnlockSlots; i++) { - bool hasGem = reader.ReadBool(); - if (hasGem) { - Sockets[i] = reader.ReadClass(); - } - } - } -} - -public class ItemGemstone : IByteSerializable, IByteDeserializable { - public int ItemId; - public ItemBinding? Binding; - public ItemStats? Stats; - public bool IsLocked; - public long UnlockTime; - - public ItemGemstone(int itemId = 0, ItemBinding? binding = null, ItemStats? stats = null, bool isLocked = false, long unlockTime = 0) { - if (stats == null) throw new ArgumentNullException(nameof(stats)); - ItemId = itemId; - Binding = binding; - IsLocked = isLocked; - UnlockTime = unlockTime; - Stats = stats; - } - - public ItemGemstone Clone() { - return new ItemGemstone(ItemId, Binding?.Clone(), Stats?.Clone(), IsLocked, UnlockTime); - } - - public void WriteTo(IByteWriter writer) { - writer.WriteInt(ItemId); - - writer.WriteBool(Binding != null); - if (Binding != null) { - writer.WriteClass(Binding); - } - - writer.WriteBool(IsLocked); - if (IsLocked) { - writer.WriteByte(); - writer.WriteLong(UnlockTime); - } - } - - public void ReadFrom(IByteReader reader) { - ItemId = reader.ReadInt(); - - bool isBound = reader.ReadBool(); - if (isBound) { - Binding = reader.ReadClass(); - } - - IsLocked = reader.ReadBool(); - if (IsLocked) { - reader.ReadByte(); - UnlockTime = reader.ReadLong(); - } - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game; + +public sealed class ItemSocket : IByteSerializable, IByteDeserializable { + public static readonly ItemSocket Default = new ItemSocket(0, 0); + + public byte MaxSlots; + public byte UnlockSlots { + get => (byte) Sockets.Length; + set => Array.Resize(ref Sockets, Math.Min(MaxSlots, value)); + } + + public ItemGemstone?[] Sockets; + + public ItemSocket(byte maxSlots, byte unlocked) { + MaxSlots = maxSlots; + Sockets = new ItemGemstone?[unlocked]; + } + + public ItemSocket(byte maxSlots, ItemGemstone?[] sockets) { + MaxSlots = maxSlots; + Sockets = sockets; + } + + public ItemSocket Clone() { + var sockets = new ItemGemstone?[Sockets.Length]; + for (int i = 0; i < Sockets.Length; i++) { + sockets[i] = Sockets[i]?.Clone(); + } + + return new ItemSocket(MaxSlots, sockets); + } + + public void WriteTo(IByteWriter writer) { + writer.WriteByte(MaxSlots); + writer.WriteByte(UnlockSlots); + for (int i = 0; i < UnlockSlots; i++) { + ItemGemstone? gem = Sockets[i]; + writer.WriteBool(gem != null); + if (gem != null) { + writer.WriteClass(gem); + } + } + } + + public void ReadFrom(IByteReader reader) { + MaxSlots = reader.ReadByte(); + UnlockSlots = reader.ReadByte(); + for (int i = 0; i < UnlockSlots; i++) { + bool hasGem = reader.ReadBool(); + if (hasGem) { + Sockets[i] = reader.ReadClass(); + } + } + } +} + +public class ItemGemstone : IByteSerializable, IByteDeserializable { + public int ItemId; + public ItemBinding? Binding; + public ItemStats? Stats; + public bool IsLocked; + public long UnlockTime; + + public ItemGemstone(int itemId = 0, ItemBinding? binding = null, ItemStats? stats = null, bool isLocked = false, long unlockTime = 0) { + if (stats == null) throw new ArgumentNullException(nameof(stats)); + ItemId = itemId; + Binding = binding; + IsLocked = isLocked; + UnlockTime = unlockTime; + Stats = stats; + } + + public ItemGemstone Clone() { + return new ItemGemstone(ItemId, Binding?.Clone(), Stats?.Clone(), IsLocked, UnlockTime); + } + + public void WriteTo(IByteWriter writer) { + writer.WriteInt(ItemId); + + writer.WriteBool(Binding != null); + if (Binding != null) { + writer.WriteClass(Binding); + } + + writer.WriteBool(IsLocked); + if (IsLocked) { + writer.WriteByte(); + writer.WriteLong(UnlockTime); + } + } + + public void ReadFrom(IByteReader reader) { + ItemId = reader.ReadInt(); + + bool isBound = reader.ReadBool(); + if (isBound) { + Binding = reader.ReadClass(); + } + + IsLocked = reader.ReadBool(); + if (IsLocked) { + reader.ReadByte(); + UnlockTime = reader.ReadLong(); + } + } +} diff --git a/Maple2.Model/Game/Item/ItemStats.cs b/Maple2.Model/Game/Item/ItemStats.cs index a26bd4a3c..00bc7e534 100644 --- a/Maple2.Model/Game/Item/ItemStats.cs +++ b/Maple2.Model/Game/Item/ItemStats.cs @@ -1,125 +1,125 @@ -using System.Text; -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game; - -public sealed class ItemStats : IByteSerializable, IByteDeserializable { - public static readonly ItemStats Default = new ItemStats(); - - public const int TYPE_COUNT = 9; - - public enum Type { - Constant = 0, - Static = 1, - Random = 2, - Title = 3, - Empowerment1 = 4, - Empowerment2 = 5, - Empowerment3 = 6, - Empowerment4 = 7, - Empowerment5 = 8, - } - - private readonly Option[] options; - - public ItemStats() { - options = new Option[TYPE_COUNT]; - for (int i = 0; i < TYPE_COUNT; i++) { - options[i] = new Option(); - } - } - - public ItemStats(Dictionary[] basicOption, Dictionary[] specialOption) { - // Ensure all entries are set. - options = new Option[TYPE_COUNT]; - for (int i = 0; i < TYPE_COUNT; i++) { - options[i] = new Option( - basicOption.ElementAtOrDefault(i, () => new Dictionary()), - specialOption.ElementAtOrDefault(i, () => new Dictionary())); - } - } - - public ItemStats Clone() { - var stats = new ItemStats(); - for (int i = 0; i < TYPE_COUNT; i++) { - stats.options[i] = new Option( - new Dictionary(options[i].Basic), - new Dictionary(options[i].Special)); - } - return stats; - } - - public Option this[Type type] { - get => options[(int) type]; - set => options[(int) type] = value; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteByte(); - for (int i = 0; i < TYPE_COUNT; i++) { - Option option = options[i]; - writer.WriteShort((short) option.Basic.Count); - foreach ((BasicAttribute type, BasicOption basicOption) in option.Basic) { - writer.WriteShort((short) type); - writer.Write(basicOption); - } - writer.WriteShort((short) option.Special.Count); - foreach ((SpecialAttribute type, SpecialOption specialOption) in option.Special) { - writer.WriteShort((short) type); - writer.Write(specialOption); - } - - writer.WriteInt(); - } - } - - public void ReadFrom(IByteReader reader) { - reader.ReadByte(); - for (int i = 0; i < TYPE_COUNT; i++) { - Option option = options[i]; - short basicCount = reader.ReadShort(); - for (int j = 0; j < basicCount; j++) { - var type = (BasicAttribute) reader.ReadShort(); - option.Basic[type] = reader.Read(); - } - short specialCount = reader.ReadShort(); - for (int j = 0; j < specialCount; j++) { - var type = (SpecialAttribute) reader.ReadShort(); - option.Special[type] = reader.Read(); - } - - reader.ReadInt(); - } - } - - public class Option { - public readonly Dictionary Basic; - public readonly Dictionary Special; - - public readonly float MultiplyFactor; - - public int Count => Basic.Count + Special.Count; - - public Option(Dictionary? basicOption = null, Dictionary? specialOption = null, float multiplyFactor = 1) { - Basic = basicOption ?? new Dictionary(); - Special = specialOption ?? new Dictionary(); - MultiplyFactor = multiplyFactor; - } - - public override string ToString() { - var builder = new StringBuilder(); - builder.AppendLine("BasicOption:"); - foreach ((BasicAttribute attribute, BasicOption option) in Basic) { - builder.AppendLine($"- {attribute}={option}"); - } - builder.AppendLine("SpecialOption:"); - foreach ((SpecialAttribute attribute, SpecialOption option) in Special) { - builder.AppendLine($"- {attribute}={option}"); - } - return builder.ToString(); - } - } -} +using System.Text; +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game; + +public sealed class ItemStats : IByteSerializable, IByteDeserializable { + public static readonly ItemStats Default = new ItemStats(); + + public const int TYPE_COUNT = 9; + + public enum Type { + Constant = 0, + Static = 1, + Random = 2, + Title = 3, + Empowerment1 = 4, + Empowerment2 = 5, + Empowerment3 = 6, + Empowerment4 = 7, + Empowerment5 = 8, + } + + private readonly Option[] options; + + public ItemStats() { + options = new Option[TYPE_COUNT]; + for (int i = 0; i < TYPE_COUNT; i++) { + options[i] = new Option(); + } + } + + public ItemStats(Dictionary[] basicOption, Dictionary[] specialOption) { + // Ensure all entries are set. + options = new Option[TYPE_COUNT]; + for (int i = 0; i < TYPE_COUNT; i++) { + options[i] = new Option( + basicOption.ElementAtOrDefault(i, () => new Dictionary()), + specialOption.ElementAtOrDefault(i, () => new Dictionary())); + } + } + + public ItemStats Clone() { + var stats = new ItemStats(); + for (int i = 0; i < TYPE_COUNT; i++) { + stats.options[i] = new Option( + new Dictionary(options[i].Basic), + new Dictionary(options[i].Special)); + } + return stats; + } + + public Option this[Type type] { + get => options[(int) type]; + set => options[(int) type] = value; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteByte(); + for (int i = 0; i < TYPE_COUNT; i++) { + Option option = options[i]; + writer.WriteShort((short) option.Basic.Count); + foreach ((BasicAttribute type, BasicOption basicOption) in option.Basic) { + writer.WriteShort((short) type); + writer.Write(basicOption); + } + writer.WriteShort((short) option.Special.Count); + foreach ((SpecialAttribute type, SpecialOption specialOption) in option.Special) { + writer.WriteShort((short) type); + writer.Write(specialOption); + } + + writer.WriteInt(); + } + } + + public void ReadFrom(IByteReader reader) { + reader.ReadByte(); + for (int i = 0; i < TYPE_COUNT; i++) { + Option option = options[i]; + short basicCount = reader.ReadShort(); + for (int j = 0; j < basicCount; j++) { + var type = (BasicAttribute) reader.ReadShort(); + option.Basic[type] = reader.Read(); + } + short specialCount = reader.ReadShort(); + for (int j = 0; j < specialCount; j++) { + var type = (SpecialAttribute) reader.ReadShort(); + option.Special[type] = reader.Read(); + } + + reader.ReadInt(); + } + } + + public class Option { + public readonly Dictionary Basic; + public readonly Dictionary Special; + + public readonly float MultiplyFactor; + + public int Count => Basic.Count + Special.Count; + + public Option(Dictionary? basicOption = null, Dictionary? specialOption = null, float multiplyFactor = 1) { + Basic = basicOption ?? new Dictionary(); + Special = specialOption ?? new Dictionary(); + MultiplyFactor = multiplyFactor; + } + + public override string ToString() { + var builder = new StringBuilder(); + builder.AppendLine("BasicOption:"); + foreach ((BasicAttribute attribute, BasicOption option) in Basic) { + builder.AppendLine($"- {attribute}={option}"); + } + builder.AppendLine("SpecialOption:"); + foreach ((SpecialAttribute attribute, SpecialOption option) in Special) { + builder.AppendLine($"- {attribute}={option}"); + } + return builder.ToString(); + } + } +} diff --git a/Maple2.Model/Game/Item/ItemTransfer.cs b/Maple2.Model/Game/Item/ItemTransfer.cs index d632627d1..b934875f7 100644 --- a/Maple2.Model/Game/Item/ItemTransfer.cs +++ b/Maple2.Model/Game/Item/ItemTransfer.cs @@ -1,80 +1,80 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game; - -public sealed class ItemTransfer : IByteSerializable, IByteDeserializable { - public static readonly ItemTransfer Default = new ItemTransfer(); - - public TransferFlag Flag { get; set; } - public int RemainTrades { get; set; } - public int RepackageCount { get; set; } - - public ItemBinding? Binding { get; private set; } - - public ItemTransfer(TransferFlag flag = 0, int remainTrades = 0, int repackageCount = 0, ItemBinding? binding = null) { - Flag = flag; - RemainTrades = remainTrades; - RepackageCount = repackageCount; - Binding = binding; - } - - public ItemTransfer Clone() { - return new ItemTransfer(Flag, RemainTrades, RepackageCount, Binding?.Clone()); - } - - public bool Bind(Character character) { - if (!Flag.HasFlag(TransferFlag.Bind)) { - return false; - } - - if (Binding != null) { - return false; - } - - Binding = new ItemBinding(character.Id, character.Name); - RemainTrades = 0; - return true; - } - - public void WriteTo(IByteWriter writer) { - // CItemTransfer is CItem[66] - writer.Write(Flag); // CItemTransfer[5] - writer.WriteBool(false); // CItemTransfer[9] *bit-1* - writer.WriteInt(RemainTrades); // CItemTransfer[10] - writer.WriteInt(RepackageCount); // CItemTransfer[11] - writer.WriteByte(); // CItemTransfer[12] - writer.WriteBool(true); // CItemTransfer[9] *bit-10* (socketTransfer?) - - // CharBound means untradable, unsellable, bound to char (ignores TransferFlag) - writer.WriteBool(Binding != null); - if (Binding != null) { - writer.WriteClass(Binding); - } - } - - public void ReadFrom(IByteReader reader) { - Flag = reader.Read(); - reader.ReadByte(); - RemainTrades = reader.ReadInt(); - RepackageCount = reader.ReadInt(); - reader.ReadByte(); - reader.ReadBool(); - bool isBound = reader.ReadBool(); - if (isBound) { - Binding = reader.ReadClass(); - } - } - - public override bool Equals(object? obj) { - if (ReferenceEquals(this, obj)) return true; - if (!(obj is ItemTransfer other)) return false; - return Flag == other.Flag && Equals(Binding, other.Binding); - } - - public override int GetHashCode() { - return HashCode.Combine((int) Flag, Binding); - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game; + +public sealed class ItemTransfer : IByteSerializable, IByteDeserializable { + public static readonly ItemTransfer Default = new ItemTransfer(); + + public TransferFlag Flag { get; set; } + public int RemainTrades { get; set; } + public int RepackageCount { get; set; } + + public ItemBinding? Binding { get; private set; } + + public ItemTransfer(TransferFlag flag = 0, int remainTrades = 0, int repackageCount = 0, ItemBinding? binding = null) { + Flag = flag; + RemainTrades = remainTrades; + RepackageCount = repackageCount; + Binding = binding; + } + + public ItemTransfer Clone() { + return new ItemTransfer(Flag, RemainTrades, RepackageCount, Binding?.Clone()); + } + + public bool Bind(Character character) { + if (!Flag.HasFlag(TransferFlag.Bind)) { + return false; + } + + if (Binding != null) { + return false; + } + + Binding = new ItemBinding(character.Id, character.Name); + RemainTrades = 0; + return true; + } + + public void WriteTo(IByteWriter writer) { + // CItemTransfer is CItem[66] + writer.Write(Flag); // CItemTransfer[5] + writer.WriteBool(false); // CItemTransfer[9] *bit-1* + writer.WriteInt(RemainTrades); // CItemTransfer[10] + writer.WriteInt(RepackageCount); // CItemTransfer[11] + writer.WriteByte(); // CItemTransfer[12] + writer.WriteBool(true); // CItemTransfer[9] *bit-10* (socketTransfer?) + + // CharBound means untradable, unsellable, bound to char (ignores TransferFlag) + writer.WriteBool(Binding != null); + if (Binding != null) { + writer.WriteClass(Binding); + } + } + + public void ReadFrom(IByteReader reader) { + Flag = reader.Read(); + reader.ReadByte(); + RemainTrades = reader.ReadInt(); + RepackageCount = reader.ReadInt(); + reader.ReadByte(); + reader.ReadBool(); + bool isBound = reader.ReadBool(); + if (isBound) { + Binding = reader.ReadClass(); + } + } + + public override bool Equals(object? obj) { + if (ReferenceEquals(this, obj)) return true; + if (!(obj is ItemTransfer other)) return false; + return Flag == other.Flag && Equals(Binding, other.Binding); + } + + public override int GetHashCode() { + return HashCode.Combine((int) Flag, Binding); + } +} diff --git a/Maple2.Model/Game/Item/ItemType.cs b/Maple2.Model/Game/Item/ItemType.cs index 739f59902..47b654487 100644 --- a/Maple2.Model/Game/Item/ItemType.cs +++ b/Maple2.Model/Game/Item/ItemType.cs @@ -1,116 +1,116 @@ -namespace Maple2.Model.Game; - -public readonly record struct ItemType(byte Group, byte Type) { - public ItemType(int id) : this((byte) (id / 10000000), (byte) ((id % 10000000) / 100000)) { } - - public bool IsSkin => Group is 0 && Type is 0; - public bool IsHair => Group is 1 && Type is 2; - public bool IsFace => Group is 1 && Type is 3; - public bool IsDecal => Group is 1 && Type is 4; - public bool IsEar => Group is 1 && Type is 5; - - public bool IsAccessory => Group is 1 && Type is 12 or >= 18 and <= 21; - public bool IsEarring => Group is 1 && Type is 12; - public bool IsCape => Group is 1 && Type is 18; - public bool IsNecklace => Group is 1 && Type is 19; - public bool IsRing => Group is 1 && Type is 20; - public bool IsBelt => Group is 1 && Type is 21; - - public bool IsArmor => Group is 1 && Type is >= 13 and <= 17 or 22; - public bool IsHat => Group is 1 && Type is 13; - public bool IsClothes => Group is 1 && Type is 14; - public bool IsPants => Group is 1 && Type is 15; - public bool IsGloves => Group is 1 && Type is 16; - public bool IsShoes => Group is 1 && Type is 17; - public bool IsOverall => Group is 1 && Type is 22; - - public bool IsWeapon => Group is 1 && Type is >= 30 and <= 39 or >= 40 and <= 49 or >= 50 and <= 59; - public bool IsOneHandWeapon => Group is 1 && Type is >= 30 and <= 39; - public bool IsOffHandWeapon => Group is 1 && Type is >= 40 and <= 49; - public bool IsTwoHandWeapon => Group is 1 && Type is >= 50 and <= 59; - public bool IsBludgeon => Group is 1 && Type is 30; - public bool IsDagger => Group is 1 && Type is 31; - public bool IsLongsword => Group is 1 && Type is 32; - public bool IsScepter => Group is 1 && Type is 33; - public bool IsThrowingStar => Group is 1 && Type is 34; - public bool IsSpellbook => Group is 1 && Type is 40; - public bool IsShield => Group is 1 && Type is 41; - public bool IsGreatsword => Group is 1 && Type is 50; - public bool IsBow => Group is 1 && Type is 51; - public bool IsStaff => Group is 1 && Type is 52; - public bool IsCannon => Group is 1 && Type is 53; - public bool IsBlade => Group is 1 && Type is 54; - public bool IsKnuckle => Group is 1 && Type is 55; - public bool IsOrb => Group is 1 && Type is 56; - - public bool IsObjectWeapon => Group is 1 && Type is >= 80 and <= 89; - public bool IsFishingDummy => Group is 1 && Type is 90; - public bool IsInstrumentDummy => Group is 1 && Type is 91; - - public bool IsConsumable => Group is 2 && Type is 0; - public bool IsEmote => Group is 2 && Type is 2; - public bool IsItemPack => Group is 2 && Type is 3; - public bool IsCompanionCookie => Group is 2 && Type is 4; - public bool IsBeautyVoucher => Group is 2 && Type is 5; - public bool IsAdBalloon => Group is 2 && Type is 6; - public bool IsBuddyBadgeChest => Group is 2 && Type is 7; - public bool IsSuperChatTheme => Group is 2 && Type is 8; - public bool IsMedal => Group is 2 && Type is 9; - public bool IsStickerPack => Group is 2 && Type is 11; - public bool IsOutfitCapsule => Group is 2 && Type is 20; - public bool IsOutfitCoin => Group is 2 && Type is 22; - public bool IsExchangeTranscendenceCrystal => Group is 2 && Type is 30; - - public bool IsMisc => Group is 3 && Type is 0; - public bool IsScroll => Group is 3 && Type is 10; - public bool IsFishingRod => Group is 3 && Type is 20; - public bool IsMastery => Group is 3 && Type is 30; - public bool IsInstrument => Group is 3 && Type is 40; - public bool IsMusicScore => Group is 3 && Type is 50 or 51; - public bool IsPresetMusicScore => Group is 3 && Type is 51; - public bool IsCustomMusicScore => Group is 3 && Type is 51; - public bool IsBlueprint => Group is 3 && Type is 52; - public bool IsFragment => Group is 3 && Type is 60; - public bool IsTranscendenceCrystal => Group is 3 && Type is 70; - public bool IsBook => Group is 3 && Type is 90; - - public bool IsGemstone => Group is 4 && Type is 2; - public bool IsGemDust => Group is 4 && Type is 3; - public bool IsAirMount => Group is 4 && Type is 4; - public bool IsLapenshard => Group is 4 && Type is 10 or 20 or 30; - public bool IsRedLapenshard => Group is 4 && Type is 10; - public bool IsBlueLapenshard => Group is 4 && Type is 20; - public bool IsGreenLapenshard => Group is 4 && Type is 30; - - public bool IsFurnishing => Group is 5 && Type is >= 1 and <= 4 or 7 or 8 or 9; - public bool IsFloorFurnishing => Group is 5 && Type is 1; - public bool IsDisplayFurnishing => Group is 5 && Type is 2; - public bool IsSkillFurnishing => Group is 5 && Type is 3; - public bool IsMonsterBox => Group is 5 && Type is 5; - public bool IsGroundMount => Group is 5 && Type is 6; - public bool IsSouvenir => Group is 5 && Type is 7; - public bool IsMaid => Group is 5 && Type is 8; - public bool IsHousePackage => Group is 5 && Type is 90; - public bool IsRoomPackage => Group is 5 && Type is 91; - public bool IsOutfitPackage => Group is 5 && Type is 92; - public bool IsFurnishingSet => Group is 5 && Type is 93; - public bool IsPetCapsule => Group is 5 && Type is 94; - public bool IsPetFood => Group is 5 && Type is 95; - - public bool IsPet => Group is 6 && Type is 0 or >= 10 and <= 29; - public bool IsStoragePet => Group is 6 && Type is 0; - public bool IsCombatPet => Group is 6 && Type is >= 10 and <= 29; - public bool IsPetCandy => Group is 6 && Type is 30; - public bool IsPetTrap => Group is 6 && Type is 31; - - public bool IsBadge => Group is 7; - public bool IsPetSkin => Group is 7 && Type is 1; - public bool IsChatBubble => Group is 7 && Type is 2; - public bool IsNameTag => Group is 7 && Type is 3; - public bool IsDamageSkin => Group is 7 && Type is 4; - public bool IsTombstone => Group is 7 && Type is 5; - public bool IsSwimTube => Group is 7 && Type is 6; - public bool IsFishingBadge => Group is 7 && Type is 7; - public bool IsBuddyBadge => Group is 7 && Type is 8; - public bool IsEffectBadge => Group is 7 && Type is 9; -} +namespace Maple2.Model.Game; + +public readonly record struct ItemType(byte Group, byte Type) { + public ItemType(int id) : this((byte) (id / 10000000), (byte) ((id % 10000000) / 100000)) { } + + public bool IsSkin => Group is 0 && Type is 0; + public bool IsHair => Group is 1 && Type is 2; + public bool IsFace => Group is 1 && Type is 3; + public bool IsDecal => Group is 1 && Type is 4; + public bool IsEar => Group is 1 && Type is 5; + + public bool IsAccessory => Group is 1 && Type is 12 or >= 18 and <= 21; + public bool IsEarring => Group is 1 && Type is 12; + public bool IsCape => Group is 1 && Type is 18; + public bool IsNecklace => Group is 1 && Type is 19; + public bool IsRing => Group is 1 && Type is 20; + public bool IsBelt => Group is 1 && Type is 21; + + public bool IsArmor => Group is 1 && Type is >= 13 and <= 17 or 22; + public bool IsHat => Group is 1 && Type is 13; + public bool IsClothes => Group is 1 && Type is 14; + public bool IsPants => Group is 1 && Type is 15; + public bool IsGloves => Group is 1 && Type is 16; + public bool IsShoes => Group is 1 && Type is 17; + public bool IsOverall => Group is 1 && Type is 22; + + public bool IsWeapon => Group is 1 && Type is >= 30 and <= 39 or >= 40 and <= 49 or >= 50 and <= 59; + public bool IsOneHandWeapon => Group is 1 && Type is >= 30 and <= 39; + public bool IsOffHandWeapon => Group is 1 && Type is >= 40 and <= 49; + public bool IsTwoHandWeapon => Group is 1 && Type is >= 50 and <= 59; + public bool IsBludgeon => Group is 1 && Type is 30; + public bool IsDagger => Group is 1 && Type is 31; + public bool IsLongsword => Group is 1 && Type is 32; + public bool IsScepter => Group is 1 && Type is 33; + public bool IsThrowingStar => Group is 1 && Type is 34; + public bool IsSpellbook => Group is 1 && Type is 40; + public bool IsShield => Group is 1 && Type is 41; + public bool IsGreatsword => Group is 1 && Type is 50; + public bool IsBow => Group is 1 && Type is 51; + public bool IsStaff => Group is 1 && Type is 52; + public bool IsCannon => Group is 1 && Type is 53; + public bool IsBlade => Group is 1 && Type is 54; + public bool IsKnuckle => Group is 1 && Type is 55; + public bool IsOrb => Group is 1 && Type is 56; + + public bool IsObjectWeapon => Group is 1 && Type is >= 80 and <= 89; + public bool IsFishingDummy => Group is 1 && Type is 90; + public bool IsInstrumentDummy => Group is 1 && Type is 91; + + public bool IsConsumable => Group is 2 && Type is 0; + public bool IsEmote => Group is 2 && Type is 2; + public bool IsItemPack => Group is 2 && Type is 3; + public bool IsCompanionCookie => Group is 2 && Type is 4; + public bool IsBeautyVoucher => Group is 2 && Type is 5; + public bool IsAdBalloon => Group is 2 && Type is 6; + public bool IsBuddyBadgeChest => Group is 2 && Type is 7; + public bool IsSuperChatTheme => Group is 2 && Type is 8; + public bool IsMedal => Group is 2 && Type is 9; + public bool IsStickerPack => Group is 2 && Type is 11; + public bool IsOutfitCapsule => Group is 2 && Type is 20; + public bool IsOutfitCoin => Group is 2 && Type is 22; + public bool IsExchangeTranscendenceCrystal => Group is 2 && Type is 30; + + public bool IsMisc => Group is 3 && Type is 0; + public bool IsScroll => Group is 3 && Type is 10; + public bool IsFishingRod => Group is 3 && Type is 20; + public bool IsMastery => Group is 3 && Type is 30; + public bool IsInstrument => Group is 3 && Type is 40; + public bool IsMusicScore => Group is 3 && Type is 50 or 51; + public bool IsPresetMusicScore => Group is 3 && Type is 51; + public bool IsCustomMusicScore => Group is 3 && Type is 51; + public bool IsBlueprint => Group is 3 && Type is 52; + public bool IsFragment => Group is 3 && Type is 60; + public bool IsTranscendenceCrystal => Group is 3 && Type is 70; + public bool IsBook => Group is 3 && Type is 90; + + public bool IsGemstone => Group is 4 && Type is 2; + public bool IsGemDust => Group is 4 && Type is 3; + public bool IsAirMount => Group is 4 && Type is 4; + public bool IsLapenshard => Group is 4 && Type is 10 or 20 or 30; + public bool IsRedLapenshard => Group is 4 && Type is 10; + public bool IsBlueLapenshard => Group is 4 && Type is 20; + public bool IsGreenLapenshard => Group is 4 && Type is 30; + + public bool IsFurnishing => Group is 5 && Type is >= 1 and <= 4 or 7 or 8 or 9; + public bool IsFloorFurnishing => Group is 5 && Type is 1; + public bool IsDisplayFurnishing => Group is 5 && Type is 2; + public bool IsSkillFurnishing => Group is 5 && Type is 3; + public bool IsMonsterBox => Group is 5 && Type is 5; + public bool IsGroundMount => Group is 5 && Type is 6; + public bool IsSouvenir => Group is 5 && Type is 7; + public bool IsMaid => Group is 5 && Type is 8; + public bool IsHousePackage => Group is 5 && Type is 90; + public bool IsRoomPackage => Group is 5 && Type is 91; + public bool IsOutfitPackage => Group is 5 && Type is 92; + public bool IsFurnishingSet => Group is 5 && Type is 93; + public bool IsPetCapsule => Group is 5 && Type is 94; + public bool IsPetFood => Group is 5 && Type is 95; + + public bool IsPet => Group is 6 && Type is 0 or >= 10 and <= 29; + public bool IsStoragePet => Group is 6 && Type is 0; + public bool IsCombatPet => Group is 6 && Type is >= 10 and <= 29; + public bool IsPetCandy => Group is 6 && Type is 30; + public bool IsPetTrap => Group is 6 && Type is 31; + + public bool IsBadge => Group is 7; + public bool IsPetSkin => Group is 7 && Type is 1; + public bool IsChatBubble => Group is 7 && Type is 2; + public bool IsNameTag => Group is 7 && Type is 3; + public bool IsDamageSkin => Group is 7 && Type is 4; + public bool IsTombstone => Group is 7 && Type is 5; + public bool IsSwimTube => Group is 7 && Type is 6; + public bool IsFishingBadge => Group is 7 && Type is 7; + public bool IsBuddyBadge => Group is 7 && Type is 8; + public bool IsEffectBadge => Group is 7 && Type is 9; +} diff --git a/Maple2.Model/Game/Item/UgcItemLook.cs b/Maple2.Model/Game/Item/UgcItemLook.cs index 7307b4a48..44c12fe33 100644 --- a/Maple2.Model/Game/Item/UgcItemLook.cs +++ b/Maple2.Model/Game/Item/UgcItemLook.cs @@ -1,56 +1,56 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public sealed class UgcItemLook : IByteSerializable, IByteDeserializable { - public static readonly UgcItemLook Default = new UgcItemLook(); - - public long Id; - public string FileName; - public string Name; - public long AccountId; - public long CharacterId; - public string Author; - public long CreationTime; - public string Url; - - public UgcItemLook() { - FileName = string.Empty; - Name = string.Empty; - Author = string.Empty; - Url = string.Empty; - } - - public UgcItemLook Clone() { - return (UgcItemLook) MemberwiseClone(); - } - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(Id); - writer.WriteUnicodeString(Id.ToString()); // UUID (filename) - writer.WriteUnicodeString(Name); // Name (itemname) - writer.WriteByte(1); // Needs to be 1 to show the proper UGC icon? - writer.WriteInt(1); - writer.WriteLong(AccountId); // AccountId - writer.WriteLong(CharacterId); // CharacterId - writer.WriteUnicodeString(Author); // CharacterName - writer.WriteLong(CreationTime); // CreationTime - writer.WriteUnicodeString(Url); // URL (no domain) - writer.WriteByte(); - } - - public void ReadFrom(IByteReader reader) { - Id = reader.ReadLong(); - FileName = reader.ReadUnicodeString(); - Name = reader.ReadUnicodeString(); - reader.ReadByte(); - reader.ReadInt(); - AccountId = reader.ReadLong(); - CharacterId = reader.ReadLong(); - Author = reader.ReadUnicodeString(); - CreationTime = reader.ReadLong(); - Url = reader.ReadUnicodeString(); - reader.ReadByte(); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public sealed class UgcItemLook : IByteSerializable, IByteDeserializable { + public static readonly UgcItemLook Default = new UgcItemLook(); + + public long Id; + public string FileName; + public string Name; + public long AccountId; + public long CharacterId; + public string Author; + public long CreationTime; + public string Url; + + public UgcItemLook() { + FileName = string.Empty; + Name = string.Empty; + Author = string.Empty; + Url = string.Empty; + } + + public UgcItemLook Clone() { + return (UgcItemLook) MemberwiseClone(); + } + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(Id); + writer.WriteUnicodeString(Id.ToString()); // UUID (filename) + writer.WriteUnicodeString(Name); // Name (itemname) + writer.WriteByte(1); // Needs to be 1 to show the proper UGC icon? + writer.WriteInt(1); + writer.WriteLong(AccountId); // AccountId + writer.WriteLong(CharacterId); // CharacterId + writer.WriteUnicodeString(Author); // CharacterName + writer.WriteLong(CreationTime); // CreationTime + writer.WriteUnicodeString(Url); // URL (no domain) + writer.WriteByte(); + } + + public void ReadFrom(IByteReader reader) { + Id = reader.ReadLong(); + FileName = reader.ReadUnicodeString(); + Name = reader.ReadUnicodeString(); + reader.ReadByte(); + reader.ReadInt(); + AccountId = reader.ReadLong(); + CharacterId = reader.ReadLong(); + Author = reader.ReadUnicodeString(); + CreationTime = reader.ReadLong(); + Url = reader.ReadUnicodeString(); + reader.ReadByte(); + } +} diff --git a/Maple2.Model/Game/Item/UgcItemMusicScore.cs b/Maple2.Model/Game/Item/UgcItemMusicScore.cs index cf5c8a0e2..35edfe8f3 100644 --- a/Maple2.Model/Game/Item/UgcItemMusicScore.cs +++ b/Maple2.Model/Game/Item/UgcItemMusicScore.cs @@ -1,32 +1,32 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class UgcItemMusicScore : IByteSerializable, IByteDeserializable { - public void WriteTo(IByteWriter writer) { - writer.WriteLong(); - writer.WriteInt(); - writer.WriteInt(); - writer.WriteUnicodeString(); - writer.WriteUnicodeString(); - writer.WriteString(); - writer.WriteInt(); - writer.WriteLong(); - writer.WriteLong(); - writer.WriteUnicodeString(); - } - - public void ReadFrom(IByteReader reader) { - reader.ReadLong(); - reader.ReadInt(); - reader.ReadInt(); - reader.ReadUnicodeString(); - reader.ReadUnicodeString(); - reader.ReadString(); - reader.ReadInt(); - reader.ReadLong(); - reader.ReadLong(); - reader.ReadUnicodeString(); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class UgcItemMusicScore : IByteSerializable, IByteDeserializable { + public void WriteTo(IByteWriter writer) { + writer.WriteLong(); + writer.WriteInt(); + writer.WriteInt(); + writer.WriteUnicodeString(); + writer.WriteUnicodeString(); + writer.WriteString(); + writer.WriteInt(); + writer.WriteLong(); + writer.WriteLong(); + writer.WriteUnicodeString(); + } + + public void ReadFrom(IByteReader reader) { + reader.ReadLong(); + reader.ReadInt(); + reader.ReadInt(); + reader.ReadUnicodeString(); + reader.ReadUnicodeString(); + reader.ReadString(); + reader.ReadInt(); + reader.ReadLong(); + reader.ReadLong(); + reader.ReadUnicodeString(); + } +} diff --git a/Maple2.Model/Game/Mail.cs b/Maple2.Model/Game/Mail.cs index e97dc99a8..0f1cc883b 100644 --- a/Maple2.Model/Game/Mail.cs +++ b/Maple2.Model/Game/Mail.cs @@ -1,170 +1,170 @@ -using System.Text; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game; - -public class Mail : IByteSerializable { - public long Id { get; init; } - public long SenderId { get; init; } - public long ReceiverId { get; init; } // Can be either AccountId or CharacterId - - public MailType Type; - - public string SenderName = string.Empty; - public string Title = string.Empty; - public string Content = string.Empty; - public string WeddingInvite = string.Empty; - public IList<(string Key, string Value)> TitleArgs { get; init; } - public IList<(string Key, string Value)> ContentArgs { get; init; } - - public long Meso; - public long MesoCollectTime; - public long Meret; - public long MeretCollectTime; - public long GameMeret; - public long GameMeretCollectTime; - - public long ReadTime; - public long ExpiryTime; - public long SendTime; - - // More than 1 item may not display properly - public readonly IList Items; - - public Mail() { - TitleArgs = new List<(string Key, string Value)>(); - ContentArgs = new List<(string Key, string Value)>(); - Items = new List(); - ExpiryTime = DateTimeOffset.UtcNow.AddDays(Constant.MailExpiryDays).ToUnixTimeSeconds(); - } - - public void Update(Mail other) { - if (Id != other.Id || SenderId != other.SenderId || ReceiverId != other.ReceiverId) { - throw new ArgumentException("Updating mail with a different mail"); - } - - Meso = other.Meso; - MesoCollectTime = other.MesoCollectTime; - Meret = other.Meret; - MeretCollectTime = other.MeretCollectTime; - GameMeret = other.GameMeret; - GameMeretCollectTime = other.GameMeretCollectTime; - ReadTime = other.ReadTime; - ExpiryTime = other.ExpiryTime; - SendTime = other.SendTime; - } - - public void SetSenderName(StringCode name) { - SenderName = $""""""; - } - - public void SetTitle(StringCode title) { - Title = $""""""; - } - - public void SetContent(StringCode content) { - Content = $""""""; - } - - public void SetWeddingInvite(WeddingHall hall) { - WeddingInvite = $""""""; - } - - public bool MesoCollected() { - return Meso == 0 || MesoCollectTime > 0; - } - - public bool MeretCollected() { - return Meret == 0 || MeretCollectTime > 0; - } - - public bool GameMeretCollected() { - return GameMeret == 0 || GameMeretCollectTime > 0; - } - - public void WriteTo(IByteWriter writer) { - writer.Write(Type); - writer.WriteLong(Id); - writer.WriteLong(SenderId); - writer.WriteUnicodeString(SenderName); - writer.WriteUnicodeString(Title); - writer.WriteUnicodeString(Content); - writer.WriteUnicodeString(FormatArgs(TitleArgs)); - writer.WriteUnicodeString(FormatArgs(ContentArgs)); - - if (Type == MailType.Ad) { // MailAdItem - byte count = 0; - writer.WriteByte(count); - for (byte i = 0; i < count; i++) { - writer.WriteInt(); - writer.WriteInt(); - writer.WriteInt(); - writer.WriteInt(); - writer.WriteLong(); - writer.WriteLong(); - writer.WriteLong(); - } - - writer.WriteUnicodeString(); - writer.WriteLong(); - writer.WriteByte(); - } else { - writer.WriteByte((byte) Items.Count); - byte index = 0; - foreach (Item item in Items) { - writer.WriteInt(item.Id); - writer.WriteLong(item.Uid); - writer.WriteByte(index); - writer.WriteInt(item.Rarity); - writer.WriteInt(item.Amount); - // Item Collect Time, this is unused because we directly set owner after collection. - writer.WriteLong(); - writer.WriteInt(); - writer.WriteLong(); - writer.WriteClass(item); - - index++; - } - } - - writer.WriteLong(Meso); - writer.WriteLong(MesoCollectTime); - writer.WriteLong(Meret); - writer.WriteLong(MeretCollectTime); - writer.WriteLong(GameMeret); - writer.WriteLong(GameMeretCollectTime); - - writer.WriteByte(); - // sub_45E8C0 - // count2 = add_byte("Count") - // for j in range(count2): - // add_byte("Unknown") - // add_byte("Unknown") - // add_long("Unknown") - // add_long("Unknown") - - writer.WriteLong(ReadTime); - writer.WriteLong(ExpiryTime); - writer.WriteLong(SendTime); - writer.WriteUnicodeString(WeddingInvite); - } - - private static string FormatArgs(ICollection<(string Key, string Value)> args) { - if (args.Count == 0) { - return string.Empty; - } - - var result = new StringBuilder(); - result.Append(""); - foreach ((string key, string value) in args) { - result.Append($""" 0 ? key : "key")}="{value}" />"""); - } - result.Append(""); - - return result.ToString(); - } -} +using System.Text; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game; + +public class Mail : IByteSerializable { + public long Id { get; init; } + public long SenderId { get; init; } + public long ReceiverId { get; init; } // Can be either AccountId or CharacterId + + public MailType Type; + + public string SenderName = string.Empty; + public string Title = string.Empty; + public string Content = string.Empty; + public string WeddingInvite = string.Empty; + public IList<(string Key, string Value)> TitleArgs { get; init; } + public IList<(string Key, string Value)> ContentArgs { get; init; } + + public long Meso; + public long MesoCollectTime; + public long Meret; + public long MeretCollectTime; + public long GameMeret; + public long GameMeretCollectTime; + + public long ReadTime; + public long ExpiryTime; + public long SendTime; + + // More than 1 item may not display properly + public readonly IList Items; + + public Mail() { + TitleArgs = new List<(string Key, string Value)>(); + ContentArgs = new List<(string Key, string Value)>(); + Items = new List(); + ExpiryTime = DateTimeOffset.UtcNow.AddDays(Constant.MailExpiryDays).ToUnixTimeSeconds(); + } + + public void Update(Mail other) { + if (Id != other.Id || SenderId != other.SenderId || ReceiverId != other.ReceiverId) { + throw new ArgumentException("Updating mail with a different mail"); + } + + Meso = other.Meso; + MesoCollectTime = other.MesoCollectTime; + Meret = other.Meret; + MeretCollectTime = other.MeretCollectTime; + GameMeret = other.GameMeret; + GameMeretCollectTime = other.GameMeretCollectTime; + ReadTime = other.ReadTime; + ExpiryTime = other.ExpiryTime; + SendTime = other.SendTime; + } + + public void SetSenderName(StringCode name) { + SenderName = $""""""; + } + + public void SetTitle(StringCode title) { + Title = $""""""; + } + + public void SetContent(StringCode content) { + Content = $""""""; + } + + public void SetWeddingInvite(WeddingHall hall) { + WeddingInvite = $""""""; + } + + public bool MesoCollected() { + return Meso == 0 || MesoCollectTime > 0; + } + + public bool MeretCollected() { + return Meret == 0 || MeretCollectTime > 0; + } + + public bool GameMeretCollected() { + return GameMeret == 0 || GameMeretCollectTime > 0; + } + + public void WriteTo(IByteWriter writer) { + writer.Write(Type); + writer.WriteLong(Id); + writer.WriteLong(SenderId); + writer.WriteUnicodeString(SenderName); + writer.WriteUnicodeString(Title); + writer.WriteUnicodeString(Content); + writer.WriteUnicodeString(FormatArgs(TitleArgs)); + writer.WriteUnicodeString(FormatArgs(ContentArgs)); + + if (Type == MailType.Ad) { // MailAdItem + byte count = 0; + writer.WriteByte(count); + for (byte i = 0; i < count; i++) { + writer.WriteInt(); + writer.WriteInt(); + writer.WriteInt(); + writer.WriteInt(); + writer.WriteLong(); + writer.WriteLong(); + writer.WriteLong(); + } + + writer.WriteUnicodeString(); + writer.WriteLong(); + writer.WriteByte(); + } else { + writer.WriteByte((byte) Items.Count); + byte index = 0; + foreach (Item item in Items) { + writer.WriteInt(item.Id); + writer.WriteLong(item.Uid); + writer.WriteByte(index); + writer.WriteInt(item.Rarity); + writer.WriteInt(item.Amount); + // Item Collect Time, this is unused because we directly set owner after collection. + writer.WriteLong(); + writer.WriteInt(); + writer.WriteLong(); + writer.WriteClass(item); + + index++; + } + } + + writer.WriteLong(Meso); + writer.WriteLong(MesoCollectTime); + writer.WriteLong(Meret); + writer.WriteLong(MeretCollectTime); + writer.WriteLong(GameMeret); + writer.WriteLong(GameMeretCollectTime); + + writer.WriteByte(); + // sub_45E8C0 + // count2 = add_byte("Count") + // for j in range(count2): + // add_byte("Unknown") + // add_byte("Unknown") + // add_long("Unknown") + // add_long("Unknown") + + writer.WriteLong(ReadTime); + writer.WriteLong(ExpiryTime); + writer.WriteLong(SendTime); + writer.WriteUnicodeString(WeddingInvite); + } + + private static string FormatArgs(ICollection<(string Key, string Value)> args) { + if (args.Count == 0) { + return string.Empty; + } + + var result = new StringBuilder(); + result.Append(""); + foreach ((string key, string value) in args) { + result.Append($""" 0 ? key : "key")}="{value}" />"""); + } + result.Append(""); + + return result.ToString(); + } +} diff --git a/Maple2.Model/Game/Market/BlackMarketListing.cs b/Maple2.Model/Game/Market/BlackMarketListing.cs index be528dcbb..83790108a 100644 --- a/Maple2.Model/Game/Market/BlackMarketListing.cs +++ b/Maple2.Model/Game/Market/BlackMarketListing.cs @@ -1,37 +1,37 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game; - -public class BlackMarketListing : IByteSerializable { - public long Id { get; init; } - public Item Item { get; init; } - public long CreationTime { get; init; } - public long ExpiryTime { get; init; } - public long Price { get; init; } - public int Quantity { get; set; } - public long AccountId { get; init; } - public long CharacterId { get; init; } - public long Deposit { get; init; } - - public BlackMarketListing(Item item) { - Item = item; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(Id); - writer.WriteLong(CreationTime); - writer.WriteLong(CreationTime); - writer.WriteLong(ExpiryTime); - writer.WriteInt(Item.Amount); - writer.WriteInt(); - writer.WriteLong(Price); - writer.WriteBool(false); - writer.WriteLong(Item.Uid); - writer.WriteInt(Item.Id); - writer.WriteByte((byte) Item.Rarity); - writer.WriteLong(AccountId); - writer.WriteClass(Item); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game; + +public class BlackMarketListing : IByteSerializable { + public long Id { get; init; } + public Item Item { get; init; } + public long CreationTime { get; init; } + public long ExpiryTime { get; init; } + public long Price { get; init; } + public int Quantity { get; set; } + public long AccountId { get; init; } + public long CharacterId { get; init; } + public long Deposit { get; init; } + + public BlackMarketListing(Item item) { + Item = item; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(Id); + writer.WriteLong(CreationTime); + writer.WriteLong(CreationTime); + writer.WriteLong(ExpiryTime); + writer.WriteInt(Item.Amount); + writer.WriteInt(); + writer.WriteLong(Price); + writer.WriteBool(false); + writer.WriteLong(Item.Uid); + writer.WriteInt(Item.Id); + writer.WriteByte((byte) Item.Rarity); + writer.WriteLong(AccountId); + writer.WriteClass(Item); + } +} diff --git a/Maple2.Model/Game/Market/MarketItem.cs b/Maple2.Model/Game/Market/MarketItem.cs index fb0ad67df..c067d9520 100644 --- a/Maple2.Model/Game/Market/MarketItem.cs +++ b/Maple2.Model/Game/Market/MarketItem.cs @@ -1,20 +1,20 @@ -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public abstract class MarketItem : IByteSerializable { - public readonly ItemMetadata ItemMetadata; - protected string Name => ItemMetadata.Name ?? string.Empty; - public long Price { get; set; } - public int SalesCount { get; set; } - public int TabId { get; init; } - public long CreationTime { get; init; } - - public MarketItem(ItemMetadata itemMetadata) { - ItemMetadata = itemMetadata; - } - - public virtual void WriteTo(IByteWriter writer) { } -} +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public abstract class MarketItem : IByteSerializable { + public readonly ItemMetadata ItemMetadata; + protected string Name => ItemMetadata.Name ?? string.Empty; + public long Price { get; set; } + public int SalesCount { get; set; } + public int TabId { get; init; } + public long CreationTime { get; init; } + + public MarketItem(ItemMetadata itemMetadata) { + ItemMetadata = itemMetadata; + } + + public virtual void WriteTo(IByteWriter writer) { } +} diff --git a/Maple2.Model/Game/Market/MeretMarketSearch.cs b/Maple2.Model/Game/Market/MeretMarketSearch.cs index 3ba1f5a19..c37cc183f 100644 --- a/Maple2.Model/Game/Market/MeretMarketSearch.cs +++ b/Maple2.Model/Game/Market/MeretMarketSearch.cs @@ -1,28 +1,28 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class MeretMarketSearch : IByteDeserializable { - public int TabId { get; private set; } - public GenderFilterFlag Gender { get; private set; } - public JobFilterFlag Job { get; private set; } - public MeretMarketSort SortBy { get; private set; } - public string SearchString { get; private set; } = ""; - public int StartPage { get; private set; } - public byte ItemsPerPage { get; set; } - - public void ReadFrom(IByteReader packet) { - TabId = packet.ReadInt(); - Gender = packet.Read(); - Job = packet.Read(); - SortBy = packet.Read(); - SearchString = packet.ReadUnicodeString(); - StartPage = packet.ReadInt(); // 1 - packet.ReadInt(); // 1 - packet.ReadByte(); // 1 on premium, 0 on design menu - packet.ReadByte(); - ItemsPerPage = packet.ReadByte(); - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class MeretMarketSearch : IByteDeserializable { + public int TabId { get; private set; } + public GenderFilterFlag Gender { get; private set; } + public JobFilterFlag Job { get; private set; } + public MeretMarketSort SortBy { get; private set; } + public string SearchString { get; private set; } = ""; + public int StartPage { get; private set; } + public byte ItemsPerPage { get; set; } + + public void ReadFrom(IByteReader packet) { + TabId = packet.ReadInt(); + Gender = packet.Read(); + Job = packet.Read(); + SortBy = packet.Read(); + SearchString = packet.ReadUnicodeString(); + StartPage = packet.ReadInt(); // 1 + packet.ReadInt(); // 1 + packet.ReadByte(); // 1 on premium, 0 on design menu + packet.ReadByte(); + ItemsPerPage = packet.ReadByte(); + } +} diff --git a/Maple2.Model/Game/Market/MesoListing.cs b/Maple2.Model/Game/Market/MesoListing.cs index 835a70705..9bb5c9e8f 100644 --- a/Maple2.Model/Game/Market/MesoListing.cs +++ b/Maple2.Model/Game/Market/MesoListing.cs @@ -1,29 +1,29 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class MesoListing : IByteSerializable { - public long Id { get; init; } - public long AccountId { get; init; } - public long CharacterId { get; init; } - - public long CreationTime; - public long ExpiryTime; - public long Price; - public long Amount; - - public MesoListing(TimeSpan duration = default) { - if (duration != default) { - ExpiryTime = (DateTimeOffset.UtcNow + duration).ToUnixTimeSeconds(); - } - } - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(Id); - writer.WriteLong(Amount); - writer.WriteLong(Price); - writer.WriteLong(CreationTime); - writer.WriteLong(ExpiryTime); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class MesoListing : IByteSerializable { + public long Id { get; init; } + public long AccountId { get; init; } + public long CharacterId { get; init; } + + public long CreationTime; + public long ExpiryTime; + public long Price; + public long Amount; + + public MesoListing(TimeSpan duration = default) { + if (duration != default) { + ExpiryTime = (DateTimeOffset.UtcNow + duration).ToUnixTimeSeconds(); + } + } + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(Id); + writer.WriteLong(Amount); + writer.WriteLong(Price); + writer.WriteLong(CreationTime); + writer.WriteLong(ExpiryTime); + } +} diff --git a/Maple2.Model/Game/Market/PremiumMarketItem.cs b/Maple2.Model/Game/Market/PremiumMarketItem.cs index b92426af1..75770f746 100644 --- a/Maple2.Model/Game/Market/PremiumMarketItem.cs +++ b/Maple2.Model/Game/Market/PremiumMarketItem.cs @@ -1,72 +1,72 @@ -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; - -namespace Maple2.Model.Game; - -public class PremiumMarketItem : MarketItem { - public int Id => Metadata.Id; - public readonly MeretMarketItemMetadata Metadata; - public readonly PremiumMarketPromoData? PromoData; - public IList AdditionalQuantities { get; set; } - - public PremiumMarketItem(MeretMarketItemMetadata marketItemMetadata, ItemMetadata metadata) : base(metadata) { - AdditionalQuantities = new List(); - Metadata = marketItemMetadata; - PromoData = new PremiumMarketPromoData { - Name = Metadata.PromoName, - StartTime = Metadata.PromoStartTime, - EndTime = Metadata.PromoEndTime, - }; - TabId = Metadata.TabId; - Price = Metadata.Price; - } - - public override void WriteTo(IByteWriter writer) { - writer.WriteInt(Id); - writer.WriteByte(); - writer.WriteUnicodeString(Name); - writer.WriteBool(true); - writer.WriteInt(Metadata.ParentId); - writer.WriteInt(); - writer.WriteInt(); - writer.WriteByte(); - writer.Write(Metadata.SaleTag); - writer.Write(Metadata.CurrencyType); - writer.WriteLong(Price); - writer.WriteLong(Metadata.SalePrice); - writer.WriteBool(Metadata.Giftable); - writer.WriteLong(Metadata.SaleStartTime); - writer.WriteLong(Metadata.SaleEndTime); - writer.WriteInt(); // Another flag - writer.WriteInt(); - writer.WriteBool(Metadata.RestockUnavailable); - writer.WriteInt(); - writer.WriteByte(); - writer.WriteShort(Metadata.RequireMinLevel); - writer.WriteShort(Metadata.RequireMaxLevel); - writer.Write(Metadata.JobRequirement); - writer.WriteInt(ItemMetadata.Id); - writer.WriteByte(Metadata.Rarity); - writer.WriteInt(Metadata.Quantity); - writer.WriteInt(Metadata.DurationInDays); - writer.WriteInt(Metadata.BonusQuantity); - writer.WriteInt(TabId); - writer.WriteInt(); - writer.WriteByte(); - writer.Write(Metadata.BannerTag); - writer.WriteString(Metadata.Banner); - writer.WriteString(); - writer.WriteByte(); - writer.WriteByte(); - writer.WriteInt(); - writer.WriteByte(); - writer.WriteInt(Metadata.RequireAchievementId); - writer.WriteInt(Metadata.RequireAchievementRank); - writer.WriteInt(); - writer.WriteBool(Metadata.PcCafe); - writer.WriteByte(); - writer.WriteInt(); - - } -} +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; + +namespace Maple2.Model.Game; + +public class PremiumMarketItem : MarketItem { + public int Id => Metadata.Id; + public readonly MeretMarketItemMetadata Metadata; + public readonly PremiumMarketPromoData? PromoData; + public IList AdditionalQuantities { get; set; } + + public PremiumMarketItem(MeretMarketItemMetadata marketItemMetadata, ItemMetadata metadata) : base(metadata) { + AdditionalQuantities = new List(); + Metadata = marketItemMetadata; + PromoData = new PremiumMarketPromoData { + Name = Metadata.PromoName, + StartTime = Metadata.PromoStartTime, + EndTime = Metadata.PromoEndTime, + }; + TabId = Metadata.TabId; + Price = Metadata.Price; + } + + public override void WriteTo(IByteWriter writer) { + writer.WriteInt(Id); + writer.WriteByte(); + writer.WriteUnicodeString(Name); + writer.WriteBool(true); + writer.WriteInt(Metadata.ParentId); + writer.WriteInt(); + writer.WriteInt(); + writer.WriteByte(); + writer.Write(Metadata.SaleTag); + writer.Write(Metadata.CurrencyType); + writer.WriteLong(Price); + writer.WriteLong(Metadata.SalePrice); + writer.WriteBool(Metadata.Giftable); + writer.WriteLong(Metadata.SaleStartTime); + writer.WriteLong(Metadata.SaleEndTime); + writer.WriteInt(); // Another flag + writer.WriteInt(); + writer.WriteBool(Metadata.RestockUnavailable); + writer.WriteInt(); + writer.WriteByte(); + writer.WriteShort(Metadata.RequireMinLevel); + writer.WriteShort(Metadata.RequireMaxLevel); + writer.Write(Metadata.JobRequirement); + writer.WriteInt(ItemMetadata.Id); + writer.WriteByte(Metadata.Rarity); + writer.WriteInt(Metadata.Quantity); + writer.WriteInt(Metadata.DurationInDays); + writer.WriteInt(Metadata.BonusQuantity); + writer.WriteInt(TabId); + writer.WriteInt(); + writer.WriteByte(); + writer.Write(Metadata.BannerTag); + writer.WriteString(Metadata.Banner); + writer.WriteString(); + writer.WriteByte(); + writer.WriteByte(); + writer.WriteInt(); + writer.WriteByte(); + writer.WriteInt(Metadata.RequireAchievementId); + writer.WriteInt(Metadata.RequireAchievementRank); + writer.WriteInt(); + writer.WriteBool(Metadata.PcCafe); + writer.WriteByte(); + writer.WriteInt(); + + } +} diff --git a/Maple2.Model/Game/Market/PremiumMarketPromoData.cs b/Maple2.Model/Game/Market/PremiumMarketPromoData.cs index 6303bb5b9..31606005e 100644 --- a/Maple2.Model/Game/Market/PremiumMarketPromoData.cs +++ b/Maple2.Model/Game/Market/PremiumMarketPromoData.cs @@ -1,20 +1,20 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class PremiumMarketPromoData : IByteSerializable { - public string Name { get; init; } - public long StartTime { get; init; } - public long EndTime { get; init; } - - public PremiumMarketPromoData() { - Name = string.Empty; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteString(Name); - writer.WriteLong(StartTime); - writer.WriteLong(EndTime); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class PremiumMarketPromoData : IByteSerializable { + public string Name { get; init; } + public long StartTime { get; init; } + public long EndTime { get; init; } + + public PremiumMarketPromoData() { + Name = string.Empty; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteString(Name); + writer.WriteLong(StartTime); + writer.WriteLong(EndTime); + } +} diff --git a/Maple2.Model/Game/Market/SoldUgcMarketItem.cs b/Maple2.Model/Game/Market/SoldUgcMarketItem.cs index ba8a7c726..1b532ca30 100644 --- a/Maple2.Model/Game/Market/SoldUgcMarketItem.cs +++ b/Maple2.Model/Game/Market/SoldUgcMarketItem.cs @@ -1,30 +1,30 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class SoldUgcMarketItem : IByteSerializable { - public long Id { get; init; } - public long Price { get; init; } - public long Profit { get; init; } - public string Name { get; init; } - public long SoldTime { get; init; } - public long AccountId { get; init; } - - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(Id); - writer.WriteLong(); - writer.WriteUnicodeString(Name); - writer.WriteInt(); - writer.WriteInt(); - writer.WriteLong(); - writer.WriteLong(); - writer.WriteUnicodeString(); - writer.WriteUnicodeString(); - writer.WriteInt(); - writer.WriteLong(Price); - writer.WriteLong(SoldTime); - writer.WriteLong(Profit); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class SoldUgcMarketItem : IByteSerializable { + public long Id { get; init; } + public long Price { get; init; } + public long Profit { get; init; } + public string Name { get; init; } + public long SoldTime { get; init; } + public long AccountId { get; init; } + + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(Id); + writer.WriteLong(); + writer.WriteUnicodeString(Name); + writer.WriteInt(); + writer.WriteInt(); + writer.WriteLong(); + writer.WriteLong(); + writer.WriteUnicodeString(); + writer.WriteUnicodeString(); + writer.WriteInt(); + writer.WriteLong(Price); + writer.WriteLong(SoldTime); + writer.WriteLong(Profit); + } +} diff --git a/Maple2.Model/Game/Market/UgcMarketItem.cs b/Maple2.Model/Game/Market/UgcMarketItem.cs index ef1f0fd1c..b38680f37 100644 --- a/Maple2.Model/Game/Market/UgcMarketItem.cs +++ b/Maple2.Model/Game/Market/UgcMarketItem.cs @@ -1,58 +1,58 @@ -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game; - -public class UgcMarketItem(ItemMetadata metadata) : MarketItem(metadata) { - public long Id { get; init; } - public required UgcMarketListingStatus Status { get; set; } - public required long ListingEndTime { get; set; } - public required long PromotionEndTime { get; set; } - public required long SellerAccountId { get; init; } - public required long SellerCharacterId { get; init; } - public required string SellerCharacterName { get; init; } - public required string Description { get; set; } - public required string[] Tags { get; set; } = []; - public required UgcItemLook Look { get; init; } - public required ItemBlueprint Blueprint { get; init; } - public UgcItemMusicScore MusicScore = new(); // TODO: Implement UgcItemMusicScore - public UgcMarketHomeCategory Category = UgcMarketHomeCategory.None; - - public override void WriteTo(IByteWriter writer) { - writer.WriteInt(); - writer.WriteLong(Id); - writer.WriteInt(); - writer.WriteLong(Id); - writer.Write(Status); - writer.WriteInt(ItemMetadata.Id); - writer.WriteInt(TabId); - writer.Write(ItemMetadata.Limit.Gender.FilterFlag()); - writer.WriteInt(); - writer.WriteLong(Price); - writer.WriteInt(); - writer.WriteInt(); - writer.WriteInt(SalesCount); - writer.WriteInt(); - writer.WriteLong(CreationTime); - writer.WriteLong(CreationTime); - writer.WriteLong(ListingEndTime); - writer.WriteInt(); - writer.WriteLong(PromotionEndTime); - writer.WriteLong(); - writer.WriteLong(CreationTime); - writer.WriteInt(); - writer.WriteLong(SellerAccountId); - writer.WriteLong(SellerCharacterId); - writer.WriteUnicodeString(); - writer.WriteUnicodeString(SellerCharacterName); - writer.WriteUnicodeString(string.Join(",", string.Join(",", Tags) + ", " + Look.Name)); - writer.WriteUnicodeString(Description); - writer.WriteUnicodeString(SellerCharacterName); - writer.WriteClass(Look); - writer.WriteClass(MusicScore); - writer.WriteClass(Blueprint); - writer.Write(Category); - } -} +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game; + +public class UgcMarketItem(ItemMetadata metadata) : MarketItem(metadata) { + public long Id { get; init; } + public required UgcMarketListingStatus Status { get; set; } + public required long ListingEndTime { get; set; } + public required long PromotionEndTime { get; set; } + public required long SellerAccountId { get; init; } + public required long SellerCharacterId { get; init; } + public required string SellerCharacterName { get; init; } + public required string Description { get; set; } + public required string[] Tags { get; set; } = []; + public required UgcItemLook Look { get; init; } + public required ItemBlueprint Blueprint { get; init; } + public UgcItemMusicScore MusicScore = new(); // TODO: Implement UgcItemMusicScore + public UgcMarketHomeCategory Category = UgcMarketHomeCategory.None; + + public override void WriteTo(IByteWriter writer) { + writer.WriteInt(); + writer.WriteLong(Id); + writer.WriteInt(); + writer.WriteLong(Id); + writer.Write(Status); + writer.WriteInt(ItemMetadata.Id); + writer.WriteInt(TabId); + writer.Write(ItemMetadata.Limit.Gender.FilterFlag()); + writer.WriteInt(); + writer.WriteLong(Price); + writer.WriteInt(); + writer.WriteInt(); + writer.WriteInt(SalesCount); + writer.WriteInt(); + writer.WriteLong(CreationTime); + writer.WriteLong(CreationTime); + writer.WriteLong(ListingEndTime); + writer.WriteInt(); + writer.WriteLong(PromotionEndTime); + writer.WriteLong(); + writer.WriteLong(CreationTime); + writer.WriteInt(); + writer.WriteLong(SellerAccountId); + writer.WriteLong(SellerCharacterId); + writer.WriteUnicodeString(); + writer.WriteUnicodeString(SellerCharacterName); + writer.WriteUnicodeString(string.Join(",", string.Join(",", Tags) + ", " + Look.Name)); + writer.WriteUnicodeString(Description); + writer.WriteUnicodeString(SellerCharacterName); + writer.WriteClass(Look); + writer.WriteClass(MusicScore); + writer.WriteClass(Blueprint); + writer.Write(Category); + } +} diff --git a/Maple2.Model/Game/Medal.cs b/Maple2.Model/Game/Medal.cs index 8452a7eb3..936ee2313 100644 --- a/Maple2.Model/Game/Medal.cs +++ b/Maple2.Model/Game/Medal.cs @@ -1,21 +1,21 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class Medal : IByteSerializable { - public readonly int Id; - public readonly MedalType Type; - public short Slot = -1; - public long ExpiryTime; - - public Medal(int id, MedalType type) { - Id = id; - Type = type; - } - public void WriteTo(IByteWriter writer) { - writer.WriteInt(Id); - writer.WriteLong(ExpiryTime); - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class Medal : IByteSerializable { + public readonly int Id; + public readonly MedalType Type; + public short Slot = -1; + public long ExpiryTime; + + public Medal(int id, MedalType type) { + Id = id; + Type = type; + } + public void WriteTo(IByteWriter writer) { + writer.WriteInt(Id); + writer.WriteLong(ExpiryTime); + } +} diff --git a/Maple2.Model/Game/Npc/Npc.cs b/Maple2.Model/Game/Npc/Npc.cs index d135d541b..53a37113a 100644 --- a/Maple2.Model/Game/Npc/Npc.cs +++ b/Maple2.Model/Game/Npc/Npc.cs @@ -1,17 +1,17 @@ -using Maple2.Model.Metadata; - -namespace Maple2.Model.Game; - -public class Npc { - public readonly NpcMetadata Metadata; - public readonly IReadOnlyDictionary Animations; - - public int Id => Metadata.Id; - - public bool IsBoss => Metadata.Basic.Friendly == 0 && Metadata.Basic.Class >= 3; - - public Npc(NpcMetadata metadata, AnimationMetadata? animation) { - Metadata = metadata; - Animations = animation?.Sequences ?? new Dictionary(); - } -} +using Maple2.Model.Metadata; + +namespace Maple2.Model.Game; + +public class Npc { + public readonly NpcMetadata Metadata; + public readonly IReadOnlyDictionary Animations; + + public int Id => Metadata.Id; + + public bool IsBoss => Metadata.Basic.Friendly == 0 && Metadata.Basic.Class >= 3; + + public Npc(NpcMetadata metadata, AnimationMetadata? animation) { + Metadata = metadata; + Animations = animation?.Sequences ?? new Dictionary(); + } +} diff --git a/Maple2.Model/Game/Npc/NpcDialogue.cs b/Maple2.Model/Game/Npc/NpcDialogue.cs index 2241b4f2e..d55d98f47 100644 --- a/Maple2.Model/Game/Npc/NpcDialogue.cs +++ b/Maple2.Model/Game/Npc/NpcDialogue.cs @@ -1,17 +1,17 @@ -using System.Runtime.InteropServices; -using Maple2.Model.Enum; - -namespace Maple2.Model.Game; - -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 12)] -public readonly struct NpcDialogue { - public readonly int Id; // ScriptId - public readonly int Index; // Index - public readonly NpcTalkButton Button; - - public NpcDialogue(int id, int index, NpcTalkButton button) { - Id = id; - Index = index; - Button = button; - } -} +using System.Runtime.InteropServices; +using Maple2.Model.Enum; + +namespace Maple2.Model.Game; + +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 12)] +public readonly struct NpcDialogue { + public readonly int Id; // ScriptId + public readonly int Index; // Index + public readonly NpcTalkButton Button; + + public NpcDialogue(int id, int index, NpcTalkButton button) { + Id = id; + Index = index; + Button = button; + } +} diff --git a/Maple2.Model/Game/Npc/NpcTalkScript.cs b/Maple2.Model/Game/Npc/NpcTalkScript.cs index 027675c6a..2b11c7d4f 100644 --- a/Maple2.Model/Game/Npc/NpcTalkScript.cs +++ b/Maple2.Model/Game/Npc/NpcTalkScript.cs @@ -1,5 +1,5 @@ -namespace Maple2.Model.Game; - -public class NpcTalkScript { - -} +namespace Maple2.Model.Game; + +public class NpcTalkScript { + +} diff --git a/Maple2.Model/Game/Party/Party.cs b/Maple2.Model/Game/Party/Party.cs index e074fd8ff..634915ce5 100644 --- a/Maple2.Model/Game/Party/Party.cs +++ b/Maple2.Model/Game/Party/Party.cs @@ -1,62 +1,62 @@ -using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game.Party; - -public class Party : IByteSerializable { - private int capacity = Constant.PartyMaxCapacity; - public int Capacity { - get => capacity; - set { - capacity = Math.Clamp(value, Constant.PartyMinCapacity, Constant.PartyMaxCapacity); - } - } - - public required int Id { get; init; } - public required long LeaderAccountId; - public required long LeaderCharacterId; - public required string LeaderName; - public long CreationTime; - public bool DungeonSet; - public int DungeonId = 0; - public int DungeonLobbyRoomId = 0; - public readonly ConcurrentDictionary Members; - public PartyVote? Vote; - public PartySearch? Search; - public long LastVoteTime = 0; - - [SetsRequiredMembers] - public Party(int id, long leaderAccountId, long leaderCharacterId, string leaderName) { - Id = id; - LeaderAccountId = leaderAccountId; - LeaderCharacterId = leaderCharacterId; - LeaderName = leaderName; - CreationTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); - - Members = new ConcurrentDictionary(); - } - - [SetsRequiredMembers] - public Party(int id, PartyMember leader) : this(id, leader.AccountId, leader.CharacterId, leader.Name) { } - - public void WriteTo(IByteWriter writer) { - writer.WriteBool(true); // joining from offline? - writer.WriteInt(Id); - writer.WriteLong(LeaderCharacterId); - - writer.WriteByte((byte) Members.Count); - foreach (PartyMember member in Members.Values) { - writer.WriteBool(!member.Info.Online); - writer.WriteClass(member); - member.WriteDungeonEligibility(writer); - } - - writer.WriteBool(DungeonSet); - writer.WriteInt(DungeonId); - writer.WriteBool(false); // unk bool - } -} +using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game.Party; + +public class Party : IByteSerializable { + private int capacity = Constant.PartyMaxCapacity; + public int Capacity { + get => capacity; + set { + capacity = Math.Clamp(value, Constant.PartyMinCapacity, Constant.PartyMaxCapacity); + } + } + + public required int Id { get; init; } + public required long LeaderAccountId; + public required long LeaderCharacterId; + public required string LeaderName; + public long CreationTime; + public bool DungeonSet; + public int DungeonId = 0; + public int DungeonLobbyRoomId = 0; + public readonly ConcurrentDictionary Members; + public PartyVote? Vote; + public PartySearch? Search; + public long LastVoteTime = 0; + + [SetsRequiredMembers] + public Party(int id, long leaderAccountId, long leaderCharacterId, string leaderName) { + Id = id; + LeaderAccountId = leaderAccountId; + LeaderCharacterId = leaderCharacterId; + LeaderName = leaderName; + CreationTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + + Members = new ConcurrentDictionary(); + } + + [SetsRequiredMembers] + public Party(int id, PartyMember leader) : this(id, leader.AccountId, leader.CharacterId, leader.Name) { } + + public void WriteTo(IByteWriter writer) { + writer.WriteBool(true); // joining from offline? + writer.WriteInt(Id); + writer.WriteLong(LeaderCharacterId); + + writer.WriteByte((byte) Members.Count); + foreach (PartyMember member in Members.Values) { + writer.WriteBool(!member.Info.Online); + writer.WriteClass(member); + member.WriteDungeonEligibility(writer); + } + + writer.WriteBool(DungeonSet); + writer.WriteInt(DungeonId); + writer.WriteBool(false); // unk bool + } +} diff --git a/Maple2.Model/Game/Party/PartyMember.cs b/Maple2.Model/Game/Party/PartyMember.cs index 6d9c8beb5..a9bff4ff3 100644 --- a/Maple2.Model/Game/Party/PartyMember.cs +++ b/Maple2.Model/Game/Party/PartyMember.cs @@ -1,36 +1,36 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game.Party; - -public class PartyMember : IByteSerializable, IDisposable { - public long PartyId { get; init; } - public required PlayerInfo Info; - public long JoinTime; - public long AccountId => Info.AccountId; - public long CharacterId => Info.CharacterId; - public string Name => Info.Name; - public byte ReadyState = 0; - - public CancellationTokenSource? TokenSource; - - public void WriteTo(IByteWriter writer) { - writer.WriteClass(Info); - } - - public void WriteDungeonEligibility(IByteWriter writer) { - writer.WriteInt(Info.DungeonEnterLimits.Count); - foreach ((int dungeonId, DungeonEnterLimit limit) in Info.DungeonEnterLimits) { - writer.WriteInt(dungeonId); - writer.Write(limit); - } - } - - public void Dispose() { - TokenSource?.Cancel(); - TokenSource?.Dispose(); - TokenSource = null; - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game.Party; + +public class PartyMember : IByteSerializable, IDisposable { + public long PartyId { get; init; } + public required PlayerInfo Info; + public long JoinTime; + public long AccountId => Info.AccountId; + public long CharacterId => Info.CharacterId; + public string Name => Info.Name; + public byte ReadyState = 0; + + public CancellationTokenSource? TokenSource; + + public void WriteTo(IByteWriter writer) { + writer.WriteClass(Info); + } + + public void WriteDungeonEligibility(IByteWriter writer) { + writer.WriteInt(Info.DungeonEnterLimits.Count); + foreach ((int dungeonId, DungeonEnterLimit limit) in Info.DungeonEnterLimits) { + writer.WriteInt(dungeonId); + writer.Write(limit); + } + } + + public void Dispose() { + TokenSource?.Cancel(); + TokenSource?.Dispose(); + TokenSource = null; + } +} diff --git a/Maple2.Model/Game/Party/PartySearch.cs b/Maple2.Model/Game/Party/PartySearch.cs index 4d1f32bfa..f90bcea9b 100644 --- a/Maple2.Model/Game/Party/PartySearch.cs +++ b/Maple2.Model/Game/Party/PartySearch.cs @@ -1,39 +1,39 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game.Party; - -public class PartySearch : IByteSerializable { - public long Id { get; init; } - public int PartyId { get; init; } - public string Name { get; init; } - public int Size { get; init; } - public long CreationTime; - public bool NoApproval; - public int MemberCount; - public long LeaderAccountId; - public long LeaderCharacterId; - public string LeaderName { get; set; } - - public PartySearch(long id, string name, int size) { - Id = id; - Name = name; - Size = size; - CreationTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); - } - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(Id); - writer.WriteInt(PartyId); - writer.WriteInt(); // Unknown - writer.WriteInt(); // Unknown - writer.WriteUnicodeString(Name); - writer.WriteBool(NoApproval); - writer.WriteInt(MemberCount); - writer.WriteInt(Size); - writer.WriteLong(LeaderAccountId); - writer.WriteLong(LeaderCharacterId); - writer.WriteUnicodeString(LeaderName); - writer.WriteLong(CreationTime); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game.Party; + +public class PartySearch : IByteSerializable { + public long Id { get; init; } + public int PartyId { get; init; } + public string Name { get; init; } + public int Size { get; init; } + public long CreationTime; + public bool NoApproval; + public int MemberCount; + public long LeaderAccountId; + public long LeaderCharacterId; + public string LeaderName { get; set; } + + public PartySearch(long id, string name, int size) { + Id = id; + Name = name; + Size = size; + CreationTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + } + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(Id); + writer.WriteInt(PartyId); + writer.WriteInt(); // Unknown + writer.WriteInt(); // Unknown + writer.WriteUnicodeString(Name); + writer.WriteBool(NoApproval); + writer.WriteInt(MemberCount); + writer.WriteInt(Size); + writer.WriteLong(LeaderAccountId); + writer.WriteLong(LeaderCharacterId); + writer.WriteUnicodeString(LeaderName); + writer.WriteLong(CreationTime); + } +} diff --git a/Maple2.Model/Game/Party/PartyVote.cs b/Maple2.Model/Game/Party/PartyVote.cs index 8957fa83d..3b8a94b5a 100644 --- a/Maple2.Model/Game/Party/PartyVote.cs +++ b/Maple2.Model/Game/Party/PartyVote.cs @@ -1,55 +1,55 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game.Party; - -public class PartyVote : IByteSerializable { - public readonly PartyVoteType Type; - public readonly ICollection Voters; - public readonly ICollection Approvals; - public readonly ICollection Disapprovals; - public readonly long InitiatorId; - public PartyMember? TargetMember; - public long VoteTime { get; init; } - public readonly byte VotesNeeded; - - public PartyVote(PartyVoteType type, ICollection voters, long requestorId) { - Type = type; - Voters = voters; - InitiatorId = requestorId; - Approvals = new List { requestorId }; - Disapprovals = new List(); - if (type == PartyVoteType.Kick) { - VotesNeeded = (byte) Math.Ceiling(Voters.Count / 2.0); - } - } - - public void WriteTo(IByteWriter writer) { - writer.Write(Type); - writer.WriteInt(); // Counter - writer.WriteLong(DateTimeOffset.UtcNow.ToUnixTimeSeconds()); // TODO: This is wrong. Will not display a proper time on vote kicking. - - writer.WriteInt(Voters.Count); - foreach (long characterId in Voters) { - writer.WriteLong(characterId); - } - - writer.WriteInt(Approvals.Count); - foreach (long characterId in Approvals) { - writer.WriteLong(characterId); - } - - writer.WriteInt(Disapprovals.Count); - foreach (long characterId in Disapprovals) { - writer.WriteLong(characterId); - } - - if (Type == PartyVoteType.Kick) { - writer.WriteLong(InitiatorId); - writer.WriteLong(TargetMember!.CharacterId); - writer.WriteUnicodeString(TargetMember.Name); - writer.WriteByte(VotesNeeded); - } - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game.Party; + +public class PartyVote : IByteSerializable { + public readonly PartyVoteType Type; + public readonly ICollection Voters; + public readonly ICollection Approvals; + public readonly ICollection Disapprovals; + public readonly long InitiatorId; + public PartyMember? TargetMember; + public long VoteTime { get; init; } + public readonly byte VotesNeeded; + + public PartyVote(PartyVoteType type, ICollection voters, long requestorId) { + Type = type; + Voters = voters; + InitiatorId = requestorId; + Approvals = new List { requestorId }; + Disapprovals = new List(); + if (type == PartyVoteType.Kick) { + VotesNeeded = (byte) Math.Ceiling(Voters.Count / 2.0); + } + } + + public void WriteTo(IByteWriter writer) { + writer.Write(Type); + writer.WriteInt(); // Counter + writer.WriteLong(DateTimeOffset.UtcNow.ToUnixTimeSeconds()); // TODO: This is wrong. Will not display a proper time on vote kicking. + + writer.WriteInt(Voters.Count); + foreach (long characterId in Voters) { + writer.WriteLong(characterId); + } + + writer.WriteInt(Approvals.Count); + foreach (long characterId in Approvals) { + writer.WriteLong(characterId); + } + + writer.WriteInt(Disapprovals.Count); + foreach (long characterId in Disapprovals) { + writer.WriteLong(characterId); + } + + if (Type == PartyVoteType.Kick) { + writer.WriteLong(InitiatorId); + writer.WriteLong(TargetMember!.CharacterId); + writer.WriteUnicodeString(TargetMember.Name); + writer.WriteByte(VotesNeeded); + } + } +} diff --git a/Maple2.Model/Game/Quest/PrestigeMission.cs b/Maple2.Model/Game/Quest/PrestigeMission.cs index 1a7b3871b..6b3945ab6 100644 --- a/Maple2.Model/Game/Quest/PrestigeMission.cs +++ b/Maple2.Model/Game/Quest/PrestigeMission.cs @@ -1,20 +1,20 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class PrestigeMission : IByteSerializable { - public long Id { get; init; } - public long GainedLevels; - public bool Awarded; - - public PrestigeMission(long id) { - Id = id; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(Id); - writer.WriteLong(GainedLevels); - writer.WriteBool(Awarded); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class PrestigeMission : IByteSerializable { + public long Id { get; init; } + public long GainedLevels; + public bool Awarded; + + public PrestigeMission(long id) { + Id = id; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(Id); + writer.WriteLong(GainedLevels); + writer.WriteBool(Awarded); + } +} diff --git a/Maple2.Model/Game/Quest/Quest.cs b/Maple2.Model/Game/Quest/Quest.cs index 47775a663..db52897a1 100644 --- a/Maple2.Model/Game/Quest/Quest.cs +++ b/Maple2.Model/Game/Quest/Quest.cs @@ -1,46 +1,46 @@ -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class Quest : IByteSerializable { - public int Id => Metadata.Id; - public readonly QuestMetadata Metadata; - - public QuestState State; - public int CompletionCount; - public long StartTime; - public long EndTime; - public bool Track; - public SortedDictionary Conditions; - - public Quest(QuestMetadata metadata) { - Metadata = metadata; - Conditions = new SortedDictionary(); - } - - public void WriteTo(IByteWriter writer) { - writer.WriteInt(Id); - writer.Write(State); - writer.WriteInt(CompletionCount); - writer.WriteLong(StartTime); - writer.WriteLong(EndTime); - writer.WriteBool(Track); - - writer.WriteInt(Conditions.Count); - foreach (Condition condition in Conditions.Values) { - writer.WriteInt(condition.Counter); - } - } - - public class Condition { - public readonly ConditionMetadata Metadata; - public int Counter; - - public Condition(ConditionMetadata metadata) { - Metadata = metadata; - } - } -} +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class Quest : IByteSerializable { + public int Id => Metadata.Id; + public readonly QuestMetadata Metadata; + + public QuestState State; + public int CompletionCount; + public long StartTime; + public long EndTime; + public bool Track; + public SortedDictionary Conditions; + + public Quest(QuestMetadata metadata) { + Metadata = metadata; + Conditions = new SortedDictionary(); + } + + public void WriteTo(IByteWriter writer) { + writer.WriteInt(Id); + writer.Write(State); + writer.WriteInt(CompletionCount); + writer.WriteLong(StartTime); + writer.WriteLong(EndTime); + writer.WriteBool(Track); + + writer.WriteInt(Conditions.Count); + foreach (Condition condition in Conditions.Values) { + writer.WriteInt(condition.Counter); + } + } + + public class Condition { + public readonly ConditionMetadata Metadata; + public int Counter; + + public Condition(ConditionMetadata metadata) { + Metadata = metadata; + } + } +} diff --git a/Maple2.Model/Game/RewardItem.cs b/Maple2.Model/Game/RewardItem.cs index a52b6366d..e856b26b8 100644 --- a/Maple2.Model/Game/RewardItem.cs +++ b/Maple2.Model/Game/RewardItem.cs @@ -1,47 +1,47 @@ -using System.Runtime.InteropServices; -using System.Text.Json.Serialization; - -namespace Maple2.Model.Game; - -[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 14)] -public readonly record struct RewardItem { - public int ItemId { get; } - public short Rarity { get; } - public int Amount { get; } - public bool Unknown1 { get; } - public bool Unknown2 { get; } - public bool Unknown3 { get; } - public bool Unknown4 { get; } - - [JsonConstructor] - public RewardItem(int itemId, short rarity, int amount) { - ItemId = itemId; - Rarity = rarity; - Amount = amount; - } - - public static implicit operator RewardItem(Item item) { - return new RewardItem(item.Id, (short) item.Rarity, item.Amount); - } -} - -public readonly struct RewardRecord { - public ICollection Items { get; } = []; - public long Exp { get; } - public long PrestigeExp { get; } - public long Meso { get; } - - public RewardRecord(List items, long exp, long prestigeExp, long meso) { - Items = items; - Exp = exp; - PrestigeExp = prestigeExp; - Meso = meso; - } - - public RewardRecord(List items, long exp, long prestigeExp, long meso) { - Items = items.Select(item => (RewardItem) item).ToList(); - Exp = exp; - PrestigeExp = prestigeExp; - Meso = meso; - } -} +using System.Runtime.InteropServices; +using System.Text.Json.Serialization; + +namespace Maple2.Model.Game; + +[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 14)] +public readonly record struct RewardItem { + public int ItemId { get; } + public short Rarity { get; } + public int Amount { get; } + public bool Unknown1 { get; } + public bool Unknown2 { get; } + public bool Unknown3 { get; } + public bool Unknown4 { get; } + + [JsonConstructor] + public RewardItem(int itemId, short rarity, int amount) { + ItemId = itemId; + Rarity = rarity; + Amount = amount; + } + + public static implicit operator RewardItem(Item item) { + return new RewardItem(item.Id, (short) item.Rarity, item.Amount); + } +} + +public readonly struct RewardRecord { + public ICollection Items { get; } = []; + public long Exp { get; } + public long PrestigeExp { get; } + public long Meso { get; } + + public RewardRecord(List items, long exp, long prestigeExp, long meso) { + Items = items; + Exp = exp; + PrestigeExp = prestigeExp; + Meso = meso; + } + + public RewardRecord(List items, long exp, long prestigeExp, long meso) { + Items = items.Select(item => (RewardItem) item).ToList(); + Exp = exp; + PrestigeExp = prestigeExp; + Meso = meso; + } +} diff --git a/Maple2.Model/Game/Ride/Ride.cs b/Maple2.Model/Game/Ride/Ride.cs index 554a7341f..10cfa91b3 100644 --- a/Maple2.Model/Game/Ride/Ride.cs +++ b/Maple2.Model/Game/Ride/Ride.cs @@ -1,17 +1,17 @@ -using Maple2.Model.Metadata; - -namespace Maple2.Model.Game; - -public class Ride { - public readonly int OwnerId; // ObjectId of owner. - public readonly RideMetadata Metadata; - public readonly RideOnAction Action; - public readonly int[] Passengers; - - public Ride(int ownerId, RideMetadata metadata, RideOnAction action) { - OwnerId = ownerId; - Metadata = metadata; - Action = action; - Passengers = new int[metadata.Basic.Passengers]; - } -} +using Maple2.Model.Metadata; + +namespace Maple2.Model.Game; + +public class Ride { + public readonly int OwnerId; // ObjectId of owner. + public readonly RideMetadata Metadata; + public readonly RideOnAction Action; + public readonly int[] Passengers; + + public Ride(int ownerId, RideMetadata metadata, RideOnAction action) { + OwnerId = ownerId; + Metadata = metadata; + Action = action; + Passengers = new int[metadata.Basic.Passengers]; + } +} diff --git a/Maple2.Model/Game/Ride/RideOffAction.cs b/Maple2.Model/Game/Ride/RideOffAction.cs index 48ec6f27a..ee4c3472f 100644 --- a/Maple2.Model/Game/Ride/RideOffAction.cs +++ b/Maple2.Model/Game/Ride/RideOffAction.cs @@ -1,191 +1,191 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class RideOffAction : IByteSerializable { - private readonly RideOffType type; - private readonly bool forced; - - public RideOffAction(bool forced) : this(RideOffType.Default) { - this.forced = forced; - } - - protected RideOffAction(RideOffType type) { - this.type = type; - } - - public virtual void WriteTo(IByteWriter writer) { - writer.Write(type); - writer.WriteBool(forced); - } -} - -public class RideOffActionUseSkill() : RideOffAction(RideOffType.UseSkill) { - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteInt(); // RideOffAction+28 - writer.WriteByte(); // RideOffAction+32 - writer.WriteInt(); // RideOffAction+36 - } -} - -public class RideOffActionInteract() : RideOffAction(RideOffType.Interact) { - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteInt(); // RideOffAction+28 - writer.WriteUnicodeString(); - } -} - -public class RideOffActionTaxi() : RideOffAction(RideOffType.Taxi) { - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteInt(); // RideOffAction+28 - } -} - -public class RideOffActionCashCall() : RideOffAction(RideOffType.CashCall) { - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteInt(); // RideOffAction+28 - writer.WriteByte(); // RideOffAction+32 - writer.WriteLong(); // RideOffAction+20 - } -} - -public class RideOffActionBeautyShop() : RideOffAction(RideOffType.BeautyShop); - -public class RideOffActionTakeLr() : RideOffAction(RideOffType.TakeLr) { - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteInt(); // RideOffAction+28 - writer.WriteByte(); // RideOffAction+32 - } -} - -public class RideOffActionHold() : RideOffAction(RideOffType.Hold); - -public class RideOffActionRecall() : RideOffAction(RideOffType.Recall) { - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteLong(); // RideOffAction+16 - } -} - -public class RideOffActionSummonPetOn() : RideOffAction(RideOffType.SummonPetOn) { - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteLong(); // RideOffAction+16 - writer.WriteByte(); // RideOffAction+40 - } -} - -public class RideOffActionSummonPetTransfer() : RideOffAction(RideOffType.SummonPetTransfer) { - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteLong(); // RideOffAction+16 - } -} - -public class RideOffActionHomeConvenient() : RideOffAction(RideOffType.HomeConvenient) { - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteByte(); // RideOffAction+28 - } -} - -public class RideOffActionDisableField() : RideOffAction(RideOffType.DisableField); - -public class RideOffActionDead() : RideOffAction(RideOffType.Dead); - -public class RideOffActionAdditionalEffect() : RideOffAction(RideOffType.AdditionalEffect); - -public class RideOffActionRidingUi() : RideOffAction(RideOffType.RidingUi); - -public class RideOffActionHomemade() : RideOffAction(RideOffType.Homemade) { - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteUnicodeString(); - writer.WriteInt(); // RideOffAction+32 - writer.WriteShort(); // RideOffAction+36 - writer.WriteInt(); // RideOffAction+40 - } -} - -public class RideOffActionAutoInteraction() : RideOffAction(RideOffType.AutoInteraction) { - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteInt(); // RideOffAction+28 - writer.WriteByte(); // RideOffAction+32 - writer.WriteShort(); // RideOffAction+36 - } -} - -public class RideOffActionAutoClimb() : RideOffAction(RideOffType.AutoClimb); - -public class RideOffActionCoupleEmotion() : RideOffAction(RideOffType.CoupleEmotion) { - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteInt(); // RideOffAction+28 - writer.WriteLong(); // RideOffAction+16 - } -} - -// This can't be used for RideOff? -// -// public class RideOffActionReact : RideOffAction { -// public RideOffActionReact() : base(RideOffType.React) { } -// -// public override void WriteTo(IByteWriter writer) { -// base.WriteTo(writer); -// writer.WriteInt(); // RideOffAction+28 -// } -// } - -public class RideOffActionUseFunctionItem() : RideOffAction(RideOffType.UseFunctionItem) { - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteLong(); // RideOffAction+16 - writer.WriteUnicodeString(); - } -} - -public class RideOffActionNurturing() : RideOffAction(RideOffType.Nurturing) { - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteUnicodeString(); - writer.WriteInt(); // RideOffAction+32 - } -} - -public class RideOffActionGroggy() : RideOffAction(RideOffType.Groggy); - -public class RideOffActionUnRideSkill() : RideOffAction(RideOffType.UnRideSkill); - -public class RideOffActionUseGlideItem() : RideOffAction(RideOffType.UseGlideItem) { - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteLong(); // RideOffAction+16 - writer.WriteByte(); // RideOffAction+40 - writer.WriteInt(); // RideOffAction+44 - } -} - -public class RideOffActionHideAndSeek() : RideOffAction(RideOffType.HideAndSeek); +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class RideOffAction : IByteSerializable { + private readonly RideOffType type; + private readonly bool forced; + + public RideOffAction(bool forced) : this(RideOffType.Default) { + this.forced = forced; + } + + protected RideOffAction(RideOffType type) { + this.type = type; + } + + public virtual void WriteTo(IByteWriter writer) { + writer.Write(type); + writer.WriteBool(forced); + } +} + +public class RideOffActionUseSkill() : RideOffAction(RideOffType.UseSkill) { + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteInt(); // RideOffAction+28 + writer.WriteByte(); // RideOffAction+32 + writer.WriteInt(); // RideOffAction+36 + } +} + +public class RideOffActionInteract() : RideOffAction(RideOffType.Interact) { + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteInt(); // RideOffAction+28 + writer.WriteUnicodeString(); + } +} + +public class RideOffActionTaxi() : RideOffAction(RideOffType.Taxi) { + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteInt(); // RideOffAction+28 + } +} + +public class RideOffActionCashCall() : RideOffAction(RideOffType.CashCall) { + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteInt(); // RideOffAction+28 + writer.WriteByte(); // RideOffAction+32 + writer.WriteLong(); // RideOffAction+20 + } +} + +public class RideOffActionBeautyShop() : RideOffAction(RideOffType.BeautyShop); + +public class RideOffActionTakeLr() : RideOffAction(RideOffType.TakeLr) { + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteInt(); // RideOffAction+28 + writer.WriteByte(); // RideOffAction+32 + } +} + +public class RideOffActionHold() : RideOffAction(RideOffType.Hold); + +public class RideOffActionRecall() : RideOffAction(RideOffType.Recall) { + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteLong(); // RideOffAction+16 + } +} + +public class RideOffActionSummonPetOn() : RideOffAction(RideOffType.SummonPetOn) { + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteLong(); // RideOffAction+16 + writer.WriteByte(); // RideOffAction+40 + } +} + +public class RideOffActionSummonPetTransfer() : RideOffAction(RideOffType.SummonPetTransfer) { + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteLong(); // RideOffAction+16 + } +} + +public class RideOffActionHomeConvenient() : RideOffAction(RideOffType.HomeConvenient) { + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteByte(); // RideOffAction+28 + } +} + +public class RideOffActionDisableField() : RideOffAction(RideOffType.DisableField); + +public class RideOffActionDead() : RideOffAction(RideOffType.Dead); + +public class RideOffActionAdditionalEffect() : RideOffAction(RideOffType.AdditionalEffect); + +public class RideOffActionRidingUi() : RideOffAction(RideOffType.RidingUi); + +public class RideOffActionHomemade() : RideOffAction(RideOffType.Homemade) { + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteUnicodeString(); + writer.WriteInt(); // RideOffAction+32 + writer.WriteShort(); // RideOffAction+36 + writer.WriteInt(); // RideOffAction+40 + } +} + +public class RideOffActionAutoInteraction() : RideOffAction(RideOffType.AutoInteraction) { + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteInt(); // RideOffAction+28 + writer.WriteByte(); // RideOffAction+32 + writer.WriteShort(); // RideOffAction+36 + } +} + +public class RideOffActionAutoClimb() : RideOffAction(RideOffType.AutoClimb); + +public class RideOffActionCoupleEmotion() : RideOffAction(RideOffType.CoupleEmotion) { + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteInt(); // RideOffAction+28 + writer.WriteLong(); // RideOffAction+16 + } +} + +// This can't be used for RideOff? +// +// public class RideOffActionReact : RideOffAction { +// public RideOffActionReact() : base(RideOffType.React) { } +// +// public override void WriteTo(IByteWriter writer) { +// base.WriteTo(writer); +// writer.WriteInt(); // RideOffAction+28 +// } +// } + +public class RideOffActionUseFunctionItem() : RideOffAction(RideOffType.UseFunctionItem) { + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteLong(); // RideOffAction+16 + writer.WriteUnicodeString(); + } +} + +public class RideOffActionNurturing() : RideOffAction(RideOffType.Nurturing) { + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteUnicodeString(); + writer.WriteInt(); // RideOffAction+32 + } +} + +public class RideOffActionGroggy() : RideOffAction(RideOffType.Groggy); + +public class RideOffActionUnRideSkill() : RideOffAction(RideOffType.UnRideSkill); + +public class RideOffActionUseGlideItem() : RideOffAction(RideOffType.UseGlideItem) { + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteLong(); // RideOffAction+16 + writer.WriteByte(); // RideOffAction+40 + writer.WriteInt(); // RideOffAction+44 + } +} + +public class RideOffActionHideAndSeek() : RideOffAction(RideOffType.HideAndSeek); diff --git a/Maple2.Model/Game/Ride/RideOnAction.cs b/Maple2.Model/Game/Ride/RideOnAction.cs index 37b50d47c..41706a96a 100644 --- a/Maple2.Model/Game/Ride/RideOnAction.cs +++ b/Maple2.Model/Game/Ride/RideOnAction.cs @@ -1,61 +1,61 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game; - -public class RideOnAction : IByteSerializable { - public readonly RideOnType Type; - public readonly int RideId; - public readonly int ObjectId; - - public RideOnAction(int rideId, int objectId) : this(RideOnType.Default, rideId, objectId) { } - - protected RideOnAction(RideOnType type, int rideId, int objectId) { - Type = type; - RideId = rideId; - ObjectId = objectId; - } - - public virtual void WriteTo(IByteWriter writer) { - writer.Write(Type); - writer.WriteInt(RideId); - writer.WriteInt(ObjectId); - } -} - -public class RideOnActionDefault : RideOnAction { - private readonly Item item; - public int ItemId => item.Id; - public long ItemUid => item.Uid; - - public RideOnActionDefault(int rideId, int objectId, Item item) : base(RideOnType.Default, rideId, objectId) { - this.item = item; - } - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteInt(item.Id); - writer.WriteLong(item.Uid); - writer.WriteClass(item.Template ?? UgcItemLook.Default); - } -} - -public class RideOnActionBattle : RideOnAction { - public readonly int SkillId; - public readonly short SkillLevel; - - public RideOnActionBattle(int rideId, int objectId, int skillId, short skillLevel) : base(RideOnType.Battle, rideId, objectId) { - SkillId = skillId; - SkillLevel = skillLevel; - } - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteInt(SkillId); - writer.WriteShort(SkillLevel); - } -} - -public class RideOnActionObject(int rideId, int objectId) : RideOnAction(RideOnType.Object, rideId, objectId); +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game; + +public class RideOnAction : IByteSerializable { + public readonly RideOnType Type; + public readonly int RideId; + public readonly int ObjectId; + + public RideOnAction(int rideId, int objectId) : this(RideOnType.Default, rideId, objectId) { } + + protected RideOnAction(RideOnType type, int rideId, int objectId) { + Type = type; + RideId = rideId; + ObjectId = objectId; + } + + public virtual void WriteTo(IByteWriter writer) { + writer.Write(Type); + writer.WriteInt(RideId); + writer.WriteInt(ObjectId); + } +} + +public class RideOnActionDefault : RideOnAction { + private readonly Item item; + public int ItemId => item.Id; + public long ItemUid => item.Uid; + + public RideOnActionDefault(int rideId, int objectId, Item item) : base(RideOnType.Default, rideId, objectId) { + this.item = item; + } + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteInt(item.Id); + writer.WriteLong(item.Uid); + writer.WriteClass(item.Template ?? UgcItemLook.Default); + } +} + +public class RideOnActionBattle : RideOnAction { + public readonly int SkillId; + public readonly short SkillLevel; + + public RideOnActionBattle(int rideId, int objectId, int skillId, short skillLevel) : base(RideOnType.Battle, rideId, objectId) { + SkillId = skillId; + SkillLevel = skillLevel; + } + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteInt(SkillId); + writer.WriteShort(SkillLevel); + } +} + +public class RideOnActionObject(int rideId, int objectId) : RideOnAction(RideOnType.Object, rideId, objectId); diff --git a/Maple2.Model/Game/Shop/BeautyShop.cs b/Maple2.Model/Game/Shop/BeautyShop.cs index 5672463b0..245df0457 100644 --- a/Maple2.Model/Game/Shop/BeautyShop.cs +++ b/Maple2.Model/Game/Shop/BeautyShop.cs @@ -1,33 +1,33 @@ -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game.Shop; - -public class BeautyShop : IByteSerializable { - public int Id => Metadata.Id; - public readonly BeautyShopMetadata Metadata; - public BeautyShopType Type { get; init; } - public BeautyShopItem[] Items { get; init; } - - public BeautyShop(BeautyShopMetadata metadata, BeautyShopItem[] items) { - Metadata = metadata; - Items = items; - Type = BeautyShopType.Default; - } - - public virtual void WriteTo(IByteWriter writer) { - writer.Write(Type); - writer.WriteInt(Id); - writer.Write(Metadata.Category); - writer.WriteInt(Metadata.CouponId); - writer.WriteByte(); // Related to random hair tickets - writer.WriteInt(Metadata.ReturnCouponId); - writer.WriteInt(Metadata.SubType); - writer.WriteByte(); - writer.WriteClass(Metadata.StyleCostMetadata); - writer.WriteClass(Metadata.ColorCostMetadata); - } -} +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game.Shop; + +public class BeautyShop : IByteSerializable { + public int Id => Metadata.Id; + public readonly BeautyShopMetadata Metadata; + public BeautyShopType Type { get; init; } + public BeautyShopItem[] Items { get; init; } + + public BeautyShop(BeautyShopMetadata metadata, BeautyShopItem[] items) { + Metadata = metadata; + Items = items; + Type = BeautyShopType.Default; + } + + public virtual void WriteTo(IByteWriter writer) { + writer.Write(Type); + writer.WriteInt(Id); + writer.Write(Metadata.Category); + writer.WriteInt(Metadata.CouponId); + writer.WriteByte(); // Related to random hair tickets + writer.WriteInt(Metadata.ReturnCouponId); + writer.WriteInt(Metadata.SubType); + writer.WriteByte(); + writer.WriteClass(Metadata.StyleCostMetadata); + writer.WriteClass(Metadata.ColorCostMetadata); + } +} diff --git a/Maple2.Model/Game/Shop/BeautyShopCost.cs b/Maple2.Model/Game/Shop/BeautyShopCost.cs index c8bb6cd14..a6ea93a8c 100644 --- a/Maple2.Model/Game/Shop/BeautyShopCost.cs +++ b/Maple2.Model/Game/Shop/BeautyShopCost.cs @@ -1,25 +1,25 @@ -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game.Shop; - -public class BeautyShopCost : IByteSerializable { - public BeautyShopCostMetadata Metadata { get; init; } - - public BeautyShopCost(BeautyShopCostMetadata metadata) { - Metadata = metadata; - } - - public static implicit operator BeautyShopCost(BeautyShopCostMetadata other) { - return new BeautyShopCost(other); - } - - public void WriteTo(IByteWriter writer) { - writer.Write(Metadata.CurrencyType); - writer.WriteInt(Metadata.PaymentItemId); - writer.WriteInt(Metadata.Price); - writer.WriteString(Metadata.Icon); - } -} +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game.Shop; + +public class BeautyShopCost : IByteSerializable { + public BeautyShopCostMetadata Metadata { get; init; } + + public BeautyShopCost(BeautyShopCostMetadata metadata) { + Metadata = metadata; + } + + public static implicit operator BeautyShopCost(BeautyShopCostMetadata other) { + return new BeautyShopCost(other); + } + + public void WriteTo(IByteWriter writer) { + writer.Write(Metadata.CurrencyType); + writer.WriteInt(Metadata.PaymentItemId); + writer.WriteInt(Metadata.Price); + writer.WriteString(Metadata.Icon); + } +} diff --git a/Maple2.Model/Game/Shop/BuyBackItem.cs b/Maple2.Model/Game/Shop/BuyBackItem.cs index 110a600dc..a040a72d1 100644 --- a/Maple2.Model/Game/Shop/BuyBackItem.cs +++ b/Maple2.Model/Game/Shop/BuyBackItem.cs @@ -1,19 +1,19 @@ -using Maple2.Model.Game; -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -public class BuyBackItem : IByteSerializable { - public int Id { get; init; } - public long AddedTime { get; init; } - public long Price { get; init; } - public required Item Item { get; init; } - - public virtual void WriteTo(IByteWriter writer) { - writer.WriteInt(Id); - writer.WriteInt(Item.Id); - writer.WriteByte((byte) Item.Rarity); - writer.WriteLong(Price); - writer.WriteClass(Item); - } -} +using Maple2.Model.Game; +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +public class BuyBackItem : IByteSerializable { + public int Id { get; init; } + public long AddedTime { get; init; } + public long Price { get; init; } + public required Item Item { get; init; } + + public virtual void WriteTo(IByteWriter writer) { + writer.WriteInt(Id); + writer.WriteInt(Item.Id); + writer.WriteByte((byte) Item.Rarity); + writer.WriteLong(Price); + writer.WriteClass(Item); + } +} diff --git a/Maple2.Model/Game/Shop/CharacterShopData.cs b/Maple2.Model/Game/Shop/CharacterShopData.cs index 8962fe8d3..3470259ab 100644 --- a/Maple2.Model/Game/Shop/CharacterShopData.cs +++ b/Maple2.Model/Game/Shop/CharacterShopData.cs @@ -1,10 +1,10 @@ -using Maple2.Model.Enum; - -namespace Maple2.Model.Game.Shop; - -public class CharacterShopData { - public required int ShopId { get; init; } - public long RestockTime { get; set; } - public int RestockCount { get; set; } - public ResetType Interval { get; init; } -} +using Maple2.Model.Enum; + +namespace Maple2.Model.Game.Shop; + +public class CharacterShopData { + public required int ShopId { get; init; } + public long RestockTime { get; set; } + public int RestockCount { get; set; } + public ResetType Interval { get; init; } +} diff --git a/Maple2.Model/Game/Shop/CharacterShopItemData.cs b/Maple2.Model/Game/Shop/CharacterShopItemData.cs index 2e45cc518..57ba9702a 100644 --- a/Maple2.Model/Game/Shop/CharacterShopItemData.cs +++ b/Maple2.Model/Game/Shop/CharacterShopItemData.cs @@ -1,8 +1,8 @@ -namespace Maple2.Model.Game.Shop; - -public class CharacterShopItemData { - public int ShopId { get; init; } - public int ShopItemId { get; init; } - public int StockPurchased { get; set; } - public Item Item { get; set; } -} +namespace Maple2.Model.Game.Shop; + +public class CharacterShopItemData { + public int ShopId { get; init; } + public int ShopItemId { get; init; } + public int StockPurchased { get; set; } + public Item Item { get; set; } +} diff --git a/Maple2.Model/Game/Shop/RestrictedBuyData.cs b/Maple2.Model/Game/Shop/RestrictedBuyData.cs index 6690f24b4..3b7643333 100644 --- a/Maple2.Model/Game/Shop/RestrictedBuyData.cs +++ b/Maple2.Model/Game/Shop/RestrictedBuyData.cs @@ -1,60 +1,60 @@ -using System.Runtime.InteropServices; -using System.Text.Json.Serialization; -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game.Shop; - -public class RestrictedBuyData : IByteSerializable { - public long StartTime { get; init; } - public long EndTime { get; init; } - public IList TimeRanges { get; init; } - public IList Days { get; init; } - - public RestrictedBuyData() { - TimeRanges = new List(); - Days = new List(); - } - - public RestrictedBuyData Clone() { - return new RestrictedBuyData() { - StartTime = StartTime, - EndTime = EndTime, - TimeRanges = TimeRanges.Select(time => time.Clone()).ToList(), - Days = Days.Select(day => day).ToList(), - }; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteBool(StartTime > 0 && EndTime > 0); - writer.WriteLong(StartTime); - writer.WriteLong(EndTime); - writer.WriteShort((short) TimeRanges.Count); - - foreach (BuyTimeOfDay time in TimeRanges) { - writer.Write(time); - } - - writer.WriteByte((byte) Days.Count); - foreach (ShopBuyDay day in Days) { - writer.Write(day); - } - } -} - -[StructLayout(LayoutKind.Sequential, Size = 8)] -public readonly struct BuyTimeOfDay { - public int StartTimeOfDay { get; } // time begin in seconds. ex 1200 = 12:20 AM - public int EndTimeOfDay { get; } // time end in seconds. ex 10600 = 2:56 AM - - [JsonConstructor] - public BuyTimeOfDay(int startTime, int endTime) { - StartTimeOfDay = startTime; - EndTimeOfDay = endTime; - } - - public BuyTimeOfDay Clone() { - return new BuyTimeOfDay(StartTimeOfDay, EndTimeOfDay); - } -} +using System.Runtime.InteropServices; +using System.Text.Json.Serialization; +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game.Shop; + +public class RestrictedBuyData : IByteSerializable { + public long StartTime { get; init; } + public long EndTime { get; init; } + public IList TimeRanges { get; init; } + public IList Days { get; init; } + + public RestrictedBuyData() { + TimeRanges = new List(); + Days = new List(); + } + + public RestrictedBuyData Clone() { + return new RestrictedBuyData() { + StartTime = StartTime, + EndTime = EndTime, + TimeRanges = TimeRanges.Select(time => time.Clone()).ToList(), + Days = Days.Select(day => day).ToList(), + }; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteBool(StartTime > 0 && EndTime > 0); + writer.WriteLong(StartTime); + writer.WriteLong(EndTime); + writer.WriteShort((short) TimeRanges.Count); + + foreach (BuyTimeOfDay time in TimeRanges) { + writer.Write(time); + } + + writer.WriteByte((byte) Days.Count); + foreach (ShopBuyDay day in Days) { + writer.Write(day); + } + } +} + +[StructLayout(LayoutKind.Sequential, Size = 8)] +public readonly struct BuyTimeOfDay { + public int StartTimeOfDay { get; } // time begin in seconds. ex 1200 = 12:20 AM + public int EndTimeOfDay { get; } // time end in seconds. ex 10600 = 2:56 AM + + [JsonConstructor] + public BuyTimeOfDay(int startTime, int endTime) { + StartTimeOfDay = startTime; + EndTimeOfDay = endTime; + } + + public BuyTimeOfDay Clone() { + return new BuyTimeOfDay(StartTimeOfDay, EndTimeOfDay); + } +} diff --git a/Maple2.Model/Game/Shop/Shop.cs b/Maple2.Model/Game/Shop/Shop.cs index 077a354fe..296d4c5ba 100644 --- a/Maple2.Model/Game/Shop/Shop.cs +++ b/Maple2.Model/Game/Shop/Shop.cs @@ -1,50 +1,50 @@ -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game.Shop; - -public class Shop : IByteSerializable { - public int Id => Metadata.Id; - public readonly ShopMetadata Metadata; - public ShopRestockData RestockData => Metadata.RestockData; - public long RestockTime; - public int RestockCount; - public SortedDictionary Items; - - public Shop(ShopMetadata metadata) { - Metadata = metadata; - RestockTime = metadata.RestockTime; - Items = new SortedDictionary(); - } - - public virtual void WriteTo(IByteWriter writer) { - writer.WriteInt(Id); - writer.WriteLong(RestockTime); - writer.WriteInt(); - writer.WriteShort((short) Items.Count); - writer.WriteInt(Metadata.CategoryId); - writer.WriteBool(Metadata.OpenWallet); - writer.WriteBool(Metadata.IsOnlySell); - writer.WriteBool(Metadata.EnableReset); - writer.WriteBool(Metadata.DisableDisplayOrderSort); - writer.Write(Metadata.FrameType); - writer.WriteBool(Metadata.DisplayOnlyUsable); - writer.WriteBool(Metadata.HideStats); - writer.WriteBool(false); - writer.WriteBool(Metadata.DisplayNew); - writer.WriteString(Metadata.Name); - if (Metadata.EnableReset) { - writer.Write(RestockData.CurrencyType); - writer.Write(RestockData.ExcessCurrencyType); - writer.WriteInt(); - writer.WriteInt(RestockData.Price); - writer.WriteBool(RestockData.EnablePriceMultiplier); - writer.WriteInt(RestockCount); - writer.Write(RestockData.ResetType); - writer.WriteBool(RestockData.DisableInstantRestock); - writer.WriteBool(RestockData.AccountWide); - } - } -} +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game.Shop; + +public class Shop : IByteSerializable { + public int Id => Metadata.Id; + public readonly ShopMetadata Metadata; + public ShopRestockData RestockData => Metadata.RestockData; + public long RestockTime; + public int RestockCount; + public SortedDictionary Items; + + public Shop(ShopMetadata metadata) { + Metadata = metadata; + RestockTime = metadata.RestockTime; + Items = new SortedDictionary(); + } + + public virtual void WriteTo(IByteWriter writer) { + writer.WriteInt(Id); + writer.WriteLong(RestockTime); + writer.WriteInt(); + writer.WriteShort((short) Items.Count); + writer.WriteInt(Metadata.CategoryId); + writer.WriteBool(Metadata.OpenWallet); + writer.WriteBool(Metadata.IsOnlySell); + writer.WriteBool(Metadata.EnableReset); + writer.WriteBool(Metadata.DisableDisplayOrderSort); + writer.Write(Metadata.FrameType); + writer.WriteBool(Metadata.DisplayOnlyUsable); + writer.WriteBool(Metadata.HideStats); + writer.WriteBool(false); + writer.WriteBool(Metadata.DisplayNew); + writer.WriteString(Metadata.Name); + if (Metadata.EnableReset) { + writer.Write(RestockData.CurrencyType); + writer.Write(RestockData.ExcessCurrencyType); + writer.WriteInt(); + writer.WriteInt(RestockData.Price); + writer.WriteBool(RestockData.EnablePriceMultiplier); + writer.WriteInt(RestockCount); + writer.Write(RestockData.ResetType); + writer.WriteBool(RestockData.DisableInstantRestock); + writer.WriteBool(RestockData.AccountWide); + } + } +} diff --git a/Maple2.Model/Game/Shop/ShopCost.cs b/Maple2.Model/Game/Shop/ShopCost.cs index 778d8f196..dd744a7d5 100644 --- a/Maple2.Model/Game/Shop/ShopCost.cs +++ b/Maple2.Model/Game/Shop/ShopCost.cs @@ -1,32 +1,32 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game.Shop; - -public class ShopCost : IByteSerializable { - public static readonly ShopCost Zero = new() { - Type = ShopCurrencyType.Meso, - ItemId = 0, - Amount = 0, - SaleAmount = 0, - }; - - public ShopCurrencyType Type { get; init; } - public int ItemId { get; init; } - public int Amount { get; init; } - public int SaleAmount { get; init; } - - public ShopCost() { - ItemId = 0; - SaleAmount = 0; - } - - public void WriteTo(IByteWriter writer) { - writer.Write(Type); - writer.WriteInt(ItemId); - writer.WriteInt(); - writer.WriteInt(Amount); - writer.WriteInt(SaleAmount); - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game.Shop; + +public class ShopCost : IByteSerializable { + public static readonly ShopCost Zero = new() { + Type = ShopCurrencyType.Meso, + ItemId = 0, + Amount = 0, + SaleAmount = 0, + }; + + public ShopCurrencyType Type { get; init; } + public int ItemId { get; init; } + public int Amount { get; init; } + public int SaleAmount { get; init; } + + public ShopCost() { + ItemId = 0; + SaleAmount = 0; + } + + public void WriteTo(IByteWriter writer) { + writer.Write(Type); + writer.WriteInt(ItemId); + writer.WriteInt(); + writer.WriteInt(Amount); + writer.WriteInt(SaleAmount); + } +} diff --git a/Maple2.Model/Game/Shop/ShopItem.cs b/Maple2.Model/Game/Shop/ShopItem.cs index dc681b36d..804e8f5fe 100644 --- a/Maple2.Model/Game/Shop/ShopItem.cs +++ b/Maple2.Model/Game/Shop/ShopItem.cs @@ -1,60 +1,60 @@ -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game.Shop; - -public class ShopItem : IByteSerializable { - public readonly ShopItemMetadata Metadata; - public int Id => Metadata.Id; - public int StockPurchased { get; set; } - public int StockCount { get; set; } - public required Item Item { get; init; } - - public ShopItem(ShopItemMetadata metadata) { - Metadata = metadata; - StockCount = metadata.SellCount; - } - - public ShopItem Clone() { - return new ShopItem(Metadata) { - Item = Item.Clone(), - StockCount = StockCount, - StockPurchased = StockPurchased, - }; - } - - public virtual void WriteTo(IByteWriter writer) { - writer.WriteInt(Id); - writer.WriteInt(Metadata.ItemId); - writer.WriteClass(Metadata.Cost); - writer.WriteByte(Metadata.Rarity); - writer.WriteInt(500); - writer.WriteInt(StockCount); - writer.WriteInt(StockPurchased * Metadata.SellUnit); - writer.WriteInt(Metadata.Requirements.GuildTrophy); - writer.WriteString(Metadata.Category); - writer.WriteInt(Metadata.Requirements.Achievement.Id); - writer.WriteInt(Metadata.Requirements.Achievement.Rank); - writer.WriteByte(Metadata.Requirements.Championship.Rank); - writer.WriteShort(Metadata.Requirements.Championship.JoinCount); - writer.WriteByte((byte) Metadata.Requirements.GuildNpc.Type); - writer.WriteShort(Metadata.Requirements.GuildNpc.Level); - writer.WriteBool(false); - writer.WriteShort(Metadata.SellUnit); - writer.WriteByte(); - writer.Write(Metadata.Label); - writer.WriteString(Metadata.IconTag); - writer.Write(Metadata.Requirements.QuestAlliance.Type); - writer.WriteInt(Metadata.Requirements.QuestAlliance.Grade); - writer.WriteBool(Metadata.WearForPreview); - writer.WriteBool(Metadata.RestrictedBuyData != null); - if (Metadata.RestrictedBuyData != null) { - writer.WriteClass(Metadata.RestrictedBuyData); - } - - writer.WriteClass(Item); - } -} +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game.Shop; + +public class ShopItem : IByteSerializable { + public readonly ShopItemMetadata Metadata; + public int Id => Metadata.Id; + public int StockPurchased { get; set; } + public int StockCount { get; set; } + public required Item Item { get; init; } + + public ShopItem(ShopItemMetadata metadata) { + Metadata = metadata; + StockCount = metadata.SellCount; + } + + public ShopItem Clone() { + return new ShopItem(Metadata) { + Item = Item.Clone(), + StockCount = StockCount, + StockPurchased = StockPurchased, + }; + } + + public virtual void WriteTo(IByteWriter writer) { + writer.WriteInt(Id); + writer.WriteInt(Metadata.ItemId); + writer.WriteClass(Metadata.Cost); + writer.WriteByte(Metadata.Rarity); + writer.WriteInt(500); + writer.WriteInt(StockCount); + writer.WriteInt(StockPurchased * Metadata.SellUnit); + writer.WriteInt(Metadata.Requirements.GuildTrophy); + writer.WriteString(Metadata.Category); + writer.WriteInt(Metadata.Requirements.Achievement.Id); + writer.WriteInt(Metadata.Requirements.Achievement.Rank); + writer.WriteByte(Metadata.Requirements.Championship.Rank); + writer.WriteShort(Metadata.Requirements.Championship.JoinCount); + writer.WriteByte((byte) Metadata.Requirements.GuildNpc.Type); + writer.WriteShort(Metadata.Requirements.GuildNpc.Level); + writer.WriteBool(false); + writer.WriteShort(Metadata.SellUnit); + writer.WriteByte(); + writer.Write(Metadata.Label); + writer.WriteString(Metadata.IconTag); + writer.Write(Metadata.Requirements.QuestAlliance.Type); + writer.WriteInt(Metadata.Requirements.QuestAlliance.Grade); + writer.WriteBool(Metadata.WearForPreview); + writer.WriteBool(Metadata.RestrictedBuyData != null); + if (Metadata.RestrictedBuyData != null) { + writer.WriteClass(Metadata.RestrictedBuyData); + } + + writer.WriteClass(Item); + } +} diff --git a/Maple2.Model/Game/Shop/ShopRestock.cs b/Maple2.Model/Game/Shop/ShopRestock.cs index ec227a3d4..d2396dfda 100644 --- a/Maple2.Model/Game/Shop/ShopRestock.cs +++ b/Maple2.Model/Game/Shop/ShopRestock.cs @@ -1,27 +1,27 @@ -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game.Shop; - -public class ShopRestock : IByteSerializable { - - public ShopRestockData Metadata; - public int RestockCount; - public ShopRestock(ShopRestockData metadata) { - Metadata = metadata; - } - - public void WriteTo(IByteWriter writer) { - writer.Write(Metadata.CurrencyType); - writer.Write(Metadata.ExcessCurrencyType); - writer.WriteInt(); - writer.WriteInt(Metadata.Price); - writer.WriteBool(Metadata.EnablePriceMultiplier); - writer.WriteInt(RestockCount); - writer.Write(Metadata.ResetType); - writer.WriteBool(Metadata.DisableInstantRestock); - writer.WriteBool(Metadata.AccountWide); - } -} +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game.Shop; + +public class ShopRestock : IByteSerializable { + + public ShopRestockData Metadata; + public int RestockCount; + public ShopRestock(ShopRestockData metadata) { + Metadata = metadata; + } + + public void WriteTo(IByteWriter writer) { + writer.Write(Metadata.CurrencyType); + writer.Write(Metadata.ExcessCurrencyType); + writer.WriteInt(); + writer.WriteInt(Metadata.Price); + writer.WriteBool(Metadata.EnablePriceMultiplier); + writer.WriteInt(RestockCount); + writer.Write(Metadata.ResetType); + writer.WriteBool(Metadata.DisableInstantRestock); + writer.WriteBool(Metadata.AccountWide); + } +} diff --git a/Maple2.Model/Game/Sync/StateSync.cs b/Maple2.Model/Game/Sync/StateSync.cs index c935bbb9a..1861f7704 100644 --- a/Maple2.Model/Game/Sync/StateSync.cs +++ b/Maple2.Model/Game/Sync/StateSync.cs @@ -1,193 +1,193 @@ -using System.Numerics; -using System.Text; -using Maple2.Model.Common; -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class StateSync : IByteSerializable, IByteDeserializable { - [Flags] - public enum Flag : byte { - None = 0, - Flag1 = 1, - Flag2 = 2, - Flag3 = 4, - Animation = 8, - Flag5 = 16, - Flag6 = 32, - } - - public ActorState State; - public ActorSubState SubState; - public Flag Flags; - - public Vector3 Position; - public short Rotation; - public byte Animation; - public float UnknownFloat1; - public float UnknownFloat2; - public Vector3 Speed; - public byte Unknown1; // Always 2... - public short Rotation2; // Rotation * 10 - public short Unknown3; - public int SyncNumber; - - #region Flag1 - public int EmotionId; // Skill ID for emotes + flying mounts (not ground) - public short Flag1Unknown2; // Goes to 1 for flying mounts - #endregion - - #region Flag2 - public Vector3 Flag2Unknown1; - public string? Flag2Unknown2; - #endregion - - #region Flag3 - public int Flag3Unknown1; - public string? Flag3Unknown2; - #endregion - - #region AnimationFlag - public string? AnimationName; - #endregion - - #region Flag5 - public int Flag5Unknown1; - public string? Flag5Unknown2; - #endregion - - #region Flag6 - public int Flag6Unknown1; - public int Flag6Unknown2; - public byte Flag6Unknown3; - public Vector3 Flag6Position; - public Vector3 Flag6Rotation; - #endregion - - public virtual void WriteTo(IByteWriter writer) { - writer.Write(State); - writer.Write(SubState); - writer.Write(Flags); - - if (Flags.HasFlag(Flag.Flag1)) { - writer.WriteInt(EmotionId); - writer.WriteShort(Flag1Unknown2); - } - - writer.Write(Position); - writer.WriteShort(Rotation); - writer.WriteByte(Animation); - - if (Animation > 127) { - writer.WriteFloat(UnknownFloat1); - writer.WriteFloat(UnknownFloat2); - } - - writer.Write(Speed); - writer.WriteByte(Unknown1); - writer.WriteShort(Rotation2); - writer.WriteShort(Unknown3); - if (Flags.HasFlag(Flag.Flag2)) { - writer.Write(Flag2Unknown1); - writer.WriteUnicodeString(Flag2Unknown2 ?? ""); - } - if (Flags.HasFlag(Flag.Flag3)) { - writer.WriteInt(Flag3Unknown1); - writer.WriteUnicodeString(Flag3Unknown2 ?? ""); - } - if (Flags.HasFlag(Flag.Animation)) { - writer.WriteUnicodeString(AnimationName ?? ""); - } - if (Flags.HasFlag(Flag.Flag5)) { - writer.WriteInt(Flag5Unknown1); - writer.WriteUnicodeString(Flag5Unknown2 ?? ""); - } - if (Flags.HasFlag(Flag.Flag6)) { - writer.WriteInt(Flag6Unknown1); - writer.WriteInt(Flag6Unknown2); - writer.WriteByte(Flag6Unknown3); - writer.Write(Flag6Position); - writer.Write(Flag6Rotation); - } - - writer.WriteInt(SyncNumber); - } - - public virtual void ReadFrom(IByteReader reader) { - State = reader.Read(); - SubState = reader.Read(); - Flags = reader.Read(); - - if (Flags.HasFlag(Flag.Flag1)) { - EmotionId = reader.ReadInt(); - Flag1Unknown2 = reader.ReadShort(); - } - - Position = reader.Read(); - Rotation = reader.ReadShort(); // CoordS / 10 (Rotation?) - Animation = reader.ReadByte(); - if (Animation > 127) { // if animation < 0 (signed) - UnknownFloat1 = reader.ReadFloat(); - UnknownFloat2 = reader.ReadFloat(); - } - Speed = reader.Read(); // XYZ Speed? - Unknown1 = reader.ReadByte(); - Rotation2 = reader.ReadShort(); // CoordS / 10 - Unknown3 = reader.ReadShort(); // CoordS / 1000 - - if (Flags.HasFlag(Flag.Flag2)) { - Flag2Unknown1 = reader.Read(); - Flag2Unknown2 = reader.ReadUnicodeString(); - } - if (Flags.HasFlag(Flag.Flag3)) { - Flag3Unknown1 = reader.ReadInt(); - Flag3Unknown2 = reader.ReadUnicodeString(); - } - if (Flags.HasFlag(Flag.Animation)) { - AnimationName = reader.ReadUnicodeString(); - } - if (Flags.HasFlag(Flag.Flag5)) { - Flag5Unknown1 = reader.ReadInt(); - Flag5Unknown2 = reader.ReadUnicodeString(); - } - if (Flags.HasFlag(Flag.Flag6)) { - Flag6Unknown1 = reader.ReadInt(); - Flag6Unknown2 = reader.ReadInt(); - Flag6Unknown3 = reader.ReadByte(); - Flag6Position = reader.Read(); - Flag6Rotation = reader.Read(); - } - - SyncNumber = reader.ReadInt(); - } - - public override string ToString() { - var builder = new StringBuilder(); - builder.AppendLine($"State:{State}, SubState:{SubState}, SyncNumber{SyncNumber}"); - builder.AppendLine($" Position:{Position}, Rotation:{Rotation}, Speed:{Speed}"); - builder.AppendLine($" Animation:{Animation} ({UnknownFloat1}, {UnknownFloat2}), Unknown1:{Unknown1}, Rotation2:{Rotation2}, Unknown3:{Unknown3}"); - if (Flags.HasFlag(Flag.Flag1)) { - builder.Append($"Flag1: {EmotionId}, {Flag1Unknown2}"); - } - if (Flags.HasFlag(Flag.Flag2)) { - builder.Append($"Flag2: {Flag2Unknown1}, {Flag2Unknown2}"); - } - if (Flags.HasFlag(Flag.Flag3)) { - builder.Append($"Flag3: {Flag3Unknown1}, {Flag3Unknown2}"); - } - if (Flags.HasFlag(Flag.Animation)) { - builder.Append($"Animation: {AnimationName}"); - } - if (Flags.HasFlag(Flag.Flag5)) { - builder.Append($"Flag5: {Flag5Unknown1}, {Flag5Unknown2}"); - } - if (Flags.HasFlag(Flag.Flag6)) { - builder.Append($"Flag6: {Flag6Unknown1}, {Flag6Unknown2}, {Flag6Unknown3}"); - builder.Append($"- Position:{Flag6Position}, Rotation:{Flag6Rotation}"); - } - - return builder.ToString(); - } -} +using System.Numerics; +using System.Text; +using Maple2.Model.Common; +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class StateSync : IByteSerializable, IByteDeserializable { + [Flags] + public enum Flag : byte { + None = 0, + Flag1 = 1, + Flag2 = 2, + Flag3 = 4, + Animation = 8, + Flag5 = 16, + Flag6 = 32, + } + + public ActorState State; + public ActorSubState SubState; + public Flag Flags; + + public Vector3 Position; + public short Rotation; + public byte Animation; + public float UnknownFloat1; + public float UnknownFloat2; + public Vector3 Speed; + public byte Unknown1; // Always 2... + public short Rotation2; // Rotation * 10 + public short Unknown3; + public int SyncNumber; + + #region Flag1 + public int EmotionId; // Skill ID for emotes + flying mounts (not ground) + public short Flag1Unknown2; // Goes to 1 for flying mounts + #endregion + + #region Flag2 + public Vector3 Flag2Unknown1; + public string? Flag2Unknown2; + #endregion + + #region Flag3 + public int Flag3Unknown1; + public string? Flag3Unknown2; + #endregion + + #region AnimationFlag + public string? AnimationName; + #endregion + + #region Flag5 + public int Flag5Unknown1; + public string? Flag5Unknown2; + #endregion + + #region Flag6 + public int Flag6Unknown1; + public int Flag6Unknown2; + public byte Flag6Unknown3; + public Vector3 Flag6Position; + public Vector3 Flag6Rotation; + #endregion + + public virtual void WriteTo(IByteWriter writer) { + writer.Write(State); + writer.Write(SubState); + writer.Write(Flags); + + if (Flags.HasFlag(Flag.Flag1)) { + writer.WriteInt(EmotionId); + writer.WriteShort(Flag1Unknown2); + } + + writer.Write(Position); + writer.WriteShort(Rotation); + writer.WriteByte(Animation); + + if (Animation > 127) { + writer.WriteFloat(UnknownFloat1); + writer.WriteFloat(UnknownFloat2); + } + + writer.Write(Speed); + writer.WriteByte(Unknown1); + writer.WriteShort(Rotation2); + writer.WriteShort(Unknown3); + if (Flags.HasFlag(Flag.Flag2)) { + writer.Write(Flag2Unknown1); + writer.WriteUnicodeString(Flag2Unknown2 ?? ""); + } + if (Flags.HasFlag(Flag.Flag3)) { + writer.WriteInt(Flag3Unknown1); + writer.WriteUnicodeString(Flag3Unknown2 ?? ""); + } + if (Flags.HasFlag(Flag.Animation)) { + writer.WriteUnicodeString(AnimationName ?? ""); + } + if (Flags.HasFlag(Flag.Flag5)) { + writer.WriteInt(Flag5Unknown1); + writer.WriteUnicodeString(Flag5Unknown2 ?? ""); + } + if (Flags.HasFlag(Flag.Flag6)) { + writer.WriteInt(Flag6Unknown1); + writer.WriteInt(Flag6Unknown2); + writer.WriteByte(Flag6Unknown3); + writer.Write(Flag6Position); + writer.Write(Flag6Rotation); + } + + writer.WriteInt(SyncNumber); + } + + public virtual void ReadFrom(IByteReader reader) { + State = reader.Read(); + SubState = reader.Read(); + Flags = reader.Read(); + + if (Flags.HasFlag(Flag.Flag1)) { + EmotionId = reader.ReadInt(); + Flag1Unknown2 = reader.ReadShort(); + } + + Position = reader.Read(); + Rotation = reader.ReadShort(); // CoordS / 10 (Rotation?) + Animation = reader.ReadByte(); + if (Animation > 127) { // if animation < 0 (signed) + UnknownFloat1 = reader.ReadFloat(); + UnknownFloat2 = reader.ReadFloat(); + } + Speed = reader.Read(); // XYZ Speed? + Unknown1 = reader.ReadByte(); + Rotation2 = reader.ReadShort(); // CoordS / 10 + Unknown3 = reader.ReadShort(); // CoordS / 1000 + + if (Flags.HasFlag(Flag.Flag2)) { + Flag2Unknown1 = reader.Read(); + Flag2Unknown2 = reader.ReadUnicodeString(); + } + if (Flags.HasFlag(Flag.Flag3)) { + Flag3Unknown1 = reader.ReadInt(); + Flag3Unknown2 = reader.ReadUnicodeString(); + } + if (Flags.HasFlag(Flag.Animation)) { + AnimationName = reader.ReadUnicodeString(); + } + if (Flags.HasFlag(Flag.Flag5)) { + Flag5Unknown1 = reader.ReadInt(); + Flag5Unknown2 = reader.ReadUnicodeString(); + } + if (Flags.HasFlag(Flag.Flag6)) { + Flag6Unknown1 = reader.ReadInt(); + Flag6Unknown2 = reader.ReadInt(); + Flag6Unknown3 = reader.ReadByte(); + Flag6Position = reader.Read(); + Flag6Rotation = reader.Read(); + } + + SyncNumber = reader.ReadInt(); + } + + public override string ToString() { + var builder = new StringBuilder(); + builder.AppendLine($"State:{State}, SubState:{SubState}, SyncNumber{SyncNumber}"); + builder.AppendLine($" Position:{Position}, Rotation:{Rotation}, Speed:{Speed}"); + builder.AppendLine($" Animation:{Animation} ({UnknownFloat1}, {UnknownFloat2}), Unknown1:{Unknown1}, Rotation2:{Rotation2}, Unknown3:{Unknown3}"); + if (Flags.HasFlag(Flag.Flag1)) { + builder.Append($"Flag1: {EmotionId}, {Flag1Unknown2}"); + } + if (Flags.HasFlag(Flag.Flag2)) { + builder.Append($"Flag2: {Flag2Unknown1}, {Flag2Unknown2}"); + } + if (Flags.HasFlag(Flag.Flag3)) { + builder.Append($"Flag3: {Flag3Unknown1}, {Flag3Unknown2}"); + } + if (Flags.HasFlag(Flag.Animation)) { + builder.Append($"Animation: {AnimationName}"); + } + if (Flags.HasFlag(Flag.Flag5)) { + builder.Append($"Flag5: {Flag5Unknown1}, {Flag5Unknown2}"); + } + if (Flags.HasFlag(Flag.Flag6)) { + builder.Append($"Flag6: {Flag6Unknown1}, {Flag6Unknown2}, {Flag6Unknown3}"); + builder.Append($"- Position:{Flag6Position}, Rotation:{Flag6Rotation}"); + } + + return builder.ToString(); + } +} diff --git a/Maple2.Model/Game/Sync/StateSyncCoupleDance.cs b/Maple2.Model/Game/Sync/StateSyncCoupleDance.cs index 1cfd0c9b6..3803cfef4 100644 --- a/Maple2.Model/Game/Sync/StateSyncCoupleDance.cs +++ b/Maple2.Model/Game/Sync/StateSyncCoupleDance.cs @@ -1,24 +1,24 @@ -using Maple2.PacketLib.Tools; - -namespace Maple2.Model.Game; - -// gosMicroGameCoupleDance -public sealed class StateSyncCoupleDance : StateSync { - public int UnknownCoupleDanceInt; - public bool UnknownCoupleDanceBool; - public string? UnknownCoupleDanceString; - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteInt(UnknownCoupleDanceInt); - writer.WriteBool(UnknownCoupleDanceBool); - writer.WriteUnicodeString(UnknownCoupleDanceString ?? ""); - } - - public override void ReadFrom(IByteReader reader) { - base.ReadFrom(reader); - UnknownCoupleDanceInt = reader.ReadInt(); - UnknownCoupleDanceBool = reader.ReadBool(); - UnknownCoupleDanceString = reader.ReadUnicodeString(); - } -} +using Maple2.PacketLib.Tools; + +namespace Maple2.Model.Game; + +// gosMicroGameCoupleDance +public sealed class StateSyncCoupleDance : StateSync { + public int UnknownCoupleDanceInt; + public bool UnknownCoupleDanceBool; + public string? UnknownCoupleDanceString; + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteInt(UnknownCoupleDanceInt); + writer.WriteBool(UnknownCoupleDanceBool); + writer.WriteUnicodeString(UnknownCoupleDanceString ?? ""); + } + + public override void ReadFrom(IByteReader reader) { + base.ReadFrom(reader); + UnknownCoupleDanceInt = reader.ReadInt(); + UnknownCoupleDanceBool = reader.ReadBool(); + UnknownCoupleDanceString = reader.ReadUnicodeString(); + } +} diff --git a/Maple2.Model/Game/Sync/StateSyncRps.cs b/Maple2.Model/Game/Sync/StateSyncRps.cs index 790f9e258..ac129c37e 100644 --- a/Maple2.Model/Game/Sync/StateSyncRps.cs +++ b/Maple2.Model/Game/Sync/StateSyncRps.cs @@ -1,27 +1,27 @@ -using Maple2.PacketLib.Tools; - -namespace Maple2.Model.Game; - -// gosMicroGameRps -public class StateSyncRps : StateSync { - public int UnknownRpsInt; - public byte UnknownRpsByte1; - public byte UnknownRpsByte2; - public string? UnknownRpsString; - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteInt(UnknownRpsInt); - writer.WriteByte(UnknownRpsByte1); - writer.WriteByte(UnknownRpsByte2); - writer.WriteUnicodeString(UnknownRpsString ?? ""); - } - - public override void ReadFrom(IByteReader reader) { - base.ReadFrom(reader); - UnknownRpsInt = reader.ReadInt(); - UnknownRpsByte1 = reader.ReadByte(); - UnknownRpsByte2 = reader.ReadByte(); - UnknownRpsString = reader.ReadUnicodeString(); - } -} +using Maple2.PacketLib.Tools; + +namespace Maple2.Model.Game; + +// gosMicroGameRps +public class StateSyncRps : StateSync { + public int UnknownRpsInt; + public byte UnknownRpsByte1; + public byte UnknownRpsByte2; + public string? UnknownRpsString; + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteInt(UnknownRpsInt); + writer.WriteByte(UnknownRpsByte1); + writer.WriteByte(UnknownRpsByte2); + writer.WriteUnicodeString(UnknownRpsString ?? ""); + } + + public override void ReadFrom(IByteReader reader) { + base.ReadFrom(reader); + UnknownRpsInt = reader.ReadInt(); + UnknownRpsByte1 = reader.ReadByte(); + UnknownRpsByte2 = reader.ReadByte(); + UnknownRpsString = reader.ReadUnicodeString(); + } +} diff --git a/Maple2.Model/Game/Sync/StateSyncWeddingEmotion.cs b/Maple2.Model/Game/Sync/StateSyncWeddingEmotion.cs index 018c1e886..c5cc4266c 100644 --- a/Maple2.Model/Game/Sync/StateSyncWeddingEmotion.cs +++ b/Maple2.Model/Game/Sync/StateSyncWeddingEmotion.cs @@ -1,38 +1,38 @@ -using Maple2.PacketLib.Tools; - -namespace Maple2.Model.Game; - -// gosWeddingEmotion -public class StateSyncWeddingEmotion : StateSync { - public bool UnknownWeddingEmotionBool1; - public bool UnknownWeddingEmotionBool2; - public int UnknownWeddingEmotionInt1; - public int UnknownWeddingEmotionInt2; - public bool UnknownWeddingEmotionBool3; - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteBool(UnknownWeddingEmotionBool1); - writer.WriteBool(UnknownWeddingEmotionBool2); - if (UnknownWeddingEmotionBool1) { - writer.WriteInt(UnknownWeddingEmotionInt1); - } - if (UnknownWeddingEmotionBool2) { - writer.WriteInt(UnknownWeddingEmotionInt2); - writer.WriteBool(UnknownWeddingEmotionBool3); - } - } - - public override void ReadFrom(IByteReader reader) { - base.ReadFrom(reader); - UnknownWeddingEmotionBool1 = reader.ReadBool(); - UnknownWeddingEmotionBool2 = reader.ReadBool(); - if (UnknownWeddingEmotionBool1) { - UnknownWeddingEmotionInt1 = reader.ReadInt(); - } - if (UnknownWeddingEmotionBool2) { - UnknownWeddingEmotionInt2 = reader.ReadInt(); - UnknownWeddingEmotionBool3 = reader.ReadBool(); - } - } -} +using Maple2.PacketLib.Tools; + +namespace Maple2.Model.Game; + +// gosWeddingEmotion +public class StateSyncWeddingEmotion : StateSync { + public bool UnknownWeddingEmotionBool1; + public bool UnknownWeddingEmotionBool2; + public int UnknownWeddingEmotionInt1; + public int UnknownWeddingEmotionInt2; + public bool UnknownWeddingEmotionBool3; + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteBool(UnknownWeddingEmotionBool1); + writer.WriteBool(UnknownWeddingEmotionBool2); + if (UnknownWeddingEmotionBool1) { + writer.WriteInt(UnknownWeddingEmotionInt1); + } + if (UnknownWeddingEmotionBool2) { + writer.WriteInt(UnknownWeddingEmotionInt2); + writer.WriteBool(UnknownWeddingEmotionBool3); + } + } + + public override void ReadFrom(IByteReader reader) { + base.ReadFrom(reader); + UnknownWeddingEmotionBool1 = reader.ReadBool(); + UnknownWeddingEmotionBool2 = reader.ReadBool(); + if (UnknownWeddingEmotionBool1) { + UnknownWeddingEmotionInt1 = reader.ReadInt(); + } + if (UnknownWeddingEmotionBool2) { + UnknownWeddingEmotionInt2 = reader.ReadInt(); + UnknownWeddingEmotionBool3 = reader.ReadBool(); + } + } +} diff --git a/Maple2.Model/Game/SystemBanner.cs b/Maple2.Model/Game/SystemBanner.cs index 23175149f..28229fbe3 100644 --- a/Maple2.Model/Game/SystemBanner.cs +++ b/Maple2.Model/Game/SystemBanner.cs @@ -1,33 +1,33 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class SystemBanner : IByteSerializable { - public int Id { get; init; } - public string Name { get; init; } = string.Empty; // name must start with "homeproduct_" for Meret Market banners - public SystemBannerType Type { get; init; } - public SystemBannerFunction Function { get; init; } - public string FunctionParameter { get; init; } = string.Empty; - public string Url { get; init; } = string.Empty; // Meret Market banner resolution: 538x301 - public SystemBannerLanguage Language { get; init; } - public long BeginTime { get; init; } - public long EndTime { get; init; } - - public SystemBanner(int id) { - Id = id; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteInt(Id); - writer.WriteUnicodeString(Name); - writer.WriteUnicodeString(Type.ToString()); - writer.WriteUnicodeString(Function.ToString()); - writer.WriteUnicodeString(FunctionParameter); - writer.WriteUnicodeString(Url); - writer.Write(Language); - writer.WriteLong(BeginTime); - writer.WriteLong(EndTime); - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class SystemBanner : IByteSerializable { + public int Id { get; init; } + public string Name { get; init; } = string.Empty; // name must start with "homeproduct_" for Meret Market banners + public SystemBannerType Type { get; init; } + public SystemBannerFunction Function { get; init; } + public string FunctionParameter { get; init; } = string.Empty; + public string Url { get; init; } = string.Empty; // Meret Market banner resolution: 538x301 + public SystemBannerLanguage Language { get; init; } + public long BeginTime { get; init; } + public long EndTime { get; init; } + + public SystemBanner(int id) { + Id = id; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteInt(Id); + writer.WriteUnicodeString(Name); + writer.WriteUnicodeString(Type.ToString()); + writer.WriteUnicodeString(Function.ToString()); + writer.WriteUnicodeString(FunctionParameter); + writer.WriteUnicodeString(Url); + writer.Write(Language); + writer.WriteLong(BeginTime); + writer.WriteLong(EndTime); + } +} diff --git a/Maple2.Model/Game/TriggerObject.cs b/Maple2.Model/Game/TriggerObject.cs index 74a0923a1..b7b8f44e7 100644 --- a/Maple2.Model/Game/TriggerObject.cs +++ b/Maple2.Model/Game/TriggerObject.cs @@ -1,126 +1,126 @@ -using System.Numerics; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Collision; - -namespace Maple2.Model.Game; - -public interface ITriggerObject : IByteSerializable { - public int Id { get; } - public bool Visible { get; } -} - -public abstract class TriggerObject : ITriggerObject where T : Ms2Trigger { - public readonly T Metadata; - - public int Id => Metadata.TriggerId; - public bool Visible { get; set; } - - public TriggerObject(T metadata) { - Metadata = metadata; - } - - public virtual void WriteTo(IByteWriter writer) { - writer.WriteInt(Id); - writer.WriteBool(Visible); - } -} - -public class TriggerObjectSound(Ms2TriggerSound metadata) : TriggerObject(metadata); - -public class TriggerObjectMesh : TriggerObject { - public bool MinimapVisible { get; init; } - public int Fade { get; set; } - public float Scale { get; set; } - - public TriggerObjectMesh(Ms2TriggerMesh metadata) : base(metadata) { - MinimapVisible = metadata.MinimapInvisible; - Scale = metadata.Scale; - Visible = metadata.Visible; - } - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteBool(MinimapVisible); - writer.WriteInt(Fade); // Fade 10 = 1s? - writer.WriteUnicodeString(); - writer.WriteFloat(Scale); - } -} - -public class TriggerObjectActor(Ms2TriggerActor metadata) : TriggerObject(metadata) { - public string SequenceName { get; set; } = string.Empty; - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteUnicodeString(SequenceName); - } -} - -public class TriggerObjectRope(Ms2TriggerRope metadata) : TriggerObject(metadata) { - public bool Animate { get; set; } - public int Delay { get; set; } - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteBool(Animate); - writer.WriteInt(Delay); - } -} - -public class TriggerObjectLadder(Ms2TriggerLadder metadata) : TriggerObject(metadata) { - public bool Animate { get; set; } - public int Delay { get; set; } - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteBool(Animate); - writer.WriteInt(Delay); - } -} - -public class TriggerObjectEffect(Ms2TriggerEffect metadata) : TriggerObject(metadata) { - public bool UnknownBool { get; set; } - public int UnknownInt { get; set; } - - public override void WriteTo(IByteWriter writer) { - base.WriteTo(writer); - writer.WriteBool(UnknownBool); - writer.WriteInt(UnknownInt); - } -} - -public class TriggerObjectCube(Ms2TriggerCube metadata) : TriggerObject(metadata); - -public class TriggerObjectCamera(Ms2TriggerCamera metadata) : TriggerObject(metadata); - -public class TriggerObjectAgent(Ms2TriggerAgent metadata) : TriggerObject(metadata); - -public class TriggerBox { - // Some extra height to compensate for entity height - private const float EXTRA_HEIGHT = 10f; - - // Extra to check for entity size - private const float EXTRA_WIDTH = 10f; - - public readonly Ms2TriggerBox Metadata; - - public int Id => Metadata.TriggerId; - - private readonly Prism box; - - public TriggerBox(Ms2TriggerBox metadata) { - Metadata = metadata; - - var min = new Vector2(metadata.Position.X - metadata.Dimensions.X / 2 - EXTRA_WIDTH, metadata.Position.Y - metadata.Dimensions.Y / 2 - EXTRA_WIDTH); - var max = new Vector2(metadata.Position.X + metadata.Dimensions.X / 2 + EXTRA_WIDTH, metadata.Position.Y + metadata.Dimensions.Y / 2 + EXTRA_WIDTH); - box = new Prism(new BoundingBox(min, max), metadata.Position.Z - metadata.Dimensions.Z / 2 - EXTRA_HEIGHT, metadata.Dimensions.Z + EXTRA_HEIGHT); - } - - public bool Contains(in Vector3 point) => box.Contains(point); - - public override string ToString() { - return $"Id:{Id}\n- {box}"; - } -} +using System.Numerics; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Collision; + +namespace Maple2.Model.Game; + +public interface ITriggerObject : IByteSerializable { + public int Id { get; } + public bool Visible { get; } +} + +public abstract class TriggerObject : ITriggerObject where T : Ms2Trigger { + public readonly T Metadata; + + public int Id => Metadata.TriggerId; + public bool Visible { get; set; } + + public TriggerObject(T metadata) { + Metadata = metadata; + } + + public virtual void WriteTo(IByteWriter writer) { + writer.WriteInt(Id); + writer.WriteBool(Visible); + } +} + +public class TriggerObjectSound(Ms2TriggerSound metadata) : TriggerObject(metadata); + +public class TriggerObjectMesh : TriggerObject { + public bool MinimapVisible { get; init; } + public int Fade { get; set; } + public float Scale { get; set; } + + public TriggerObjectMesh(Ms2TriggerMesh metadata) : base(metadata) { + MinimapVisible = metadata.MinimapInvisible; + Scale = metadata.Scale; + Visible = metadata.Visible; + } + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteBool(MinimapVisible); + writer.WriteInt(Fade); // Fade 10 = 1s? + writer.WriteUnicodeString(); + writer.WriteFloat(Scale); + } +} + +public class TriggerObjectActor(Ms2TriggerActor metadata) : TriggerObject(metadata) { + public string SequenceName { get; set; } = string.Empty; + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteUnicodeString(SequenceName); + } +} + +public class TriggerObjectRope(Ms2TriggerRope metadata) : TriggerObject(metadata) { + public bool Animate { get; set; } + public int Delay { get; set; } + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteBool(Animate); + writer.WriteInt(Delay); + } +} + +public class TriggerObjectLadder(Ms2TriggerLadder metadata) : TriggerObject(metadata) { + public bool Animate { get; set; } + public int Delay { get; set; } + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteBool(Animate); + writer.WriteInt(Delay); + } +} + +public class TriggerObjectEffect(Ms2TriggerEffect metadata) : TriggerObject(metadata) { + public bool UnknownBool { get; set; } + public int UnknownInt { get; set; } + + public override void WriteTo(IByteWriter writer) { + base.WriteTo(writer); + writer.WriteBool(UnknownBool); + writer.WriteInt(UnknownInt); + } +} + +public class TriggerObjectCube(Ms2TriggerCube metadata) : TriggerObject(metadata); + +public class TriggerObjectCamera(Ms2TriggerCamera metadata) : TriggerObject(metadata); + +public class TriggerObjectAgent(Ms2TriggerAgent metadata) : TriggerObject(metadata); + +public class TriggerBox { + // Some extra height to compensate for entity height + private const float EXTRA_HEIGHT = 10f; + + // Extra to check for entity size + private const float EXTRA_WIDTH = 10f; + + public readonly Ms2TriggerBox Metadata; + + public int Id => Metadata.TriggerId; + + private readonly Prism box; + + public TriggerBox(Ms2TriggerBox metadata) { + Metadata = metadata; + + var min = new Vector2(metadata.Position.X - metadata.Dimensions.X / 2 - EXTRA_WIDTH, metadata.Position.Y - metadata.Dimensions.Y / 2 - EXTRA_WIDTH); + var max = new Vector2(metadata.Position.X + metadata.Dimensions.X / 2 + EXTRA_WIDTH, metadata.Position.Y + metadata.Dimensions.Y / 2 + EXTRA_WIDTH); + box = new Prism(new BoundingBox(min, max), metadata.Position.Z - metadata.Dimensions.Z / 2 - EXTRA_HEIGHT, metadata.Dimensions.Z + EXTRA_HEIGHT); + } + + public bool Contains(in Vector3 point) => box.Contains(point); + + public override string ToString() { + return $"Id:{Id}\n- {box}"; + } +} diff --git a/Maple2.Model/Game/Ugc/UgcBanner.cs b/Maple2.Model/Game/Ugc/UgcBanner.cs index ea6208f7a..510616c6e 100644 --- a/Maple2.Model/Game/Ugc/UgcBanner.cs +++ b/Maple2.Model/Game/Ugc/UgcBanner.cs @@ -1,62 +1,62 @@ -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game.Ugc; - -public class UgcBanner { - public readonly long Id; - public readonly int MapId; - public readonly List Slots; - - public UgcBanner(long id, int mapId, List slots) { - Id = id; - MapId = mapId; - Slots = slots; - } -} - -public class BannerSlot : IByteSerializable { - public long Id; - public readonly int Date; - public readonly int Hour; - public readonly long BannerId; - public bool Active; - public UgcItemLook? Template; - public bool Expired; - - public readonly DateTimeOffset ActivateTime; - - public BannerSlot() { } - - public BannerSlot(long bannerId, int date, int hour) { - BannerId = bannerId; - Date = date; - Hour = hour; - - int year = date / 10000; - int month = date % 10000 / 100; - int day = date % 100; - - ActivateTime = new(year, month, day, hour, 0, 0, TimeSpan.Zero); - } - - public BannerSlot(long id, DateTimeOffset dateInUnixSeconds, long bannerId, UgcItemLook? template) { - Id = id; - ActivateTime = dateInUnixSeconds; - - Date = int.Parse(ActivateTime.ToString("yyyyMMdd")); - Hour = ActivateTime.Hour; - - BannerId = bannerId; - Template = template; - } - - public void WriteTo(IByteWriter pWriter) { - pWriter.WriteLong(Id); - pWriter.WriteInt(Active ? 2 : 1); // idk - pWriter.WriteLong(BannerId); - pWriter.WriteInt(Date); - pWriter.WriteInt(Hour); - pWriter.WriteLong(); - } -} +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game.Ugc; + +public class UgcBanner { + public readonly long Id; + public readonly int MapId; + public readonly List Slots; + + public UgcBanner(long id, int mapId, List slots) { + Id = id; + MapId = mapId; + Slots = slots; + } +} + +public class BannerSlot : IByteSerializable { + public long Id; + public readonly int Date; + public readonly int Hour; + public readonly long BannerId; + public bool Active; + public UgcItemLook? Template; + public bool Expired; + + public readonly DateTimeOffset ActivateTime; + + public BannerSlot() { } + + public BannerSlot(long bannerId, int date, int hour) { + BannerId = bannerId; + Date = date; + Hour = hour; + + int year = date / 10000; + int month = date % 10000 / 100; + int day = date % 100; + + ActivateTime = new(year, month, day, hour, 0, 0, TimeSpan.Zero); + } + + public BannerSlot(long id, DateTimeOffset dateInUnixSeconds, long bannerId, UgcItemLook? template) { + Id = id; + ActivateTime = dateInUnixSeconds; + + Date = int.Parse(ActivateTime.ToString("yyyyMMdd")); + Hour = ActivateTime.Hour; + + BannerId = bannerId; + Template = template; + } + + public void WriteTo(IByteWriter pWriter) { + pWriter.WriteLong(Id); + pWriter.WriteInt(Active ? 2 : 1); // idk + pWriter.WriteLong(BannerId); + pWriter.WriteInt(Date); + pWriter.WriteInt(Hour); + pWriter.WriteLong(); + } +} diff --git a/Maple2.Model/Game/Ugc/UgcBannerReservation.cs b/Maple2.Model/Game/Ugc/UgcBannerReservation.cs index df55a5cdd..844283c8d 100644 --- a/Maple2.Model/Game/Ugc/UgcBannerReservation.cs +++ b/Maple2.Model/Game/Ugc/UgcBannerReservation.cs @@ -1,13 +1,13 @@ -using System.Runtime.InteropServices; - -namespace Maple2.Model.Game; - -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 36)] -public readonly struct UgcBannerReservation { - public readonly long Uid; - public readonly int Unknown1; - public readonly long Id; - public readonly int Date; - public readonly int Hour; - public readonly long Unknown2; -} +using System.Runtime.InteropServices; + +namespace Maple2.Model.Game; + +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 36)] +public readonly struct UgcBannerReservation { + public readonly long Uid; + public readonly int Unknown1; + public readonly long Id; + public readonly int Date; + public readonly int Hour; + public readonly long Unknown2; +} diff --git a/Maple2.Model/Game/Ugc/UgcInfo.cs b/Maple2.Model/Game/Ugc/UgcInfo.cs index d87b8d990..55e75f288 100644 --- a/Maple2.Model/Game/Ugc/UgcInfo.cs +++ b/Maple2.Model/Game/Ugc/UgcInfo.cs @@ -1,14 +1,14 @@ -using System.Runtime.InteropServices; -using Maple2.Model.Enum; - -namespace Maple2.Model.Game; - -[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 23)] -public readonly struct UgcInfo { - public readonly UgcType Type; - private readonly byte Unknown1; - private readonly byte Unknown2; - private readonly int Unknown3; - public readonly long AccountId; - public readonly long CharacterId; -} +using System.Runtime.InteropServices; +using Maple2.Model.Enum; + +namespace Maple2.Model.Game; + +[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 23)] +public readonly struct UgcInfo { + public readonly UgcType Type; + private readonly byte Unknown1; + private readonly byte Unknown2; + private readonly int Unknown3; + public readonly long AccountId; + public readonly long CharacterId; +} diff --git a/Maple2.Model/Game/Ugc/UgcResource.cs b/Maple2.Model/Game/Ugc/UgcResource.cs index 02e1b12ba..2a7bbb720 100644 --- a/Maple2.Model/Game/Ugc/UgcResource.cs +++ b/Maple2.Model/Game/Ugc/UgcResource.cs @@ -1,13 +1,13 @@ -using Maple2.Model.Enum; - -namespace Maple2.Model.Game; - -public class UgcResource { - public long Id { get; init; } - public string Path { get; set; } - public UgcType Type { get; init; } - - public UgcResource() { - Path = string.Empty; - } -} +using Maple2.Model.Enum; + +namespace Maple2.Model.Game; + +public class UgcResource { + public long Id { get; init; } + public string Path { get; set; } + public UgcType Type { get; init; } + + public UgcResource() { + Path = string.Empty; + } +} diff --git a/Maple2.Model/Game/User/Account.cs b/Maple2.Model/Game/User/Account.cs index 8e67cfa67..8382a7837 100644 --- a/Maple2.Model/Game/User/Account.cs +++ b/Maple2.Model/Game/User/Account.cs @@ -1,40 +1,40 @@ -using Maple2.Model.Enum; - -namespace Maple2.Model.Game; - -public class Account { - #region Immutable - public DateTime LastModified { get; init; } - public long Id { get; init; } - - public required string Username { get; init; } - public Guid MachineId { get; set; } - #endregion - - public int MaxCharacters { get; set; } - public int PrestigeLevel { get; set; } - public int PrestigeLevelsGained { get; set; } - public long PrestigeExp { get; set; } - public long PrestigeCurrentExp { get; set; } - public IList PrestigeMissions { get; set; } - public IList PrestigeRewardsClaimed { get; set; } - public long PremiumTime { get; set; } - public IList PremiumRewardsClaimed { get; set; } - public int MesoMarketListed { get; set; } - public int MesoMarketPurchased { get; set; } - - public int SurvivalLevel { get; set; } - public long SurvivalExp { get; set; } - public int SurvivalSilverLevelRewardClaimed { get; set; } - public int SurvivalGoldLevelRewardClaimed { get; set; } - public bool ActiveGoldPass { get; set; } - public bool Online { get; set; } - public AdminPermissions AdminPermissions { get; set; } - - public Account() { - PremiumRewardsClaimed = new List(); - PrestigeMissions = new List(); - PrestigeRewardsClaimed = new List(); - AdminPermissions = AdminPermissions.None; - } -} +using Maple2.Model.Enum; + +namespace Maple2.Model.Game; + +public class Account { + #region Immutable + public DateTime LastModified { get; init; } + public long Id { get; init; } + + public required string Username { get; init; } + public Guid MachineId { get; set; } + #endregion + + public int MaxCharacters { get; set; } + public int PrestigeLevel { get; set; } + public int PrestigeLevelsGained { get; set; } + public long PrestigeExp { get; set; } + public long PrestigeCurrentExp { get; set; } + public IList PrestigeMissions { get; set; } + public IList PrestigeRewardsClaimed { get; set; } + public long PremiumTime { get; set; } + public IList PremiumRewardsClaimed { get; set; } + public int MesoMarketListed { get; set; } + public int MesoMarketPurchased { get; set; } + + public int SurvivalLevel { get; set; } + public long SurvivalExp { get; set; } + public int SurvivalSilverLevelRewardClaimed { get; set; } + public int SurvivalGoldLevelRewardClaimed { get; set; } + public bool ActiveGoldPass { get; set; } + public bool Online { get; set; } + public AdminPermissions AdminPermissions { get; set; } + + public Account() { + PremiumRewardsClaimed = new List(); + PrestigeMissions = new List(); + PrestigeRewardsClaimed = new List(); + AdminPermissions = AdminPermissions.None; + } +} diff --git a/Maple2.Model/Game/User/Achievement.cs b/Maple2.Model/Game/User/Achievement.cs index f891ebe12..385962ed9 100644 --- a/Maple2.Model/Game/User/Achievement.cs +++ b/Maple2.Model/Game/User/Achievement.cs @@ -1,67 +1,67 @@ -using System.Runtime.InteropServices; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 12)] -public struct AchievementInfo { - public int Combat { get; set; } - public int Adventure { get; set; } - public int Lifestyle { get; set; } - - public static AchievementInfo operator +(in AchievementInfo a, in AchievementInfo b) { - return new AchievementInfo { - Combat = a.Combat + b.Combat, - Adventure = a.Adventure + b.Adventure, - Lifestyle = a.Lifestyle + b.Lifestyle, - }; - } - - public static AchievementInfo operator -(in AchievementInfo a, in AchievementInfo b) { - return new AchievementInfo { - Combat = a.Combat - b.Combat, - Adventure = a.Adventure - b.Adventure, - Lifestyle = a.Lifestyle - b.Lifestyle, - }; - } - public int Total => Combat + Adventure + Lifestyle; -} - -public class Achievement : IByteSerializable { - public readonly AchievementMetadata Metadata; - public readonly int Id; - - public bool Completed => Metadata.Grades.Count == Grades.Count; - public AchievementStatus Status => Completed ? AchievementStatus.Completed : AchievementStatus.InProgress; - public int CurrentGrade { get; set; } - public int RewardGrade { get; set; } - public bool Favorite { get; set; } - public long Counter { get; set; } - public AchievementCategory Category { get; init; } - - public IDictionary Grades { get; set; } = new Dictionary(); - - public Achievement(AchievementMetadata metadata) { - Metadata = metadata; - Id = metadata.Id; - Category = metadata.Category; - } - - public void WriteTo(IByteWriter writer) { - writer.Write(Status); - writer.WriteInt(Completed ? 1 : 0); - writer.WriteInt(CurrentGrade); - writer.WriteInt(RewardGrade); - writer.WriteBool(Favorite); - writer.WriteLong(Counter); - writer.WriteInt(Grades.Count); - - foreach ((int grade, long timeAcquired) in Grades.OrderBy(grade => grade.Key).ToList()) { - writer.WriteInt(grade); - writer.WriteLong(timeAcquired); - } - } -} +using System.Runtime.InteropServices; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 12)] +public struct AchievementInfo { + public int Combat { get; set; } + public int Adventure { get; set; } + public int Lifestyle { get; set; } + + public static AchievementInfo operator +(in AchievementInfo a, in AchievementInfo b) { + return new AchievementInfo { + Combat = a.Combat + b.Combat, + Adventure = a.Adventure + b.Adventure, + Lifestyle = a.Lifestyle + b.Lifestyle, + }; + } + + public static AchievementInfo operator -(in AchievementInfo a, in AchievementInfo b) { + return new AchievementInfo { + Combat = a.Combat - b.Combat, + Adventure = a.Adventure - b.Adventure, + Lifestyle = a.Lifestyle - b.Lifestyle, + }; + } + public int Total => Combat + Adventure + Lifestyle; +} + +public class Achievement : IByteSerializable { + public readonly AchievementMetadata Metadata; + public readonly int Id; + + public bool Completed => Metadata.Grades.Count == Grades.Count; + public AchievementStatus Status => Completed ? AchievementStatus.Completed : AchievementStatus.InProgress; + public int CurrentGrade { get; set; } + public int RewardGrade { get; set; } + public bool Favorite { get; set; } + public long Counter { get; set; } + public AchievementCategory Category { get; init; } + + public IDictionary Grades { get; set; } = new Dictionary(); + + public Achievement(AchievementMetadata metadata) { + Metadata = metadata; + Id = metadata.Id; + Category = metadata.Category; + } + + public void WriteTo(IByteWriter writer) { + writer.Write(Status); + writer.WriteInt(Completed ? 1 : 0); + writer.WriteInt(CurrentGrade); + writer.WriteInt(RewardGrade); + writer.WriteBool(Favorite); + writer.WriteLong(Counter); + writer.WriteInt(Grades.Count); + + foreach ((int grade, long timeAcquired) in Grades.OrderBy(grade => grade.Key).ToList()) { + writer.WriteInt(grade); + writer.WriteLong(timeAcquired); + } + } +} diff --git a/Maple2.Model/Game/User/Character.cs b/Maple2.Model/Game/User/Character.cs index b626beff5..020bb6bc4 100644 --- a/Maple2.Model/Game/User/Character.cs +++ b/Maple2.Model/Game/User/Character.cs @@ -1,58 +1,58 @@ -using System.Numerics; -using Maple2.Model.Common; -using Maple2.Model.Enum; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class Character { - #region Immutable - public long CreationTime { get; init; } - public DateTime LastModified { get; init; } - - public long Id { get; init; } - public long AccountId { get; init; } - #endregion - - public long DeleteTime; - public long LastOnlineTime; - - public required string Name; - public Gender Gender; - public int MapId; - public Job Job; - - public SkinColor SkinColor; - public short Level = 1; - public long Exp; - public long RestExp; - - public int Title; - public short Insignia; - - public int RoomId; - public int InstanceMapId; - public short Channel; - public short ReturnChannel; - - public long StorageCooldown; - public long DoctorCooldown; - - public LimitedStack ReturnMaps = new LimitedStack(3); - public Vector3 ReturnPosition; - public string Picture = string.Empty; - public string Motto = string.Empty; - public string GuildName = string.Empty; - public long GuildId; - public int PartyId; - public List ClubIds = []; - public required Mastery Mastery; - public AchievementInfo AchievementInfo; - public MarriageInfo MarriageInfo; - public readonly Dictionary DungeonEnterLimits = []; - public short DeathCount; - public long DeathTick; - public DeathState DeathState; - public long PremiumTime; - public MentorRole MentorRole; -} +using System.Numerics; +using Maple2.Model.Common; +using Maple2.Model.Enum; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class Character { + #region Immutable + public long CreationTime { get; init; } + public DateTime LastModified { get; init; } + + public long Id { get; init; } + public long AccountId { get; init; } + #endregion + + public long DeleteTime; + public long LastOnlineTime; + + public required string Name; + public Gender Gender; + public int MapId; + public Job Job; + + public SkinColor SkinColor; + public short Level = 1; + public long Exp; + public long RestExp; + + public int Title; + public short Insignia; + + public int RoomId; + public int InstanceMapId; + public short Channel; + public short ReturnChannel; + + public long StorageCooldown; + public long DoctorCooldown; + + public LimitedStack ReturnMaps = new LimitedStack(3); + public Vector3 ReturnPosition; + public string Picture = string.Empty; + public string Motto = string.Empty; + public string GuildName = string.Empty; + public long GuildId; + public int PartyId; + public List ClubIds = []; + public required Mastery Mastery; + public AchievementInfo AchievementInfo; + public MarriageInfo MarriageInfo; + public readonly Dictionary DungeonEnterLimits = []; + public short DeathCount; + public long DeathTick; + public DeathState DeathState; + public long PremiumTime; + public MentorRole MentorRole; +} diff --git a/Maple2.Model/Game/User/GameEventUserValue.cs b/Maple2.Model/Game/User/GameEventUserValue.cs index 55b0dd72d..d7982b0a5 100644 --- a/Maple2.Model/Game/User/GameEventUserValue.cs +++ b/Maple2.Model/Game/User/GameEventUserValue.cs @@ -1,42 +1,42 @@ -using Maple2.Model.Enum; -using Maple2.PacketLib.Tools; -using Maple2.Tools; - -namespace Maple2.Model.Game; - -public class GameEventUserValue : IByteSerializable { - public GameEventUserValueType Type { get; init; } - public string Value { get; private set; } - public int EventId { get; init; } - public long ExpirationTime { get; init; } - - public GameEventUserValue(string value = "") { - Value = value; - } - - public GameEventUserValue(GameEventUserValueType type, long expirationTime, int eventId) { - Value = string.Empty; - Type = type; - ExpirationTime = expirationTime; - EventId = eventId; - } - - public void SetValue(string value) { - Value = value; - } - - public int Int() => int.TryParse(Value, out int result) ? result : 0; - - public long Long() => long.TryParse(Value, out long result) ? result : 0; - - public string String() => Value; - - public bool Bool() => bool.TryParse(Value, out bool result) && result; - - public void WriteTo(IByteWriter writer) { - writer.Write(Type); - writer.WriteInt(EventId); - writer.WriteUnicodeString(Value); - writer.WriteLong(ExpirationTime); - } -} +using Maple2.Model.Enum; +using Maple2.PacketLib.Tools; +using Maple2.Tools; + +namespace Maple2.Model.Game; + +public class GameEventUserValue : IByteSerializable { + public GameEventUserValueType Type { get; init; } + public string Value { get; private set; } + public int EventId { get; init; } + public long ExpirationTime { get; init; } + + public GameEventUserValue(string value = "") { + Value = value; + } + + public GameEventUserValue(GameEventUserValueType type, long expirationTime, int eventId) { + Value = string.Empty; + Type = type; + ExpirationTime = expirationTime; + EventId = eventId; + } + + public void SetValue(string value) { + Value = value; + } + + public int Int() => int.TryParse(Value, out int result) ? result : 0; + + public long Long() => long.TryParse(Value, out long result) ? result : 0; + + public string String() => Value; + + public bool Bool() => bool.TryParse(Value, out bool result) && result; + + public void WriteTo(IByteWriter writer) { + writer.Write(Type); + writer.WriteInt(EventId); + writer.WriteUnicodeString(Value); + writer.WriteLong(ExpirationTime); + } +} diff --git a/Maple2.Model/Game/User/Home.cs b/Maple2.Model/Game/User/Home.cs index b96343dd7..20f8b3a99 100644 --- a/Maple2.Model/Game/User/Home.cs +++ b/Maple2.Model/Game/User/Home.cs @@ -1,245 +1,245 @@ -using System.Numerics; -using Maple2.Model.Enum; -using Maple2.Model.Metadata; -using Maple2.PacketLib.Tools; -using Maple2.Tools; -using Maple2.Tools.Extensions; - -namespace Maple2.Model.Game; - -public class Home : IByteSerializable { - private const byte HOME_PERMISSION_COUNT = 9; - - public long AccountId { get; init; } - public long LastModified { get; set; } - - public byte Area { get; private set; } - public byte Height { get; private set; } - - public byte PlannerArea { get; private set; } - public byte PlannerHeight { get; private set; } - public bool IsPlanner => Indoor.PlotMode is PlotMode.DecorPlanner or PlotMode.BlueprintPlanner; - - public int CurrentArchitectScore { get; set; } - public int ArchitectScore { get; set; } - - // Interior Settings - public HomeBackground Background { get; private set; } - public HomeLighting Lighting { get; private set; } - public HomeCamera Camera { get; private set; } - public string? Passcode { get; set; } - public readonly IDictionary Permissions; - - public long DecorationExp { get; set; } - public long DecorationLevel { get; set; } - public long DecorationRewardTimestamp { get; set; } - public List InteriorRewardsClaimed { get; set; } - public List Layouts { get; set; } - public List Blueprints { get; set; } - - private string message; - public string Message { - get => message; - set { - if (!string.IsNullOrWhiteSpace(value)) { - message = value; - } - } - } - - public PlotInfo Indoor { get; set; } = null!; // Required, when getting a home, this should be set always. Setting to null! to please the compiler. - public PlotInfo? Outdoor { get; set; } - - public string Name => Outdoor?.Name ?? Indoor.Name; - public int PlotMapId => Outdoor?.MapId ?? 0; - public int PlotNumber => Outdoor?.Number ?? 0; - public int ApartmentNumber => Outdoor?.ApartmentNumber ?? 0; - public long PlotExpiryTime => Outdoor?.ExpiryTime ?? (string.IsNullOrEmpty(Name) ? 0 : Indoor.ExpiryTime); // If the name is empty, the plot is not setup yet. - public PlotState State => Outdoor?.State ?? PlotState.Open; - - public bool IsHomeSetup => !string.IsNullOrEmpty(Name); - - public static DateTimeOffset HomeExpiryTime = new DateTimeOffset(2900, 12, 31, 0, 0, 0, TimeSpan.Zero); - - public Home() { - message = string.Empty; - Permissions = new Dictionary(); - InteriorRewardsClaimed = []; - Layouts = []; - Blueprints = []; - } - - public void NewHomeDefaults(string characterName) { - Indoor.Name = characterName; - Indoor.ExpiryTime = HomeExpiryTime.ToUnixTimeSeconds(); - Message = "Thanks for visiting. Come back soon!"; - DecorationLevel = 1; - Passcode = string.Empty; - } - - public bool SetArea(int area) { - if (Area == area) return false; - Area = (byte) Math.Clamp(area, Constant.MinHomeArea, Constant.MaxHomeArea); - return Area == area; - } - - public bool SetHeight(int height) { - if (Height == height) return false; - Height = (byte) Math.Clamp(height, Constant.MinHomeHeight, Constant.MaxHomeHeight); - return Height == height; - } - - public bool SetPlannerArea(int area) { - if (PlannerArea == area) return false; - PlannerArea = (byte) Math.Clamp(area, Constant.MinHomeArea, Constant.MaxHomeArea); - return PlannerArea == area; - } - - public bool SetPlannerHeight(int height) { - if (PlannerHeight == height) return false; - PlannerHeight = (byte) Math.Clamp(height, Constant.MinHomeHeight, Constant.MaxHomeHeight); - return PlannerHeight == height; - } - - public bool SetBackground(HomeBackground background) { - if (Background == background || !System.Enum.IsDefined(background)) { - return false; - } - - Background = background; - return true; - } - - public bool SetLighting(HomeLighting lighting) { - if (Lighting == lighting || !System.Enum.IsDefined(lighting)) { - return false; - } - - Lighting = lighting; - return true; - } - - public bool SetCamera(HomeCamera camera) { - if (Camera == camera || !System.Enum.IsDefined(camera)) { - return false; - } - - Camera = camera; - return true; - } - - public void EnterPlanner(PlotMode mode) { - PlannerArea = Area; - PlannerHeight = Height; - Indoor.PlotMode = mode; - } - - public Vector3 CalculateSafePosition(List plotCubes) { - int area = IsPlanner ? PlannerArea : Area; - - // plots start at 0,0 and are built towards negative x and y - int dimension = -1 * (area - 1); - - // find the blocks in most negative x,y direction, with the highest z value - int height = 0; - if (plotCubes.Count > 0) { - List cubes = plotCubes.Where(cube => cube.Position.X == dimension && cube.Position.Y == dimension).ToList(); - if (cubes.Count > 0) { - height = cubes.Max(cube => cube.Position.Z); - } - } - - dimension *= VectorExtensions.BLOCK_SIZE; - - height++; // add 1 to height to be on top of the block - height *= VectorExtensions.BLOCK_SIZE; - return new Vector3(dimension, dimension, height); - } - - public void GainExp(long exp, IReadOnlyDictionary masteryTable) { - if (exp <= 0 || DecorationLevel >= Constant.HomeDecorationMaxLevel) { - return; - } - - while (masteryTable.TryGetValue((int) DecorationLevel + 1, out MasteryUgcHousingTable.Entry? entry) && DecorationExp + exp >= entry.Exp) { - exp -= entry.Exp - DecorationExp; - DecorationLevel++; - } - - DecorationRewardTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); - DecorationExp += exp; - } - - public void WriteTo(IByteWriter writer) { - writer.WriteLong(AccountId); - writer.WriteUnicodeString(Indoor.Name); - writer.WriteUnicodeString(Message); - writer.WriteByte(); - writer.WriteInt(CurrentArchitectScore); - writer.WriteInt(ArchitectScore); - writer.WriteInt(PlotMapId); - writer.WriteInt(PlotNumber); - writer.Write(Indoor.PlotMode); - writer.WriteByte(Area); - writer.WriteByte(Height); - writer.Write(Background); - writer.Write(Lighting); - writer.Write(Camera); - - writer.WriteByte(HOME_PERMISSION_COUNT); - for (byte i = 0; i < HOME_PERMISSION_COUNT; i++) { - bool enabled = Permissions.TryGetValue((HomePermission) i, out HomePermissionSetting setting); - writer.WriteBool(enabled); - if (enabled) { - writer.Write(setting); - } - } - - writer.WriteByte((byte) Layouts.Count); - foreach (HomeLayout layout in Layouts) { - writer.WriteClass(layout); - } - writer.WriteByte((byte) Blueprints.Count); - foreach (HomeLayout blueprint in Blueprints) { - writer.WriteClass(blueprint); - } - } -} - -public class HomeLayout : IByteSerializable { - public long Uid { get; private set; } - public int Id { get; private set; } - public string Name { get; private set; } - public byte Area { get; private set; } - public byte Height { get; private set; } - public HomeBackground Background { get; init; } - public HomeLighting Lighting { get; init; } - public HomeCamera Camera { get; init; } - public DateTimeOffset Timestamp { get; private set; } - public List Cubes { get; set; } - - public HomeLayout(long uid, int layoutId, string layoutName, byte area, byte height, DateTimeOffset timestamp, List plotCubes) { - Uid = uid; - Id = layoutId; - Name = layoutName; - Area = area; - Height = height; - Timestamp = timestamp; - Cubes = plotCubes; - } - - public HomeLayout(int layoutId, string layoutName, byte area, byte height, DateTimeOffset timestamp, List cubes) { - Id = layoutId; - Name = layoutName; - Area = area; - Height = height; - Timestamp = timestamp; - Cubes = cubes; - } - - public void WriteTo(IByteWriter pWriter) { - pWriter.WriteInt(Id); - pWriter.WriteUnicodeString(Name); - pWriter.WriteLong(Timestamp.ToUnixTimeSeconds()); - } -} +using System.Numerics; +using Maple2.Model.Enum; +using Maple2.Model.Metadata; +using Maple2.PacketLib.Tools; +using Maple2.Tools; +using Maple2.Tools.Extensions; + +namespace Maple2.Model.Game; + +public class Home : IByteSerializable { + private const byte HOME_PERMISSION_COUNT = 9; + + public long AccountId { get; init; } + public long LastModified { get; set; } + + public byte Area { get; private set; } + public byte Height { get; private set; } + + public byte PlannerArea { get; private set; } + public byte PlannerHeight { get; private set; } + public bool IsPlanner => Indoor.PlotMode is PlotMode.DecorPlanner or PlotMode.BlueprintPlanner; + + public int CurrentArchitectScore { get; set; } + public int ArchitectScore { get; set; } + + // Interior Settings + public HomeBackground Background { get; private set; } + public HomeLighting Lighting { get; private set; } + public HomeCamera Camera { get; private set; } + public string? Passcode { get; set; } + public readonly IDictionary Permissions; + + public long DecorationExp { get; set; } + public long DecorationLevel { get; set; } + public long DecorationRewardTimestamp { get; set; } + public List InteriorRewardsClaimed { get; set; } + public List Layouts { get; set; } + public List Blueprints { get; set; } + + private string message; + public string Message { + get => message; + set { + if (!string.IsNullOrWhiteSpace(value)) { + message = value; + } + } + } + + public PlotInfo Indoor { get; set; } = null!; // Required, when getting a home, this should be set always. Setting to null! to please the compiler. + public PlotInfo? Outdoor { get; set; } + + public string Name => Outdoor?.Name ?? Indoor.Name; + public int PlotMapId => Outdoor?.MapId ?? 0; + public int PlotNumber => Outdoor?.Number ?? 0; + public int ApartmentNumber => Outdoor?.ApartmentNumber ?? 0; + public long PlotExpiryTime => Outdoor?.ExpiryTime ?? (string.IsNullOrEmpty(Name) ? 0 : Indoor.ExpiryTime); // If the name is empty, the plot is not setup yet. + public PlotState State => Outdoor?.State ?? PlotState.Open; + + public bool IsHomeSetup => !string.IsNullOrEmpty(Name); + + public static DateTimeOffset HomeExpiryTime = new DateTimeOffset(2900, 12, 31, 0, 0, 0, TimeSpan.Zero); + + public Home() { + message = string.Empty; + Permissions = new Dictionary(); + InteriorRewardsClaimed = []; + Layouts = []; + Blueprints = []; + } + + public void NewHomeDefaults(string characterName) { + Indoor.Name = characterName; + Indoor.ExpiryTime = HomeExpiryTime.ToUnixTimeSeconds(); + Message = "Thanks for visiting. Come back soon!"; + DecorationLevel = 1; + Passcode = string.Empty; + } + + public bool SetArea(int area) { + if (Area == area) return false; + Area = (byte) Math.Clamp(area, Constant.MinHomeArea, Constant.MaxHomeArea); + return Area == area; + } + + public bool SetHeight(int height) { + if (Height == height) return false; + Height = (byte) Math.Clamp(height, Constant.MinHomeHeight, Constant.MaxHomeHeight); + return Height == height; + } + + public bool SetPlannerArea(int area) { + if (PlannerArea == area) return false; + PlannerArea = (byte) Math.Clamp(area, Constant.MinHomeArea, Constant.MaxHomeArea); + return PlannerArea == area; + } + + public bool SetPlannerHeight(int height) { + if (PlannerHeight == height) return false; + PlannerHeight = (byte) Math.Clamp(height, Constant.MinHomeHeight, Constant.MaxHomeHeight); + return PlannerHeight == height; + } + + public bool SetBackground(HomeBackground background) { + if (Background == background || !System.Enum.IsDefined(background)) { + return false; + } + + Background = background; + return true; + } + + public bool SetLighting(HomeLighting lighting) { + if (Lighting == lighting || !System.Enum.IsDefined(lighting)) { + return false; + } + + Lighting = lighting; + return true; + } + + public bool SetCamera(HomeCamera camera) { + if (Camera == camera || !System.Enum.IsDefined(camera)) { + return false; + } + + Camera = camera; + return true; + } + + public void EnterPlanner(PlotMode mode) { + PlannerArea = Area; + PlannerHeight = Height; + Indoor.PlotMode = mode; + } + + public Vector3 CalculateSafePosition(List plotCubes) { + int area = IsPlanner ? PlannerArea : Area; + + // plots start at 0,0 and are built towards negative x and y + int dimension = -1 * (area - 1); + + // find the blocks in most negative x,y direction, with the highest z value + int height = 0; + if (plotCubes.Count > 0) { + List cubes = plotCubes.Where(cube => cube.Position.X == dimension && cube.Position.Y == dimension).ToList(); + if (cubes.Count > 0) { + height = cubes.Max(cube => cube.Position.Z); + } + } + + dimension *= VectorExtensions.BLOCK_SIZE; + + height++; // add 1 to height to be on top of the block + height *= VectorExtensions.BLOCK_SIZE; + return new Vector3(dimension, dimension, height); + } + + public void GainExp(long exp, IReadOnlyDictionary masteryTable) { + if (exp <= 0 || DecorationLevel >= Constant.HomeDecorationMaxLevel) { + return; + } + + while (masteryTable.TryGetValue((int) DecorationLevel + 1, out MasteryUgcHousingTable.Entry? entry) && DecorationExp + exp >= entry.Exp) { + exp -= entry.Exp - DecorationExp; + DecorationLevel++; + } + + DecorationRewardTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + DecorationExp += exp; + } + + public void WriteTo(IByteWriter writer) { + writer.WriteLong(AccountId); + writer.WriteUnicodeString(Indoor.Name); + writer.WriteUnicodeString(Message); + writer.WriteByte(); + writer.WriteInt(CurrentArchitectScore); + writer.WriteInt(ArchitectScore); + writer.WriteInt(PlotMapId); + writer.WriteInt(PlotNumber); + writer.Write(Indoor.PlotMode); + writer.WriteByte(Area); + writer.WriteByte(Height); + writer.Write(Background); + writer.Write(Lighting); + writer.Write(Camera); + + writer.WriteByte(HOME_PERMISSION_COUNT); + for (byte i = 0; i < HOME_PERMISSION_COUNT; i++) { + bool enabled = Permissions.TryGetValue((HomePermission) i, out HomePermissionSetting setting); + writer.WriteBool(enabled); + if (enabled) { + writer.Write(setting); + } + } + + writer.WriteByte((byte) Layouts.Count); + foreach (HomeLayout layout in Layouts) { + writer.WriteClass(layout); + } + writer.WriteByte((byte) Blueprints.Count); + foreach (HomeLayout blueprint in Blueprints) { + writer.WriteClass(blueprint); + } + } +} + +public class HomeLayout : IByteSerializable { + public long Uid { get; private set; } + public int Id { get; private set; } + public string Name { get; private set; } + public byte Area { get; private set; } + public byte Height { get; private set; } + public HomeBackground Background { get; init; } + public HomeLighting Lighting { get; init; } + public HomeCamera Camera { get; init; } + public DateTimeOffset Timestamp { get; private set; } + public List Cubes { get; set; } + + public HomeLayout(long uid, int layoutId, string layoutName, byte area, byte height, DateTimeOffset timestamp, List plotCubes) { + Uid = uid; + Id = layoutId; + Name = layoutName; + Area = area; + Height = height; + Timestamp = timestamp; + Cubes = plotCubes; + } + + public HomeLayout(int layoutId, string layoutName, byte area, byte height, DateTimeOffset timestamp, List cubes) { + Id = layoutId; + Name = layoutName; + Area = area; + Height = height; + Timestamp = timestamp; + Cubes = cubes; + } + + public void WriteTo(IByteWriter pWriter) { + pWriter.WriteInt(Id); + pWriter.WriteUnicodeString(Name); + pWriter.WriteLong(Timestamp.ToUnixTimeSeconds()); + } +} diff --git a/Maple2.Model/Game/User/HomeSurvey.cs b/Maple2.Model/Game/User/HomeSurvey.cs index f5826b846..08889cbab 100644 --- a/Maple2.Model/Game/User/HomeSurvey.cs +++ b/Maple2.Model/Game/User/HomeSurvey.cs @@ -1,53 +1,53 @@ -namespace Maple2.Model.Game; - -public class HomeSurvey { - public readonly long Id; - public readonly bool Public; - - public string Question; - public long OwnerId; - public bool Started; - public bool Ended; - - public int Answers; - public int MaxAnswers; - - // Dictionary