From dbb4df6ddcbc0363ff0723ab572a050d779f17d3 Mon Sep 17 00:00:00 2001 From: Samuel Magnan Date: Sat, 6 Jan 2024 19:44:58 -0500 Subject: [PATCH] Add getter/setter for Sav Gen3/4 Trendy Word, Trainer Hill, Berry Blender, Accessory, Backdrop, Wallpaper (#4152) --- PKHeX.Core/Saves/SAV3E.cs | 47 +++++ PKHeX.Core/Saves/SAV4Pt.cs | 163 ++++++++++++++---- .../Substructures/Gen3/TrainerHillMode3E.cs | 9 + .../Saves/Substructures/Gen3/TrendyWord3E.cs | 38 ++++ .../Saves/Substructures/Gen4/Accessory4.cs | 107 ++++++++++++ .../Saves/Substructures/Gen4/Backdrop4.cs | 23 +++ .../Saves/Substructures/Gen4/ToughWord4.cs | 37 ++++ .../Substructures/Gen4/VillaFurniture4.cs | 24 +++ .../Saves/Substructures/Gen4/Wallpaper4Pt.cs | 29 ++++ 9 files changed, 445 insertions(+), 32 deletions(-) create mode 100644 PKHeX.Core/Saves/Substructures/Gen3/TrainerHillMode3E.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen3/TrendyWord3E.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen4/Accessory4.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen4/Backdrop4.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen4/ToughWord4.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen4/VillaFurniture4.cs create mode 100644 PKHeX.Core/Saves/Substructures/Gen4/Wallpaper4Pt.cs diff --git a/PKHeX.Core/Saves/SAV3E.cs b/PKHeX.Core/Saves/SAV3E.cs index 55d001f76..d8f3ed7e4 100644 --- a/PKHeX.Core/Saves/SAV3E.cs +++ b/PKHeX.Core/Saves/SAV3E.cs @@ -128,6 +128,9 @@ public override uint Coin private const int OFS_PouchBalls = 0x0650; private const int OFS_PouchTMHM = 0x0690; private const int OFS_PouchBerry = 0x0790; + private const int OFS_BerryBlenderRecord = 0x9BC; + private const int OFS_TrendyWord = 0x2E20; + private const int OFS_TrainerHillRecord = 0x3718; protected override InventoryPouch3[] GetItems() { @@ -185,6 +188,50 @@ public int SwarmIndex protected override int ExternalEventData => 0x31B3; + /// + /// Max RPM for 2, 3 and 4 players. Each value unit represents 0.01 RPM. Value 0 if no record. + /// + /// 2 players: index 0, 3 players: index 1, 4 players: index 2 + public const int BerryBlenderRPMRecordCount = 3; + + private Span GetBlenderRPMSpan(int index) + { + if ((uint)index >= BerryBlenderRPMRecordCount) + throw new ArgumentOutOfRangeException(nameof(index)); + return Large.AsSpan(OFS_BerryBlenderRecord + (index * 2)); + } + + public ushort GetBerryBlenderRPMRecord(int index) => ReadUInt16LittleEndian(GetBlenderRPMSpan(index)); + + public void SetBerryBlenderRPMRecord(int index, ushort value) + { + WriteUInt16LittleEndian(GetBlenderRPMSpan(index), value); + State.Edited = true; + } + + public bool GetTrendyWordUnlocked(TrendyWord3E word) + { + return GetFlag(OFS_TrendyWord + ((byte)word >> 3), (byte)word & 7); + } + + public void SetTrendyWordUnlocked(TrendyWord3E word, bool value) + { + SetFlag(OFS_TrendyWord + ((byte)word >> 3), (byte)word & 7, value); + State.Edited = true; + } + + /** Each value unit represents 1/60th of a second. Value 0 if no record. */ + public uint GetTrainerHillRecord(TrainerHillMode3E mode) + { + return ReadUInt32LittleEndian(Large.AsSpan(OFS_TrainerHillRecord + (byte)mode * 4)); + } + + public void SetTrainerHillRecord(TrainerHillMode3E mode, uint value) + { + WriteUInt32LittleEndian(Large.AsSpan(OFS_TrainerHillRecord + (byte)mode * 4), value); + State.Edited = true; + } + #region eBerry private const int OFFSET_EBERRY = 0x31F8; private const int SIZE_EBERRY = 0x34; diff --git a/PKHeX.Core/Saves/SAV4Pt.cs b/PKHeX.Core/Saves/SAV4Pt.cs index 9a1608f40..4c616017d 100644 --- a/PKHeX.Core/Saves/SAV4Pt.cs +++ b/PKHeX.Core/Saves/SAV4Pt.cs @@ -29,7 +29,8 @@ public SAV4Pt(byte[] data) : base(data, GeneralSize, StorageSize, GeneralSize) public const int GeneralSize = 0xCF2C; private const int StorageSize = 0x121E4; // Start 0xCF2C, +4 starts box data - private const int VillaFurnitureStart = 0x111F; + + public const byte BACKDROP_POSITION_IF_NOT_UNLOCKED = 0x12; protected override BlockInfo4[] ExtraBlocks => [ @@ -51,6 +52,12 @@ private void Initialize() protected override int EventFlag => 0xFEC; public override BattleFrontierFacility4 MaxFacility => BattleFrontierFacility4.Arcade; + private const int OFS_AccessoryMultiCount = 0x4E38; // 4 bits each + private const int OFS_AccessorySingleCount = 0x4E58; // 1 bit each + private const int OFS_Backdrop = 0x4E60; + private const int OFS_ToughWord = 0xCEB4; + private const int OFS_VillaFurniture = 0x111F; + private void GetSAVOffsets() { AdventureInfo = 0; @@ -78,6 +85,8 @@ private void GetSAVOffsets() } #region Storage + private const int OFS_Wallpaper = 0x121C6; + private static int AdjustWallpaper(int value, int shift) { // Pt's Special Wallpapers 1-8 are shifted by +0x8 @@ -102,6 +111,25 @@ public override void SetBoxWallpaper(int box, int value) value = AdjustWallpaper(value, 0x08); Storage[GetBoxWallpaperOffset(box)] = (byte)value; } + + public bool GetWallpaperUnlocked(Wallpaper4Pt wallpaperId) + { + if (wallpaperId < Wallpaper4Pt.Distortion) + return true; + + var unlockableWallpaperIdx = wallpaperId - Wallpaper4Pt.Distortion; + return FlagUtil.GetFlag(Storage, OFS_Wallpaper + (unlockableWallpaperIdx >> 3), unlockableWallpaperIdx & 7); + } + + public void SetWallpaperUnlocked(Wallpaper4Pt wallpaperId, bool value) + { + if (wallpaperId < Wallpaper4Pt.Distortion) + return; // Always unlocked + + var unlockableWallpaperIdx = wallpaperId - Wallpaper4Pt.Distortion; + FlagUtil.SetFlag(Storage, OFS_Wallpaper + (unlockableWallpaperIdx >> 3), unlockableWallpaperIdx & 7, value); + State.Edited = true; + } #endregion public override IReadOnlyList Inventory @@ -169,41 +197,112 @@ private Roamer4 GetRoamer(int index) return new Roamer4(mem); } - public bool GetVillaFurniturePurchased(VillaFurniture index) + public byte GetAccessoryOwnedCount(Accessory4 accessory) { - if (index > VillaFurniture.MAX) - throw new ArgumentOutOfRangeException(nameof(index)); - return FlagUtil.GetFlag(General, VillaFurnitureStart + ((byte)index >> 3), (byte)index & 7); + if (accessory < Accessory4.ColoredParasol) + { + byte enumIdx = (byte)accessory; + byte val = General[OFS_AccessoryMultiCount + (enumIdx / 2)]; + if (enumIdx % 2 == 0) + return (byte)(val & 0x0F); + return (byte)(val >> 4); + } + + // Otherwise, it's a single-count accessory + var flagIdx = accessory - Accessory4.ColoredParasol; + if (GetFlag(OFS_AccessorySingleCount + (flagIdx >> 3), flagIdx & 7)) + return 1; + return 0; } - public void SetVillaFurniturePurchased(VillaFurniture index, bool value = true) + public void SetAccessoryOwnedCount(Accessory4 accessory, byte count) { - if (index > VillaFurniture.MAX) + if (accessory < Accessory4.ColoredParasol) + { + if (count > 9) + count = 9; + + var enumIdx = (byte)accessory; + var addr = OFS_AccessoryMultiCount + (enumIdx / 2); + + if (enumIdx % 2 == 0) + { + General[addr] &= 0xF0; // Reset old count to 0 + General[addr] |= count; // Set new count + } + else + { + General[addr] &= 0x0F; // Reset old count to 0 + General[addr] |= (byte)(count << 4); // Set new count + } + } + else + { + var flagIdx = accessory - Accessory4.ColoredParasol; + SetFlag(OFS_AccessorySingleCount + (flagIdx >> 3), flagIdx & 7, count != 0); + } + + State.Edited = true; + } + + public byte GetBackdropPosition(Backdrop4 backdrop) + { + if (backdrop > Backdrop4.Theater) + throw new ArgumentOutOfRangeException(nameof(backdrop), backdrop, null); + return General[OFS_Backdrop + (byte)backdrop]; + } + + public bool GetBackdropUnlocked(Backdrop4 backdrop) + { + return GetBackdropPosition(backdrop) != BACKDROP_POSITION_IF_NOT_UNLOCKED; + } + + public void RemoveBackdrop(Backdrop4 backdrop) => SetBackdropPosition(backdrop, BACKDROP_POSITION_IF_NOT_UNLOCKED); + + /// + /// Sets the position of a backdrop. + /// + /// + /// Every unlocked backdrop must have a different position. + /// Use to remove a backdrop. + /// + public void SetBackdropPosition(Backdrop4 backdrop, byte position) + { + if (backdrop > Backdrop4.Theater) + throw new ArgumentOutOfRangeException(nameof(backdrop), backdrop, null); + if (position > BACKDROP_POSITION_IF_NOT_UNLOCKED) + position = BACKDROP_POSITION_IF_NOT_UNLOCKED; + General[OFS_Backdrop + (byte)backdrop] = position; + State.Edited = true; + } + + public bool GetToughWordUnlocked(ToughWord4 word) + { + if (word > ToughWord4.REMSleep) + throw new ArgumentOutOfRangeException(nameof(word), word, null); + return GetFlag(OFS_ToughWord + ((byte)word >> 3), (byte)word & 7); + } + + public void SetToughWordUnlocked(ToughWord4 word, bool value) + { + if (word > ToughWord4.REMSleep) + throw new ArgumentOutOfRangeException(nameof(word), word, null); + SetFlag(OFS_ToughWord + ((byte)word >> 3), (byte)word & 7, value); + State.Edited = true; + } + + public bool GetVillaFurniturePurchased(VillaFurniture4 index) + { + if (index > VillaFurniture4.Chandelier) throw new ArgumentOutOfRangeException(nameof(index)); - FlagUtil.SetFlag(General, VillaFurnitureStart + ((byte)index >> 3), (byte)index & 7, value); + return GetFlag(OFS_VillaFurniture + ((byte)index >> 3), (byte)index & 7); + } + + public void SetVillaFurniturePurchased(VillaFurniture4 index, bool value = true) + { + if (index > VillaFurniture4.Chandelier) + throw new ArgumentOutOfRangeException(nameof(index)); + SetFlag(OFS_VillaFurniture + ((byte)index >> 3), (byte)index & 7, value); + State.Edited = true; } } - -public enum VillaFurniture -{ - BigSofa, - SmallSofa, - Bed, - NightTable, - TV, - AudioSystem, - Bookshelf, - Rack, - Houseplant, - PCDesk, - MusicBox, - PokemonBust1, - PokemonBust2, - Piano, - GuestSet, - WallClock, - Masterpiece, - TeaSet, - Chandelier, - MAX = Chandelier, -} diff --git a/PKHeX.Core/Saves/Substructures/Gen3/TrainerHillMode3E.cs b/PKHeX.Core/Saves/Substructures/Gen3/TrainerHillMode3E.cs new file mode 100644 index 000000000..a186510b7 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen3/TrainerHillMode3E.cs @@ -0,0 +1,9 @@ +namespace PKHeX.Core; + +public enum TrainerHillMode3E : byte +{ + Normal = 0, + Variety = 1, + Unique = 2, + Expert = 3, +} diff --git a/PKHeX.Core/Saves/Substructures/Gen3/TrendyWord3E.cs b/PKHeX.Core/Saves/Substructures/Gen3/TrendyWord3E.cs new file mode 100644 index 000000000..c14b4c568 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen3/TrendyWord3E.cs @@ -0,0 +1,38 @@ +namespace PKHeX.Core; + +public enum TrendyWord3E : byte +{ + KTHXBYE = 0, + YESSIR = 1, + AVANTGARDE = 2, + COUPLE = 3, + MUCHOBLIGED = 4, + YEEHAW = 5, + MEGA = 6, + ONEHITKO = 7, + DESTINY = 8, + CANCEL = 9, + NEW = 10, + FLATTEN = 11, + KIDDING = 12, + LOSER = 13, + LOSING = 14, + HAPPENING = 15, + HIPAND = 16, + SHAKE = 17, + SHADY = 18, + UPBEAT = 19, + MODERN = 20, + SMELLYA = 21, + BANG = 22, + KNOCKOUT = 23, + HASSLE = 24, + WINNER = 25, + FEVER = 26, + WANNABE = 27, + BABY = 28, + HEART = 29, + OLD = 30, + YOUNG = 31, + UGLY = 32, +} diff --git a/PKHeX.Core/Saves/Substructures/Gen4/Accessory4.cs b/PKHeX.Core/Saves/Substructures/Gen4/Accessory4.cs new file mode 100644 index 000000000..a76a2bd91 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen4/Accessory4.cs @@ -0,0 +1,107 @@ +namespace PKHeX.Core; + +public enum Accessory4 : byte +{ + WhiteFluff = 0, + YellowFluff = 1, + PinkFluff = 2, + BrownFluff = 3, + BlackFluff = 4, + OrangeFluff = 5, + RoundPebble = 6, + GlitterBoulder = 7, + SnaggyPebble = 8, + JaggedBoulder = 9, + BlackPebble = 10, + MiniPebble = 11, + PinkScale = 12, + BlueScale = 13, + GreenScale = 14, + PurpleScale = 15, + BigScale = 16, + NarrowScale = 17, + BlueFeather = 18, + RedFeather = 19, + YellowFeather = 20, + WhiteFeather = 21, + BlackMoustache = 22, + WhiteMoustache = 23, + BlackBeard = 24, + WhiteBeard = 25, + SmallLeaf = 26, + BigLeaf = 27, + NarrowLeaf = 28, + ShedClaw = 29, + ShedHorn = 30, + ThinMushroom = 31, + ThickMushroom = 32, + Stump = 33, + PrettyDewdrop = 34, + SnowCrystal = 35, + Sparks = 36, + ShimmeringFire = 37, + MysticFire = 38, + Determination = 39, + PeculiarSpoon = 40, + PuffySmoke = 41, + PoisonExtract = 42, + WealthyCoin = 43, + EerieThing = 44, + Spring = 45, + Seashell = 46, + HummingNote = 47, + ShinyPowder = 48, + GlitterPowder = 49, + RedFlower = 50, + PinkFlower = 51, + WhiteFlower = 52, + BlueFlower = 53, + OrangeFlower = 54, + YellowFlower = 55, + GooglySpecs = 56, + BlackSpecs = 57, + GorgeousSpecs = 58, + SweetCandy = 59, + Confetti = 60, + + // For accessories below this point, only 1 copy can be owned at once + ColoredParasol = 61, + OldUmbrella = 62, + Spotlight = 63, + Cape = 64, + StandingMike = 65, + Surfboard = 66, + Carpet = 67, + RetroPipe = 68, + FluffyBed = 69, + MirrorBall = 70, + PhotoBoard = 71, + PinkBarrette = 72, + RedBarrette = 73, + BlueBarrette = 74, + YellowBarrette = 75, + GreenBarrette = 76, + PinkBalloon = 77, + RedBalloons = 78, + BlueBalloons = 79, + YellowBalloon = 80, + GreenBalloons = 81, + LaceHeadress = 82, + TopHat = 83, + SilkVeil = 84, + HeroicHeadband = 85, + ProfessorHat = 86, + FlowerStage = 87, + GoldPedestal = 88, + GlassStage = 89, + AwardPodium = 90, + CubeStage = 91, + TURTWIGMask = 92, + CHIMCHARMask = 93, + PIPLUPMask = 94, + BigTree = 95, + Flag = 96, + Crown = 97, + Tiara = 98, + Comet = 99, +} diff --git a/PKHeX.Core/Saves/Substructures/Gen4/Backdrop4.cs b/PKHeX.Core/Saves/Substructures/Gen4/Backdrop4.cs new file mode 100644 index 000000000..e452c04f5 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen4/Backdrop4.cs @@ -0,0 +1,23 @@ +namespace PKHeX.Core; + +public enum Backdrop4 : byte +{ + DressUp = 0, + Ranch = 1, + CityatNight = 2, + SnowyTown = 3, + Fiery = 4, + OuterSpace = 5, + Desert = 6, + CumulusCloud = 7, + FlowerPatch = 8, + FutureRoom = 9, + OpenSea = 10, + TotalDarkness = 11, + TatamiRoom = 12, + GingerbreadRoom = 13, + Seafloor = 14, + Underground = 15, + Sky = 16, + Theater = 17, +} diff --git a/PKHeX.Core/Saves/Substructures/Gen4/ToughWord4.cs b/PKHeX.Core/Saves/Substructures/Gen4/ToughWord4.cs new file mode 100644 index 000000000..544a8ed64 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen4/ToughWord4.cs @@ -0,0 +1,37 @@ +namespace PKHeX.Core; + +public enum ToughWord4 : byte +{ + EarthTones = 0, + Implant = 1, + GoldenRatio = 2, + Omnibus = 3, + Starboard = 4, + MoneyRate = 5, + Resolution = 6, + Cadenza = 7, + Education = 8, + Cubism = 9, + CrossStitch = 10, + Artery = 11, + BoneDensity = 12, + Gommage = 13, + Streaming = 14, + Conductivity = 15, + Copyright = 16, + TwoStep = 17, + Contour = 18, + Neutrino = 19, + Howling = 20, + Spreadsheet = 21, + GMT = 22, + Irritability = 23, + Fractals = 24, + Flambe = 25, + StockPrices = 26, + PHBalance = 27, + Vector = 28, + Polyphenol = 29, + Ubiquitous = 30, + REMSleep = 31, +} diff --git a/PKHeX.Core/Saves/Substructures/Gen4/VillaFurniture4.cs b/PKHeX.Core/Saves/Substructures/Gen4/VillaFurniture4.cs new file mode 100644 index 000000000..ec3955869 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen4/VillaFurniture4.cs @@ -0,0 +1,24 @@ +namespace PKHeX.Core; + +public enum VillaFurniture4 : byte +{ + BigSofa = 0, + SmallSofa = 1, + Bed = 2, + NightTable = 3, + TV = 4, + AudioSystem = 5, + Bookshelf = 6, + Rack = 7, + Houseplant = 8, + PCDesk = 9, + MusicBox = 10, + PokemonBust1 = 11, + PokemonBust2 = 12, + Piano = 13, + GuestSet = 14, + WallClock = 15, + Masterpiece = 16, + TeaSet = 17, + Chandelier = 18, +} diff --git a/PKHeX.Core/Saves/Substructures/Gen4/Wallpaper4Pt.cs b/PKHeX.Core/Saves/Substructures/Gen4/Wallpaper4Pt.cs new file mode 100644 index 000000000..9bf26c6b1 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/Gen4/Wallpaper4Pt.cs @@ -0,0 +1,29 @@ +namespace PKHeX.Core; + +public enum Wallpaper4Pt : byte +{ + Forest = 0, + City = 1, + Desert = 2, + Savanna = 3, + Crag = 4, + Volcano = 5, + Snow = 6, + Cave = 7, + Beach = 8, + Seafloor = 9, + River = 10, + Sky = 11, + Checks = 12, + PokeCenter = 13, + Machine = 14, + Simple = 15, + Distortion = 16, + Contest = 17, + Nostalgic = 18, + Croagunk = 19, + trio = 20, + PikaPika = 21, + Legend = 22, + Team_Galactic = 23, +}