diff --git a/FF1Blazorizer/Tabs/AdjustmentsTab.razor b/FF1Blazorizer/Tabs/AdjustmentsTab.razor index cfde739d0..4e1f3a266 100644 --- a/FF1Blazorizer/Tabs/AdjustmentsTab.razor +++ b/FF1Blazorizer/Tabs/AdjustmentsTab.razor @@ -39,6 +39,7 @@

Conveniences

+ In-Game Tracker No Party Shuffle Speed Hacks Faster Walking Speed diff --git a/FF1Blazorizer/Tabs/QoLTab.razor b/FF1Blazorizer/Tabs/QoLTab.razor index 0e3843943..e543edbc6 100644 --- a/FF1Blazorizer/Tabs/QoLTab.razor +++ b/FF1Blazorizer/Tabs/QoLTab.razor @@ -16,6 +16,7 @@ Uninterrupted Music Lock Respond Rate Respond Rate: + Overlay Orb Letters Accessible Spell Names Cleaner Blursed Names Shop Information Icons @@ -24,6 +25,7 @@ Quick Controller 2 Reset
Remove Unwanted Features:
+ Legacy Shard Display Renounce Autosort Renounce Chest Info Renounce Can't Hold Red diff --git a/FF1Lib/BankUsageRegister.md b/FF1Lib/BankUsageRegister.md index 795b6d5bb..bb1d57b2f 100644 --- a/FF1Lib/BankUsageRegister.md +++ b/FF1Lib/BankUsageRegister.md @@ -181,7 +181,7 @@ ROM: 38000-3BFFF 0E 8DE4-8E10 Fix character stat rendering OE 9079-90D9 Restore npc manip routines 0E 9273-92A3 Restore map object -0E 94E0-94F0 Moved Magic Menu Finalize Ouput +0E 94E0-94F0 Moved Magic Menu Finalize Output 0E 96B0-96F8 Item Jump Table 0E 9C54-9C7D Exit Boss 0E A360-A3AA Magic Shop Menu QoS @@ -198,8 +198,7 @@ ROM: 3C000-3FFFF 0F 8D00-8D53 Autosort 0F 9100-91AC Progressive Scaling 0F B000-B300 Expanded Teleporter tables -0F F100-F200 Encounter RNG Table -0F FCF1-FDF1 Battle RNG Table + ROM: 40000-43FFF 10 8000-BFFF Dialogues (whole bank is reserved) @@ -304,7 +303,7 @@ ROM: 74000-77FFF ROM: 78000-7BFFF 1E 8000-85B0 Moving routines from bank 0E to 1E (PartyGen and menu stuff) 1E 806B-8070 Battlestep to SRAM Jump -1E 85B0-85C1 Parry Permissions table +1E 85B0-85C1 Party Permissions table 1E 8680-86A7 New Icons routine 1E 86E0-B9F1 Encounter Table True PRNG 1E 8800-8933 Class Info Window diff --git a/FF1Lib/BlackOrb.cs b/FF1Lib/BlackOrb.cs index 0309943cb..335f7e12c 100644 --- a/FF1Lib/BlackOrb.cs +++ b/FF1Lib/BlackOrb.cs @@ -30,7 +30,7 @@ private void BlackOrbMode(TalkRoutines talkRoutines, DialogueData dialogues, Fla { if (((bool)flags.Treasures) && flags.ShardHunt) { - EnableShardHunt(rng, TalkRoutines, Dialogues, flags.ShardCount, pref.randomShardNames, flags.SpookyFlag, funRng); + EnableShardHunt(rng, TalkRoutines, Dialogues, flags.ShardCount, pref.randomShardNames, flags.SpookyFlag, pref.LegacyShardDisplay, funRng); } if (!flags.ShardHunt && (flags.GameMode != GameModes.DeepDungeon)) @@ -46,14 +46,27 @@ private void BlackOrbMode(TalkRoutines talkRoutines, DialogueData dialogues, Fla "JEWEL", "PIECE", "CHUNK", "PRISM", "STONE", "SLICE", "WEDGE", "BIGGS", "SLIVR", "ORBLT", "ESPER", "FORCE", }; - public void addShardIcon(int bank, int address) + public async Task AddShardGraphics(int bank, int address, bool legacyShardDisplay, bool orbGraphicsInResourcePack) { - // Replace the upper two tiles of the unlit orb with an empty and found shard. - // These are at tile address $76 and $77 respectively. - PutInBank(bank, address, Blob.FromHex("001C22414141221CFFE3DDBEBEBEDDE3001C3E7F7F7F3E1CFFFFE3CFDFDFFFFF")); + Console.WriteLine("Entering AddShardGraphics"); + if (legacyShardDisplay) + { + // Replace the upper two tiles of the unlit orb with an empty and found shard. + // These are at tile address $76 and $77 respectively. + PutInBank(bank, address + 0x760, Blob.FromHex("001C22414141221CFFE3DDBEBEBEDDE3001C3E7F7F7F3E1CFFFFE3CFDFDFFFFF")); + } + else if (!orbGraphicsInResourcePack) + { + Console.WriteLine("AddShardGraphics Step 2"); + var assembly = System.Reflection.Assembly.GetExecutingAssembly(); + var shardGraphicsFile = assembly.GetManifestResourceNames() + .Single(str => str.EndsWith("orbs_shards.png")); + var shardGraphicsStream = assembly.GetManifestResourceStream(shardGraphicsFile); + await SetCustomOrbGraphics(shardGraphicsStream, bank, address + 0x640); + } } - public void EnableShardHunt(MT19337 rng, TalkRoutines talkroutines, DialogueData dialogues, ShardCount count, bool RandomShardNames, bool skipFlavorText, MT19337 funRngSeed) + public void EnableShardHunt(MT19337 rng, TalkRoutines talkroutines, DialogueData dialogues, ShardCount count, bool RandomShardNames, bool skipFlavorText, bool LegacyShardDisplay, MT19337 funRngSeed) { int goal = 16; switch (count) { @@ -76,12 +89,24 @@ public void EnableShardHunt(MT19337 rng, TalkRoutines talkroutines, DialogueData ItemsText[(int)Item.Shard] = shardName; //addShardIcon(0xD, 0xB760); + if (LegacyShardDisplay) + { + int ppu = 0x2043; + ppu = ppu + (goal <= 24 ? 0x20 : 0x00); - int ppu = 0x2043; - ppu = ppu + (goal <= 24 ? 0x20 : 0x00); + // Fancy shard drawing code, see 0E_B8D7_DrawShardBox.asm + Put(0x3B87D, Blob.FromHex($"A9{ppu & 0xFF:X2}8511A9{(ppu & 0xFF00) >> 8:X2}8512A977A00048AD0220A5128D0620A51118692085118D0620900DAD0220E612A5128D0620A5118D062068A200CC3560D002A976C0{goal:X2}D001608D0720C8E8E006D0EB1890C1")); + } + else + { + byte[] ShardGoal = [(byte)goal, (byte)(goal/10 + 0x80), (byte)(goal%10 + 0x80)]; + // New shard display + PutInBank(0x0E,0xBAA2,Blob.FromHex("01010A09")); + PutInBank(0x0E,0xB8A5,Blob.FromHex("A9A448A90248A91B4C03FEEAEAEAEAEAEAEA")); + PutInBank(0x1B,0xA400,ShardGoal); + PutInBank(0x1B,0xA403,Blob.FromHex("AD0220A9238D0620A9C98D0620A5178D0720A200A9639D106EE8A000AD3560C90AB00CA9FF9D106EE8AD35604C43A4C838E90AC90AB0F8489809809D106EE86809809D106EE8A97A9D106EE8AD01A49D106EE8AD02A49D106EA903853AA902853B20ABDCA200A9068510BD106EAC0220A4558C0620A4548C06208D0720E8E654C610D0E6A93FAC0220A0238C0620A0C08C06208D0720AD3560CD00A49012A9CFAC0220A0238C0620A0C18C06208D0720A90E4C03FE")); - // Fancy shard drawing code, see 0E_B8D7_DrawShardBox.asm - Put(0x3B87D, Blob.FromHex($"A9{ppu & 0xFF:X2}8511A9{(ppu & 0xFF00) >> 8:X2}8512A977A00048AD0220A5128D0620A51118692085118D0620900DAD0220E612A5128D0620A5118D062068A200CC3560D002A976C0{goal:X2}D001608D0720C8E8E006D0EB1890C1")); + } // Black Orb Override to check for shards rather than ORBs. BlackOrbChecksShardsCountFor(goal,talkroutines); diff --git a/FF1Lib/Classes/Lockpicking.cs b/FF1Lib/Classes/Lockpicking.cs index 611e85b01..e5551e800 100644 --- a/FF1Lib/Classes/Lockpicking.cs +++ b/FF1Lib/Classes/Lockpicking.cs @@ -6,7 +6,8 @@ public void EnableLockpicking() { //put in the base hack: see 1B_9300_LockpickDoors.asm for more info PutInBank(0x1F, 0xCE53, Blob.FromHex("AA9848A91B2003FE200093C00168A88AB0ED")); - PutInBank(0x1B, 0x9300, Blob.FromHex("8A4A2903C902D059A2008645AE2560D050AE2661E009900BAE0061E001F042E007F03EAE6661E009900BAE4061E001F030E007F02CAEA661E009900BAE8061E001F01EE007F01AAEE661E009900BAEC061E001F00CE007F008A001AAA90E4C03FEA000AAA90E4C03FE")); + PutInBank(0x1B,0x9300,Blob.FromHex("8A4A29038D036EC902D026A2008645AE2560D01DA2008E046E204093A240204093A280204093A2C02040934901A84C3393A000AE036EA90E4C03FE00000E0107BD2661CD3D93900DBD0061CD3E93F009CD3F93F004A900F002A901A80D046E8D046E60")); + // PutInBank(0x1B, 0x9300, Blob.FromHex("8A4A2903C902D059A2008645AE2560D050AE2661E009900BAE0061E001F042E007F03EAE6661E009900BAE4061E001F030E007F02CAEA661E009900BAE8061E001F01EE007F01AAEE661E009900BAEC061E001F00CE007F008A001AAA90E4C03FEA000AAA90E4C03FE")); } public void SetLockpickingLevel(int requiredLevel) @@ -14,11 +15,12 @@ public void SetLockpickingLevel(int requiredLevel) //overlay the level requirement if (requiredLevel > 0 && requiredLevel <= 50) { - //level is stored zero based - PutInBank(0x1B, 0x9315, new byte[] { (byte)(requiredLevel - 1) }); - PutInBank(0x1B, 0x9327, new byte[] { (byte)(requiredLevel - 1) }); - PutInBank(0x1B, 0x9339, new byte[] { (byte)(requiredLevel - 1) }); - PutInBank(0x1B, 0x934B, new byte[] { (byte)(requiredLevel - 1) }); + // //level is stored zero based + // PutInBank(0x1B, 0x9315, new byte[] { (byte)(requiredLevel - 1) }); + // PutInBank(0x1B, 0x9327, new byte[] { (byte)(requiredLevel - 1) }); + // PutInBank(0x1B, 0x9339, new byte[] { (byte)(requiredLevel - 1) }); + // PutInBank(0x1B, 0x934B, new byte[] { (byte)(requiredLevel - 1) }); + PutInBank(0x1B, 0x933D, (byte)(requiredLevel - 1)); } } @@ -26,15 +28,17 @@ public void SetLockpickingClass(int requiredClass) { if (requiredClass >= 0 && requiredClass < 6) { - PutInBank(0x1B, 0x9315 + 7, new byte[] { (byte)(requiredClass) }); - PutInBank(0x1B, 0x9327 + 7, new byte[] { (byte)(requiredClass) }); - PutInBank(0x1B, 0x9339 + 7, new byte[] { (byte)(requiredClass) }); - PutInBank(0x1B, 0x934B + 7, new byte[] { (byte)(requiredClass) }); + // PutInBank(0x1B, 0x9315 + 7, new byte[] { (byte)(requiredClass) }); + // PutInBank(0x1B, 0x9327 + 7, new byte[] { (byte)(requiredClass) }); + // PutInBank(0x1B, 0x9339 + 7, new byte[] { (byte)(requiredClass) }); + // PutInBank(0x1B, 0x934B + 7, new byte[] { (byte)(requiredClass) }); - PutInBank(0x1B, 0x9315 + 11, new byte[] { (byte)(requiredClass + 6) }); - PutInBank(0x1B, 0x9327 + 11, new byte[] { (byte)(requiredClass + 6) }); - PutInBank(0x1B, 0x9339 + 11, new byte[] { (byte)(requiredClass + 6) }); - PutInBank(0x1B, 0x934B + 11, new byte[] { (byte)(requiredClass + 6) }); + // PutInBank(0x1B, 0x9315 + 11, new byte[] { (byte)(requiredClass + 6) }); + // PutInBank(0x1B, 0x9327 + 11, new byte[] { (byte)(requiredClass + 6) }); + // PutInBank(0x1B, 0x9339 + 11, new byte[] { (byte)(requiredClass + 6) }); + // PutInBank(0x1B, 0x934B + 11, new byte[] { (byte)(requiredClass + 6) }); + PutInBank(0x1B, 0x933E, (byte)requiredClass); + PutInBank(0x1B, 0x933F, (byte)(requiredClass + 6)); } } } diff --git a/FF1Lib/FF1Lib.csproj b/FF1Lib/FF1Lib.csproj index 42492201d..123c4ede8 100644 --- a/FF1Lib/FF1Lib.csproj +++ b/FF1Lib/FF1Lib.csproj @@ -65,6 +65,8 @@ + + diff --git a/FF1Lib/FF1Text.cs b/FF1Lib/FF1Text.cs index 364648120..45c9624ed 100644 --- a/FF1Lib/FF1Text.cs +++ b/FF1Lib/FF1Text.cs @@ -561,7 +561,7 @@ public static Blob TextToCopyrightLine(string text) } // Loads custom icons - public static void AddNewIcons(FF1Rom rom, Flags flags) + public static async Task AddNewIcons(FF1Rom rom, Flags flags, Preferences preferences, ResourcePackSettings resourcePackSettings) { // Icons have a 0x10 Control code indicating the next byte is pulled from the 0x00-0x7F tile reference instead of the 0x80-0xFF like regular fonts @@ -578,8 +578,14 @@ public static void AddNewIcons(FF1Rom rom, Flags flags) var tileset = rom.GetFromBank(0x0D, 0xB600, 0x0200); // Get font tileset rom.PutInBank(0x12, 0x8800 + 0x600, tileset); // Put it in bank 12 - if (flags.ShardHunt) - rom.addShardIcon(0x12, 0x8800 + 0x760); // If we're shard hunt, add the shard to tiles 0x76 and 0x77 because we missed em + if ((bool)flags.Treasures && flags.ShardHunt) + await rom.AddShardGraphics(0x12, 0x8800, preferences.LegacyShardDisplay, resourcePackSettings.OrbGraphics); // If we're in shard hunt, add the shard graphics. + + if (flags.Tracker) + rom.AddTrackerIcons(flags); + + if (preferences.OrbLetterOverlays) + rom.AddOrbLetterOverlays(); // Change where the ORBS are loaded from and to so we can piggyback our icons - Now we load 8 lines from Bank 12 into 0000 of the PPU rom.PutInBank(0x1F, 0xEA9F, Blob.FromHex("2002EAA9122003FEA208A9008510A9888511A900")); diff --git a/FF1Lib/Flags/Flags.cs b/FF1Lib/Flags/Flags.cs index 0b46a7524..16db41ad6 100644 --- a/FF1Lib/Flags/Flags.cs +++ b/FF1Lib/Flags/Flags.cs @@ -434,6 +434,7 @@ public partial class Flags : IIncentiveFlags, IScaleFlags, IVictoryConditionFlag public bool NPCSwatter { get; set; } = false; public bool BattleMagicMenuWrapAround { get; set; } = false; public bool MagicMenuSpellReordering { get; set; } = true; + public bool Tracker { get; set; } = true; public bool InventoryAutosort { get; set; } = true; public bool RepeatedPotionUse { get; set; } = true; public bool AutoRetargeting { get; set; } = true; diff --git a/FF1Lib/Flags/FlagsCompute.cs b/FF1Lib/Flags/FlagsCompute.cs index ab5388ff7..1dc1821de 100644 --- a/FF1Lib/Flags/FlagsCompute.cs +++ b/FF1Lib/Flags/FlagsCompute.cs @@ -273,5 +273,8 @@ public LoosePlacementMode LoosePlacementMode public bool IsAnythingLoose => (IncentivizedItemCountMax > IncentivizedLocationCountMin) || IncentivizeMainItems != true || IncentivizeFetchItems != true || (IncentivizeAirship != true && FreeAirship != true && NoFloater != true) || (IncentivizeCanoeItem != true && FreeCanoe != true) || (IncentivizeShipAndCanal != true && (FreeShip != true || FreeCanal != true)) || (IncentivizeBridgeItem != true && FreeBridge != true) || (IncentivizeTail != true && FreeTail != true && NoTail != true); + + // changed in the randomizer, not in UI + public bool OrbGraphicsInResourcePack { get; set; } = false; } } diff --git a/FF1Lib/Flags/Preferences.cs b/FF1Lib/Flags/Preferences.cs index b7e1e9073..4bb27f305 100644 --- a/FF1Lib/Flags/Preferences.cs +++ b/FF1Lib/Flags/Preferences.cs @@ -33,10 +33,12 @@ public class Preferences public bool randomShardNames { get; set; } = false; public Fate HurrayDwarfFate { get; set; } = Fate.Spare; public bool FunFountainText { get; set; } = false; + public bool LegacyShardDisplay { get; set; } = false; public bool RenounceAutosort { get; set; } = false; public bool RenounceChestInfo { get; set; } = false; public bool RenounceCantHoldRed { get; set; } = false; public bool AccessibleSpellNames { get; set; } = false; + public bool OrbLetterOverlays { get; set; } = false; public bool CleanBlursedEquipmentNames { get; set; } = false; public bool ShopInfoIcons { get; set; } = false; public bool MagicShopMenuChange { get; set; } = false; @@ -55,4 +57,10 @@ public class Preferences public bool NegativeHarmony { get; set; } = false; public bool MapDerp { get; set; } = false; } + + public class ResourcePackSettings + { + + public bool OrbGraphics{ get; set; } = false; + } } diff --git a/FF1Lib/GlobalImprovements.cs b/FF1Lib/GlobalImprovements.cs index 76abbbe2c..558344eb3 100644 --- a/FF1Lib/GlobalImprovements.cs +++ b/FF1Lib/GlobalImprovements.cs @@ -125,16 +125,21 @@ public void EnableBuyQuantity(bool archipelagoenabled) Put(0x39E00, Blob.FromHex("ad0a0385104c668eae0c03bd2060186d0a03c9649001609d206060a903203baaa9018d0a03a520290f856120909f2032aa2043a7a525d056a524d05aa520290fc561f0ed8561c900f0e7c904f02fc908f01ac901f00ace0a03d0d0ee0a03d0cbee0a03c964d0c4ce0a03d0bfad0a0318690a8d0a03c96490b2a96310f5ad0a0338e90af0021002a9018d0a03109d38a90085248525601890f6")); Put(0x39E99, Blob.FromHex("a90e205baaa5620a0a0a186916aabd00038d0c0320b9ecae0a03a9008d0b038d0e038d0f0318ad0b0365108d0b03ad0e0365118d0e03ad0f0369008d0f03b005caf00dd0e1a9ff8d0b038d0e038d0f03ad0f038512ad0e038511ad0b03851020429f2032aa60")); Put(0x39EFF, Blob.FromHex("ad1e60cd0f03f0049016b016ad1d60cd0e03f004900ab00aad1c60cd0b03b00238601860ad1c6038ed0b038d1c60ad1d60ed0e038d1d60ad1e60ed0f038d1e604cefa74c8e8e")); - Put(0x3A494, Blob.FromHex("201b9eb0e820999e20c2a8b0e0a562d0dc20ff9e9008a910205baa4c81a420089e9008a90c205baa4c81a420239fa913205baa4c81a4eaeaea")); + Put(0x3A494, Blob.FromHex("201b9eb0e820999e20c2a8b0e0a562d0dc20ff9e9008a910205baa4c81a420489f9008a90c205baa4c81a420239fa913205baa4c81a4eaeaea")); PutInBank(0x0E, 0x9F90, Blob.FromHex("A5620A0A0A186916A8BE0003BC2060AD0A038CEF6A186DEF6AE963300EA963EDEF6AF0021002A9018D0A034C009E")); - - if (archipelagoenabled) - { - //Replace NewCheckforSpace with patch in 0E_9F48_ItemShopCheckForSpace.asm - PutInBank(0x0E, 0xA4B2, Blob.FromHex("20489F")); - PutInBank(0x0E, 0x9F48, Blob.FromHex("AE0C03E016900CBD2060186D0A03C964900D60A2FFBD006209029D006218609D20601860")); - } + + + + //Replace NewCheckforSpace with patch in 0E_9F48_ItemShopCheckForSpace.asm + /// in AP we want this asm routine to return before adding the item-shop key item to inventory, and let the client do that. + /// in normal seeds, we need to give the item instead, so we NOP out a return. + + byte APReturnByte = archipelagoenabled? (byte)0x60 : (byte)0xEA; + // This is no longer needed -- added to the Blob string above. Do a search for 20489f to find it + //PutInBank(0x0E, 0xA4B2, Blob.FromHex("20489F")); + //PutInBank(0x0E, 0x9F48, Blob.FromHex($"AE0C03E016900CBD2060186D0A03C964900D60A0FFB90062090299006218{APReturnByte:X2}9D20601860")); + PutInBank(0x0E,0x9F48,Blob.FromHex($"AE0C03E016B00CA0FFB90062090299006218{APReturnByte:X2}BD2060186D0A03C9649001609D20601860")); } public void ChangeUnrunnableRunToWait() diff --git a/FF1Lib/Hacks.cs b/FF1Lib/Hacks.cs index 1e84a144f..223d1ce08 100644 --- a/FF1Lib/Hacks.cs +++ b/FF1Lib/Hacks.cs @@ -52,11 +52,12 @@ private void MiscHacks(Flags flags, MT19337 rng) EnableCanalBridge((bool)flags.MapCanalBridge); } - public void DeepDungeonFloorIndicator() + public void DeepDungeonFloorIndicator(Flags flags, Preferences preferences) { + byte FloorIndicatorDestY = (flags.ShardHunt && !preferences.LegacyShardDisplay) ? (byte)0x08 : (byte)0x02; // Add Current Floor indicator above orbs, see 0E_9850_DrawOrbFloor.asm PutInBank(0x0E, 0xB83D, Blob.FromHex("205098")); - PutInBank(0x0E, 0x9850, Blob.FromHex("2078B8A548C93CB018C908900638E9074C7398A8B9A698855AB9AE98855B4C8398A900851020668EA000B13E855AC8B13E855BA97A855CA985855DA982855EA900855FA95A853EA900853FA902853BA904853A4C44B98C998E968C909895B2B5AFA8B5A4B1A8")); + PutInBank(0x0E, 0x9850, Blob.FromHex($"2078B8A548C93CB018C908900638E9074C7398A8B9A698855AB9AE98855B4C8398A900851020668EA000B13E855AC8B13E855BA97A855CA985855DA982855EA900855FA95A853EA900853FA9{FloorIndicatorDestY:X2}853BA904853A4C44B98C998E968C909895B2B5AFA8B5A4B1A8")); // Extend Orb Box PutInBank(0x0E, 0xBAA2, Blob.FromHex("02010809")); } diff --git a/FF1Lib/Randomize.cs b/FF1Lib/Randomize.cs index 9ffa5a7e3..5ea61294d 100644 --- a/FF1Lib/Randomize.cs +++ b/FF1Lib/Randomize.cs @@ -53,6 +53,8 @@ public partial class FF1Rom : NesRom private Blob SavedHash; + public ResourcePackSettings ResourcePackSettings; + public void LoadSharedDataTables() { ItemsText = new ItemNames(this); @@ -107,7 +109,8 @@ public async Task Randomize(Blob seed, Flags flags, Preferences preferenc // data is read // resource pack goes after map derp; Later could make this more efficient. await this.LoadFunTiles(preferences, new MT19337(funRng.Next())); - await this.LoadResourcePackPreROM(flags.ResourcePack, preferences); + ResourcePackSettings = new(); + await this.LoadResourcePackPreROM(flags.ResourcePack, flags, preferences,ResourcePackSettings); // Load Initial Data @@ -167,7 +170,7 @@ public async Task Randomize(Blob seed, Flags flags, Preferences preferenc Dialogues.TransferDialogues(); // Apply general fixes and hacks - FF1Text.AddNewIcons(this, flags); + await FF1Text.AddNewIcons(this, flags, preferences, ResourcePackSettings); Music.ShuffleMusic(this, preferences, new MT19337(funRng.Next())); NewMusic = new NewMusic(this); Bugfixes(flags); @@ -188,7 +191,7 @@ public async Task Randomize(Blob seed, Flags flags, Preferences preferenc await this.Progress("Generating Deep Dungeon's Floors...", 2); DeepDungeon.Generate(rng, Overworld, EncounterRates, ZoneFormations, Dialogues); - DeepDungeonFloorIndicator(); + DeepDungeonFloorIndicator(flags,preferences); warmMechFloor = (MapIndex)DeepDungeon.WarMechFloor; await this.Progress("Generating Deep Dungeon's Floors... Done!"); @@ -411,7 +414,7 @@ public async Task Randomize(Blob seed, Flags flags, Preferences preferenc RollCredits(rng); - + InGameTracker(flags,unmodifiedFlags); StatsTrackingHacks(flags, preferences); if ((bool)flags.IsShipFree || flags.Archipelago) Overworld.SetShipLocation(255); if (flags.TournamentSafe || preferences.CropScreen) ActivateCropScreen(); diff --git a/FF1Lib/ResourcePack.cs b/FF1Lib/ResourcePack.cs index 145c43d69..55ac6ccb4 100644 --- a/FF1Lib/ResourcePack.cs +++ b/FF1Lib/ResourcePack.cs @@ -68,13 +68,14 @@ await SetCustomMapGraphics(maptileStream, 255, 4, } } + // split Resource Pack loading into two task sets: those which should be done pre ROM expansion // and those which should be done after. This is a little slippery, but has mostly // to do with the order in which things are done in the randomizer itself. // In the future, there may need to be a third set, which applies changes after // randomization. - async Task LoadResourcePackPreROM(string resourcepack, Preferences preferences) + async Task LoadResourcePackPreROM(string resourcepack, Flags flags, Preferences preferences, ResourcePackSettings settings) { if (resourcepack == null) { @@ -156,6 +157,16 @@ await SetCustomMapGraphics(s, 128, 4, } } + var orbs = resourcePackArchive.GetEntry("orbs.png"); + if (orbs != null) + { + using (var s = orbs.Open()) + { + await SetCustomOrbGraphics(s, 0x0D, 0xB640); + } + settings.OrbGraphics = true; + } + } @@ -244,6 +255,8 @@ async Task LoadResourcePackPostROM(string resourcepack, DialogueData dialogues, } } + + var enemysprites = resourcePackArchive.GetEntry("enemies.png"); if (enemysprites != null) { diff --git a/FF1Lib/Sprites/CharacterSprites.cs b/FF1Lib/Sprites/CharacterSprites.cs index f9fe0dd53..e2bfe45d2 100644 --- a/FF1Lib/Sprites/CharacterSprites.cs +++ b/FF1Lib/Sprites/CharacterSprites.cs @@ -25,6 +25,8 @@ public partial class FF1Rom : NesRom const int MAPMANGRAPHIC_OFFSET = 0x9000; const int VEHICLEGRAPHIC_OFFSET = 0x9D00; + const int ORBGRAPHIC_OFFSET = 0x37640; //0x37640 before rom expansion + const int NPCGRAPHIC_OFFSET = 0xA200; const int MAPMAN_DOWN = 0; @@ -75,6 +77,67 @@ bool makeMapmanPalette(List colors, Rgba32[] NESpalette, return true; } + bool makeLitOrbPalette(List colors, Rgba32[] NESpalette, + out List pal, + out Dictionary toIndex) + { + pal = new List(); + toIndex = new Dictionary(); + Rgba32 black = new Rgba32(0x00,0x00,0x00); + + colors = OrderByLightness(colors); + + + + colors.Reverse(); + if (colors.Last() != black) + { + colors.Add(black); + } + for (int i = 0; i < colors.Count; i++) + { + if (colors[i].R <= 5 && colors[i].G <= 5 && colors[i].B >= 250) + { + // treat #0000FF as menu color. + toIndex[colors[i]] = 2; + continue; + } + else if (colors[i] == black) + { + toIndex[colors[i]] = 0; + continue; + } + + byte selected = selectColor(colors[i], NESpalette); + int idx = pal.IndexOf(selected); + if (idx == -1) + { + // add 1 everything is going to get shifted + // when the tranparent entry is added + idx = pal.Count == 0? 1 : pal.Count+2; + pal.Add(selected); + } + toIndex[colors[i]] = (byte)idx; + + } + + pal.Insert(0, 0x0F); //black + pal.Insert(2, 0x01); //menu blue + + + + if (pal.Count > 4) + { + return false; + } + while (pal.Count < 4) + { + pal.Add(0x0F); + } + + return true; + } + bool makeBattlePalette(List colors, Rgba32[] NESpalette, out List pal, @@ -646,6 +709,98 @@ public void SetCustomGearIcons(Stream stream) } + public async Task SetCustomOrbGraphics(Stream stream, int bank, int address, bool sync = false) + { + Image image = Image.Load(stream); + // lit orbs share a single palette; unlit orbs must match the border palette + + var litOrbColors = new List(); + var firstUnique = new Dictionary(); + for (int y = 0; y < 32; y++) + { + for (int x = 0; x < 32; x++) + { + if (!litOrbColors.Contains(image[x, y])) + { + firstUnique[image[x, y]] = (x << 16 | y); + litOrbColors.Add(image[x, y]); + } + } + } + + List litOrbPal; + Dictionary litOrbIndex; + if (!makeLitOrbPalette(litOrbColors, NESpalette, out litOrbPal, out litOrbIndex)) + { + if (!sync) + { + await this.Progress($"WARNING: Failed importing orb sprites, too many unique colors (limit 2 unique colors, plus black and menu blue):", + 1 + litOrbPal.Count + litOrbIndex.Count); + for (int i = 1; i < litOrbPal.Count; i++) + { + await this.Progress($"WARNING: NES palette {i}: ${litOrbPal[i],2:X}"); + } + foreach (var i in litOrbIndex) + { + int c = firstUnique[i.Key]; + await this.Progress($"WARNING: RGB to index {i.Key}: {i.Value} first appears at {c >> 16}, {c & 0xFFFF}"); + } + } + return; + } + + int top; + int left; + byte[] tileTopLeft; + byte[] tileTopRight; + byte[] tileBottomLeft; + byte[] tileBottomRight; + + for (int CurrentOrb = 0; CurrentOrb < 4; CurrentOrb++) + { + + top = (CurrentOrb / 2) * 16; + left = (CurrentOrb % 2) * 16; + + tileTopLeft = makeTile(image, top, left, litOrbIndex); + tileTopRight = makeTile(image, top, left + 8, litOrbIndex); + + tileBottomLeft = makeTile(image, top + 8, left, litOrbIndex); + tileBottomRight = makeTile(image, top + 8, left + 8, litOrbIndex); + + // rom sprites are arranged ship, airship, canoe + //int[] litOrb_map = {2,0,1}; + + PutInBank(bank, address + (CurrentOrb * 64) , EncodeForPPU(tileTopLeft)); + PutInBank(bank, address + (CurrentOrb * 64) + (16 * 1), EncodeForPPU(tileTopRight)); + PutInBank(bank, address + (CurrentOrb * 64) + (16 * 2), EncodeForPPU(tileBottomLeft)); + PutInBank(bank, address + (CurrentOrb * 64) + (16 * 3), EncodeForPPU(tileBottomRight)); + } + + top = 32; + left = 0; + var shardTile = makeTile(image,top,left, litOrbIndex); + // this address is hardcoded + PutInBank(bank, address - 0x10, EncodeForPPU(shardTile)); + + int LutMenuPalettes = 0xAD78; + PutInBank(0x0E, LutMenuPalettes, litOrbPal.ToArray()); + + // Unlit orb + top = 32; + left = 16; + tileTopLeft = makeTileQuantize(image,top,left, MenuIndex); + tileTopRight = makeTileQuantize(image,top,left+8, MenuIndex); + tileBottomLeft = makeTileQuantize(image,top+8,left, MenuIndex); + tileBottomRight = makeTileQuantize(image,top+8,left+8, MenuIndex); + + PutInBank(bank, address + 0x120, EncodeForPPU(tileTopLeft)); + PutInBank(bank, address + 0x120 + (16*1), EncodeForPPU(tileTopRight)); + PutInBank(bank, address + 0x120 + (16*2), EncodeForPPU(tileBottomLeft)); + PutInBank(bank, address + 0x120 + (16*3), EncodeForPPU(tileBottomRight)); + } + + // These are terrible, but I need non-async functions for some goddamn reason public void ImportMapmanSync(Image image, int cur_class, int top, int left, Rgba32[] NESpalette) { diff --git a/FF1Lib/Sprites/Sprites.cs b/FF1Lib/Sprites/Sprites.cs index 40105e285..6981d0a70 100644 --- a/FF1Lib/Sprites/Sprites.cs +++ b/FF1Lib/Sprites/Sprites.cs @@ -142,6 +142,14 @@ How they are actually rendered on screen from an emulator depends on what palett { new Rgba32(0xbd, 0xbd, 0xbd), 3 } //0x10 light gray }; + public Dictionary MenuIndex = new Dictionary + { + { new Rgba32(0x00, 0x00, 0x00), 0}, //0x0f black + { new Rgba32(0x7b, 0x7b, 0x7b), 1}, //0x00 dark gray + { new Rgba32(0x00, 0x00, 0xff), 2}, //0x01 menu blue + { new Rgba32(0xff, 0xff, 0xff), 3} //0x30 white + }; + public byte selectColor(Rgba32 px, Rgba32[] NESpalette) { diff --git a/FF1Lib/Tracker.cs b/FF1Lib/Tracker.cs new file mode 100644 index 000000000..0fd88f6a3 --- /dev/null +++ b/FF1Lib/Tracker.cs @@ -0,0 +1,239 @@ +using RomUtilities; +using System; +using System.Collections.Generic; +using System.IO.Compression; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; + +namespace FF1Lib +{ + public enum TrackerIcon + { + + Bridge = 0, + Canal = 1, + Ship = 2, + Canoe = 3, + Airship = 4, + Floater = 5, + Crown = 6, + Crystal = 7, + Herb = 8, + Nurse = 9, + Adamant = 10, + TNT = 11, + Ruby = 12, + Tail = 13, + Bottle = 14, + Fairy = 15, + Slab = 16, + TranslatedSlab = 17, + Rod = 18, + Lute = 19, + Key = 20, + Oxyale = 21, + Chime = 22, + Cube = 23, + Sara = 24, + King = 25, + Bikke = 26, + CrescentSage = 27, + Sarda = 28, + Robot = 29, + ShopItem = 30, + SprintShoes = 31, + Repel = 32, + Mark = 33, + Sigil = 34, + AirBoat = 35, + Boulder = 36, + Bahamut = 37, + EmptyCheckbox = 38, + FilledCheckbox = 39, + + Shard = 42, + LockPicking = 43, + Hints = 44, + GoMode = 45 + } + public partial class FF1Rom + { + const int TRACKER_ICON_BANK = 0x12; + const int TRACKER_ICON_OFFSET = 0x8810; + const int TRACKER_CHECKBOX_OFFSET = 0x8F40; + + const int TRACKER_ORB_OFFSET = 0x8E70; + + + public void InGameTracker(Flags flags, Flags unmodifiedFlags) + { + if (!flags.Tracker) + { + return; + } + + + byte KingReq = (bool)flags.EarlyKing ? (byte)0x00 : (byte)ObjectId.Princess1; + byte SageReq = (bool)flags.EarlySage ? (byte)0x00 : (byte)Item.EarthOrb; + byte SardaReq = (bool)flags.EarlySarda ? (byte)0x00 : (byte)ObjectId.Vampire; + byte BahamutReq = (bool)flags.FightBahamut && (bool)flags.NoTail ? (byte)0x00 : (byte)Item.Tail; + + + byte[] NPCReqs = [KingReq,SageReq,SardaReq,BahamutReq]; + + + // set the width of the "ITEM" box, which we'll use for the tracker: + PutInBank(0x0E, 0xBABE, 0x00); // 0xBABE!! + PutInBank(0x0E, 0xBAC0, 0x20); + + // redirect "ITEM" box draw to our tracker + PutInBank(0x0E, 0xB12E, 0x07); + PutInBank(0x0E, 0xB91D,Blob.FromHex("20EFB8C63B4CD0A0EAEA")); + PutInBank(0x0E, 0xA0D0, Blob.FromHex("20ABDCA9A148A90348A91B4C03FE")); + + + + PutInBank(0x1B, 0xA100, NPCReqs); + + PutInBank(0x1B,0xA104,Blob.FromHex("A9FFA23B9D006ECA10FAA200AD0860F005A9019D006EE8AD0C60D005A9029D006EE8AD0060F005A9039D006EE8AD1260F005A9049D006EE8AD0460F004A905D007AD2B60F005A9069D006EE8AD2260F015A9079D006EA00720F7A29004A975D002A9749D1E6EE8A908851EA00AAD23602002A3A909851EA90A851FA00520F7A22A2901851DA006AD2460201DA3A90B851EA009AD27602002A3A90C851EA008AD26602002A3A01420FDA2B004A975D007AD2960F00AA9749D1E6EA90D9D006EE8A00E20F7A29004A975D00CAC03A1F005B92060F00AA9749D1E6EA90E9D006EE8A90F851EA910851FA01320FDA22A2901851DAD2F60201DA3A911851EA912851FA00B20F7A22A2901851DA00FAD2860201DA3AD2A60F015A9139D006EA01620FDA2B004A975D002A9749D1E6EE8AD2160F015A9149D006EA01720FDA2B004A975D002A9749D1E6EE8A015AD25602048A3A016AD30602048A3A017AD2C602048A3A018AD2E602048A3E8E8E8A919851EA01220FDA22A29012052A3A91A851EAC00A1D004A901D00620F7A22A2901A0012052A3A91B851EA03F20FDA22A2901A0042052A3A91C851EAC01A1D004A901D003B92060A0152052A3A91D851EAC02A1D004A901D00620F7A22A2901A00D2052A3A91E851EA901A0112052A3A91F9D006EA0FF20F7A29004A975D002A9749D1E6E4C6BA3B900624A4A60B900624A60851020F7A29004A975D006A510F00AA9749D1E6EA51E9D006EE860851020F7A29009A51F9D006EA975D016A51DF007A51F9D006ED009A510F00AA51E9D006EA9749D1E6EE860C900F004989D006EE860C900F013A51E9D006E20F7A29004A975D002A9749D1E6EE860A9008D0120A200A91E8510208BA3E63B20ABDCA91E8510AA208BA3A90E4C03FEBD006EAC0220A4558C0620A4548C06208D0720E8E654C610D0E660")); + } + + + + public void AddOrbLetterOverlays() + { + byte[][] Overlays = + [ + //Fire + [ + 4,4,4,4,4,4,4,4, + 4,4,4,0,0,0,0,0, + 4,4,4,0,1,1,1,0, + 4,4,4,0,1,0,0,0, + 4,4,4,0,1,1,0,4, + 4,4,4,0,1,0,4,4, + 4,4,4,0,1,0,4,4, + 4,4,4,0,0,0,4,4 + ], + + //Water + [ + 4,4,4,4,4,4,4,4, + 4,0,0,0,4,0,0,0, + 4,0,1,0,4,0,1,0, + 4,0,1,0,0,0,1,0, + 4,0,1,0,1,0,1,0, + 4,0,1,1,1,1,1,0, + 4,0,0,1,0,1,0,0, + 4,4,0,0,0,0,0,4 + ], + + //Air + [ + 4,4,4,4,4,4,4,4, + 4,4,4,4,0,0,0,4, + 4,4,4,0,0,1,0,0, + 4,4,4,0,1,0,1,0, + 4,4,4,0,1,1,1,0, + 4,4,4,0,1,0,1,0, + 4,4,4,0,1,0,1,0, + 4,4,4,0,0,0,0,0 + ], + + //Earth + [ + 4,4,4,4,4,4,4,4, + 4,4,4,0,0,0,0,0, + 4,4,4,0,1,1,1,0, + 4,4,4,0,1,0,0,0, + 4,4,4,0,1,1,0,4, + 4,4,4,0,1,0,0,0, + 4,4,4,0,1,1,1,0, + 4,4,4,0,0,0,0,0 + ] + ]; + + Console.WriteLine("Doing Orb Overlays"); + for (int i = 0; i < 4; i++) + { + int ThisOffset = TRACKER_ORB_OFFSET+i*0x40; + byte[] OrbTile = DecodePPU(GetFromBank(TRACKER_ICON_BANK, ThisOffset,0x10)); + for (int j = 0; j < 64; j++) + { + byte OrbByte = OrbTile[j]; + byte OverlayByte = Overlays[i][j]; + OrbTile[j] = OverlayByte == 4 ? OrbByte : OverlayByte; + } + PutInBank(TRACKER_ICON_BANK, ThisOffset,EncodeForPPU(OrbTile)); + } + } + + public void AddTrackerIcons(Flags flags) + { + + byte[] BlankTile = EncodeForPPU( + [ + 3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3 + ] + ); + + + + var assembly = System.Reflection.Assembly.GetExecutingAssembly(); + var trackerIconFile = assembly.GetManifestResourceNames(). + Single(str => str.EndsWith("tracker_icons.png")); + var trackerIconStream = assembly.GetManifestResourceStream(trackerIconFile); + + Image trackerIconImage = Image.Load(trackerIconStream); + + Dictionary TrackerIcons = new(); + + foreach (var icon in Enum.GetValues()) + { + int left = 8 * ((int)icon % 6); + int top = 8 * ((int)icon / 6); + + TrackerIcons[icon] = EncodeForPPU(makeTile(trackerIconImage,top,left,MenuIndex)); + } + + if (flags.NoOverworld) + { + TrackerIcons[TrackerIcon.Bridge] = BlankTile; + TrackerIcons[TrackerIcon.Ship] = BlankTile; + TrackerIcons[TrackerIcon.Canoe] = TrackerIcons[TrackerIcon.Mark]; + TrackerIcons[TrackerIcon.Floater] = TrackerIcons[TrackerIcon.Sigil]; + } + else if ((bool)flags.AirBoat) + { + TrackerIcons[TrackerIcon.Floater] = TrackerIcons[TrackerIcon.AirBoat]; + TrackerIcons[TrackerIcon.Airship] = TrackerIcons[TrackerIcon.AirBoat]; + } + if ((bool)flags.FightBahamut) + { + TrackerIcons[TrackerIcon.Tail] = TrackerIcons[TrackerIcon.Bahamut]; + } + + for (int i = 0; i<31; i++) + { + PutInBank(TRACKER_ICON_BANK, i*0x10 + TRACKER_ICON_OFFSET, TrackerIcons[(TrackerIcon)i]); + } + + PutInBank(TRACKER_ICON_BANK, TRACKER_ICON_OFFSET + 0x200, TrackerIcons[TrackerIcon.Shard]); + + // TODO add icons for Sprint Shoes, Repel + + PutInBank(TRACKER_ICON_BANK, TRACKER_CHECKBOX_OFFSET, TrackerIcons[TrackerIcon.EmptyCheckbox]); + PutInBank(TRACKER_ICON_BANK, TRACKER_CHECKBOX_OFFSET + 0x10, TrackerIcons[TrackerIcon.FilledCheckbox]); + } + } +} diff --git a/FF1Lib/asm/0E_9F48_ItemShopCheckForSpace.asm b/FF1Lib/asm/0E_9F48_ItemShopCheckForSpace.asm index 4d3419d08..1da208110 100644 --- a/FF1Lib/asm/0E_9F48_ItemShopCheckForSpace.asm +++ b/FF1Lib/asm/0E_9F48_ItemShopCheckForSpace.asm @@ -3,26 +3,55 @@ shop_quantity = $030A +shop_curitem = $030C +items = $6020 .org $9F48 -NewCheckForSpace: ; we need a slightly more complex formula for +NewCheckForSpace: LDX shop_curitem - CPX #$16 ; CMP ItemId with Tent - BCC :+ - LDA items, X + CPX #$16 + BCS NormalItem + LDY #$FF + LDA game_flags,Y + ORA #GMFLG_EVENT + STA game_flags,Y CLC - ADC shop_quantity ; add the shop item quantity + NOP ; AP: RTS + NormalItem: + LDA items,X + CLC + ADC shop_quantity CMP #$64 BCC SpaceAvailable - RTS ; if we would have 100 or more of the item, return with carry set to indicate we have too many - : LDX #$FF - LDA game_flags, X ; get the game flags - ORA #GMFLG_EVENT ; set the event bit - STA game_flags, X ; and write back - CLC - RTS -SpaceAvailable: - STA items, X ; otherwise, add the items to the player's inventory and return with carry not set to indicate success - CLC - RTS + RTS + SpaceAvailable: + STA items,X + CLC + RTS + ;(34 bytes) + +; NewCheckForSpace: ; we need a slightly more complex formula for +; LDX shop_curitem +; CPX #$16 ; CMP ItemId with Tent +; BCC :+ +; Quantity: +; LDA items,X +; CLC +; ADC shop_quantity ; add the shop item quantity +; CMP #$64 +; BCC SpaceAvailable +; RTS ; if we would have 100 or more of the item, return with carry set to indicate we have too many +; : LDY #$FF +; LDA game_flags,Y ; get the game flags +; ORA #GMFLG_EVENT ; set the event bit +; STA game_flags,Y ; and write back +; BNE Quantity ; AP: CLC +; ; RTS +; SpaceAvailable: +; STA items, X ; otherwise, add the items to the player's inventory and return with carry not set to indicate success +; CLC +; RTS +; ;(36 bytes) + + diff --git a/FF1Lib/asm/1B_9300_LockpickDoors.asm b/FF1Lib/asm/1B_9300_LockpickDoors.asm index 3bfb2c41b..8e72c7b59 100644 --- a/FF1Lib/asm/1B_9300_LockpickDoors.asm +++ b/FF1Lib/asm/1B_9300_LockpickDoors.asm @@ -1,9 +1,21 @@ -.include "Constants.inc" +; UPDATED 06/06/26 for use with tracker + +.include "Constants.inc" .include "variables.inc" +door_bits = $6E03 ; unused expansion ram ($6E00 is used as a tmp gold value elsewhere) +lockpicking_status = $6E04 + + SwapPRG = $FE03 SMMove_Norm_RTS = $CE52 +tileprop = $44 +item_mystickey = $6025 +BANK_MENUS = $0E +TP_SPEC_DOOR = %00000010 +TP_SPEC_LOCKED = %00000100 + ;currently in the MENU_BANK ;this replacement could be a lot less bytes if we use a temporary memory to store A ;A gets overwriten by the bank swap method and we cant grab it from the stack without some heavy stack manipulation @@ -24,71 +36,83 @@ BCS SMMove_Norm_RTS ;save a byte by just branching to a nearby rts instead of ha ;exact change replacing 18 bytes at CE53 ;AA 98 48 A9 1B 20 03 FE 20 00 93 C0 01 68 A8 8A B0 ED +;; new version +; ;bank 1b -.ORG $9300 -CheckDoorLocked: - TXA - LSR A ; downshift to get the door bits into the low 2 bits - AND #(TP_SPEC_DOOR | TP_SPEC_LOCKED) >> 1 ; mask out the door bits - - CMP #TP_SPEC_LOCKED >> 1 ; see if the door is locked - BNE DoorUnlocked ; if not.. open the door - LDX #0 ; otherwise (door is locked) - STX tileprop+1 ; erase the secondary attribute byte (prevent it from being a locked shop) - LDX item_mystickey ; check to see if the player has the key - BNE DoorUnlocked ; if they do, open the door - ;all of this checking is rom space ineffecient but I don't know if we have available temp memory to do an index - ;check to see if they have a thief/ninja in the party, and they're at or above level 10, the class and level are randomizable at rom creation - LDX ch_level - CPX #$09 - BCC Slot1UnderLeveled - LDX ch_class - CPX #$01 - BEQ DoorUnlocked - CPX #$07 - BEQ DoorUnlocked - - Slot1UnderLeveled: - LDX ch_level+$40 - CPX #$09 - BCC Slot2UnderLeveled - LDX ch_class+$40 - CPX #$01 - BEQ DoorUnlocked - CPX #$07 - BEQ DoorUnlocked - - Slot2UnderLeveled: - LDX ch_level+$80 - CPX #$09 - BCC Slot3UnderLeveled - LDX ch_class+$80 - CPX #$01 - BEQ DoorUnlocked - CPX #$07 - BEQ DoorUnlocked - - Slot3UnderLeveled: - LDX ch_level+$C0 - CPX #$09 - BCC Slot4UnderLeveled - LDX ch_class+$C0 - CPX #$01 - BEQ DoorUnlocked - CPX #$07 - BEQ DoorUnlocked - - Slot4UnderLeveled: - LDY #$01 - TAX - LDA #BANK_MENUS - JMP SwapPRG - - DoorUnlocked: - LDY #$00 - TAX - LDA #BANK_MENUS - JMP SwapPRG - -;105 bytes -;8A 4A 29 03 C9 02 D0 59 A2 00 86 45 AE 25 60 D0 50 AE 26 61 E0 09 90 0B AE 00 61 E0 01 F0 42 E0 07 F0 3E AE 66 61 E0 09 90 0B AE 40 61 E0 01 F0 30 E0 07 F0 2C AE A6 61 E0 09 90 0B AE 80 61 E0 01 F0 1E E0 07 F0 1A AE E6 61 E0 09 90 0B AE C0 61 E0 01 F0 0C E0 07 F0 08 A0 01 AA A9 0E 4C 03 FE A0 00 AA A9 0E 4C 03 FE + + + +;; 35 bytes +;; +;; BD 26 61 CD 3D 93 90 0D BD 00 61 CD 3E 93 F0 09 CD 3F 93 F0 04 A9 00 F0 02 A9 01 A8 0D 04 6E 8D 04 6E 60 + + +;; old version +;bank 1b +; .ORG $9300 +; CheckDoorLocked: +; TXA +; LSR A ; downshift to get the door bits into the low 2 bits +; AND #(TP_SPEC_DOOR | TP_SPEC_LOCKED) >> 1 ; mask out the door bits + +; CMP #TP_SPEC_LOCKED >> 1 ; see if the door is locked +; BNE DoorUnlocked ; if not.. open the door +; LDX #0 ; otherwise (door is locked) +; STX tileprop+1 ; erase the secondary attribute byte (prevent it from being a locked shop) +; LDX item_mystickey ; check to see if the player has the key +; BNE DoorUnlocked ; if they do, open the door +; ;all of this checking is rom space ineffecient but I don't know if we have available temp memory to do an index +; ;check to see if they have a thief/ninja in the party, and they're at or above level 10, the class and level are randomizable at rom creation +; LDX ch_level +; CPX #$09 +; BCC Slot1UnderLeveled +; LDX ch_class +; CPX #$01 +; BEQ DoorUnlocked +; CPX #$07 +; BEQ DoorUnlocked + +; Slot1UnderLeveled: +; LDX ch_level+$40 +; CPX #$09 +; BCC Slot2UnderLeveled +; LDX ch_class+$40 +; CPX #$01 +; BEQ DoorUnlocked +; CPX #$07 +; BEQ DoorUnlocked + +; Slot2UnderLeveled: +; LDX ch_level+$80 +; CPX #$09 +; BCC Slot3UnderLeveled +; LDX ch_class+$80 +; CPX #$01 +; BEQ DoorUnlocked +; CPX #$07 +; BEQ DoorUnlocked + +; Slot3UnderLeveled: +; LDX ch_level+$C0 +; CPX #$09 +; BCC Slot4UnderLeveled +; LDX ch_class+$C0 +; CPX #$01 +; BEQ DoorUnlocked +; CPX #$07 +; BEQ DoorUnlocked + +; Slot4UnderLeveled: +; LDY #$01 +; TAX +; LDA #BANK_MENUS +; JMP SwapPRG + +; DoorUnlocked: +; LDY #$00 +; TAX +; LDA #BANK_MENUS +; JMP SwapPRG + +; ;105 bytes +; ;8A 4A 29 03 C9 02 D0 59 A2 00 86 45 AE 25 60 D0 50 AE 26 61 E0 09 90 0B AE 00 61 E0 01 F0 42 E0 07 F0 3E AE 66 61 E0 09 90 0B AE 40 61 E0 01 F0 30 E0 07 F0 2C AE A6 61 E0 09 90 0B AE 80 61 E0 01 F0 1E E0 07 F0 1A AE E6 61 E0 09 90 0B AE C0 61 E0 01 F0 0C E0 07 F0 08 A0 01 AA A9 0E 4C 03 FE A0 00 AA A9 0E 4C 03 FE diff --git a/FF1Lib/asm/1B_A100_Tracker.asm b/FF1Lib/asm/1B_A100_Tracker.asm new file mode 100644 index 000000000..0c0e5adea --- /dev/null +++ b/FF1Lib/asm/1B_A100_Tracker.asm @@ -0,0 +1,844 @@ +tmp = $10 +event_flag = tmp+$0D +icon_id1 = tmp+$0E +icon_id2 = tmp+$0F +dest_x = $3A +dest_y = $3B +ppu_dest = $54 +icon_buf = $6E00 +unsram = $6000 ; $400 bytes +ship_vis = unsram+$00 +airship_vis = unsram+$04 +bridge_vis = unsram+$08 +canal_vis = unsram+$0C +has_canoe = unsram+$12 ; (not to be confused with item_canoe) + +items = unsram+$20 + +item_lute = items+$01 +item_crown = items+$02 +item_crystal = items+$03 +item_herb = items+$04 +item_mystickey = items+$05 +item_tnt = items+$06 +item_adamant = items+$07 +item_slab = items+$08 +item_ruby = items+$09 +item_rod = items+$0A +item_floater = items+$0B +item_chime = items+$0C +item_tail = items+$0D +item_cube = items+$0E +item_bottle = items+$0F +item_oxyale = items+$10 +orb_earth = items+$11 + +game_flags = unsram+$200 + + + + +DrawMainItemBox = $B8EF ; bank 0E + +CoordToNTAddr = $DCAB ; bank 1F +MenuCondStall = $E12E ; bank 1F +SwapPRG = $FE03 ; bnak 1F + +SOURCE_BANK = $0E +DEST_BANK = $1B + + +;;;;;;;;;;;;;;;;;;;;;; +;; Tracker Icon Tiles +;;;;;;;;;;;;;;;;;;;;;; +BRIDGE = $01 +CANAL = $02 +SHIP = $03 +CANOE = $04 +MARK = $04 +AIRSHIP = $05 +FLOATER = $06 +SIGIL = $06 +CROWN = $07 +CRYSTAL = $08 +HERB = $09 +ELFDOC = $0A +ADAMANT = $0B +TNT = $0C +RUBY = $0D +TAIL = $0E +BOTTLE = $0F +FAIRY = $10 +SLAB = $11 +TRSLAB = $12 +ROD = $13 +LUTE = $14 +KEY = $15 +OXYALE = $16 +CHIME = $17 +CUBE = $18 +SARA = $19 +KING = $1A +BIKKE = $1B +SAGE = $1C +SARDA = $1D +ROBOT = $1E +SHOP = $1F +;SHOES = $1F +;REPEL = $20 + +EMPTYCH = $74 +FILLEDCH = $75 +BLANK = $FF + +OBJID_KING = $01 ; King of Coneria +OBJID_GARLAND = $02 ; Garland (the first one, not ToFR) +OBJID_PRINCESS_1 = $03 ; kidnapped princess (in ToF) +OBJID_BIKKE = $04 ; Bikke the Pirate +OBJID_ELFDOC = $05 ; Attending the Elf Prince (more of a nurse, let's be honest) +OBJID_ELFPRINCE = $06 ; Elf Prince (sleeping man-beauty) +OBJID_ASTOS = $07 ; Astos -- the dark king! omg scarey +OBJID_NERRICK = $08 ; Nerrick -- the dwarf working on the canal +OBJID_SMITH = $09 ; Smith, the dwarven blacksmith (no, he's not Watts) +OBJID_MATOYA = $0A +OBJID_UNNE = $0B ; you've never heard of him? +OBJID_VAMPIRE = $0C ; Earth Cave's Vampire +OBJID_SARDA = $0D +OBJID_BAHAMUT = $0E ; Bahamut +OBJID_LEFEIN = $0F ; Lefein chime guy +OBJID_CUBEBOT = $11 ; waterfall robot +OBJID_PRINCESS_2 = $12 ; rescued princess (in Coneria Castle) +OBJID_FAIRY = $13 ; fairy that appears from the bottle +OBJID_TITAN = $14 ; Titan in Titan's Tunnel +OBJID_CANOESAGE = $15 ; sage you get canoe from in vanilla +OBJID_RODPLATE = $16 ; plate that is removed with the Rod +OBJID_LUTEPLATE = $17 ; plate that is removed with the Lute + +OBJID_SKYWAR_FIRST = $3A ; start of the 5 sky warriors +OBJID_SKYWAR_LAST = OBJID_SKYWAR_FIRST+4 ; last of the 5 sky warriors + +OBJID_PIRATETERR_1 = $3F ; townspeople that were terrorized by the +OBJID_PIRATETERR_2 = $40 ; pirates... they don't become visible until after +OBJID_PIRATETERR_3 = $41 ; you beat Bikke and claim the ship + +OBJID_SHOPITEM = $FF ; a key item you buy from a shop + + +.ORG $B12D ; bank $0E +LDA #07 ;; load the box ID (dimensions must be changed from $08 to $1C at $BAC0 in bank $0E) +JSR DrawItemTitleBox + + +.ORG $B91D ; bank $0E +DrawItemTitleBox: + JSR DrawMainItemBox ;; dest_x & dest_y now set + DEC dest_y ;; we need one row above the usual menu start location + JMP ItemTrackerRedirect + NOP + NOP + ; 10 bytes -- same as vanilla + +.ORG $A0D0 ; bank $0E + +ItemTrackerRedirect: + JSR CoordToNTAddr ;; convert dest_x & dest_y to ppu address and store in ppu_dest (2 bytes) + LDA #>(ItemTrackerInit-1) + PHA + LDA #<(ItemTrackerInit-1) + PHA + LDA #DEST_BANK + JMP SwapPRG + ; 14 bytes + + + + + +.ORG $A100 ; bank $1B + + +;; written by the randomizer. +;; check flag = #OBJID +;; check item = item id (offset from items) +;; noreq = #0 +;; order: KING SAGE SARDA BAHAMUT +lut_NPCReqs: + .BYTE OBJID_PRINCESS_1 $11 OBJID_VAMPIRE $0D + ; (4 bytes) + +.ORG $A104 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; SUBROUTINES +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Tracker Logic begins here +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +lut_NPCReqs = $A100 + +ItemTrackerInit: + LDA #BLANK + LDX #59 + initloop: ; fill 60 icon_buf values with blank menu tile + STA icon_buf,X + DEX + BPL initloop + + ; 13 bytes + + ;; begin processing items immediately: + +ProcessItems: + LDX #$00 ; start at icon_buf[0] + ; 2 bytes + +;;; first 4 OW items could be a subroutine but it only saves 3 bytes -- 40 vs 37 +; CheckBridge: + LDA bridge_vis + BEQ NoBridge + LDA #BRIDGE + STA icon_buf,X + NoBridge: + INX + ; 11 bytes + + +; CheckCanal: + LDA canal_vis + BNE NoCanal ; canal logic is flipped + LDA #CANAL + STA icon_buf,X + NoCanal: + INX + ; 11 bytes + +; CheckShip -- should work with AirBoat + LDA ship_vis + BEQ NoShip + LDA #SHIP + STA icon_buf,X + NoShip: + INX + ; 11 bytes + +; CheckCanoe: + LDA has_canoe + BEQ NoCanoe + LDA #CANOE + STA icon_buf,X + NoCanoe: + INX + ; 11 bytes + +; CheckFloaterAirship +; With AirBoat on, the FLOATER and AIRSHIP icons are identical + LDA airship_vis + BEQ NoAirship + LDA #AIRSHIP + BNE GotAirship + NoAirship: + LDA item_floater + BEQ NoFloater + LDA #FLOATER + GotAirship: + STA icon_buf,X + NoFloater: + INX + ; 20 bytes + +; CheckCrown: + LDA item_crown + BEQ NoCrown + LDA #CROWN + STA icon_buf,X + LDY #OBJID_ASTOS + JSR CheckGameEventFlag + BCC NoAstos + LDA #FILLEDCH + BNE CrownCheckbox + NoAstos: + LDA #EMPTYCH + CrownCheckbox: + STA icon_buf+30,X + NoCrown: + INX + ; 27 bytes + +; CheckCrystal + LDA #CRYSTAL + STA icon_id1 + LDY #OBJID_MATOYA + LDA item_crystal + JSR CheckTurnInItem + ; 12 bytes + + +; CheckHerb: + LDA #HERB + STA icon_id1 + LDA #ELFDOC + STA icon_id2 + LDY #OBJID_ELFDOC + JSR CheckGameEventFlag + ROL + AND #1 + STA event_flag + LDY #OBJID_ELFPRINCE + LDA item_herb + JSR CheckTwoPartTurnInItem + ; 26 bytes + +; CheckAdamant + LDA #ADAMANT + STA icon_id1 + LDY #OBJID_SMITH + LDA item_adamant + JSR CheckTurnInItem + ; 12 bytes + +; CheckTNT + LDA #TNT + STA icon_id1 + LDY #OBJID_NERRICK + LDA item_tnt + JSR CheckTurnInItem + ; 12 bytes + +; CheckRuby + LDY #OBJID_TITAN + JSR IsObjectVisible + BCS TitanStillThere + LDA #FILLEDCH + BNE RubyCheckbox + TitanStillThere: + LDA item_ruby + BEQ NoRuby + LDA #EMPTYCH + RubyCheckbox: + STA icon_buf+30,X + LDA #RUBY + STA icon_buf,X + NoRuby: + INX + + ; 12 bytes + +; CheckTail + LDY #OBJID_BAHAMUT + JSR CheckGameEventFlag + BCC NoTailTurnIn + LDA #FILLEDCH + BNE TailCheckbox + NoTailTurnIn: + LDY lut_NPCReqs+3 + BEQ NoBahamutReq + LDA items,Y + BEQ NoTail + NoBahamutReq: + LDA #EMPTYCH + TailCheckbox: + STA icon_buf+30,X + LDA #TAIL + STA icon_buf,X + NoTail: + INX + +; CheckBottle: + LDA #BOTTLE + STA icon_id1 + LDA #FAIRY + STA icon_id2 + LDY #OBJID_FAIRY + JSR IsObjectVisible + ROL + AND #1 + STA event_flag + ; LDY #OBJID_FAIRY -- Y still set to fairy + LDA item_bottle + JSR CheckTwoPartTurnInItem + ; 24 bytes + +; CheckSlab: + LDA #SLAB + STA icon_id1 + LDA #TRSLAB + STA icon_id2 + LDY #OBJID_UNNE + JSR CheckGameEventFlag + ROL + AND #1 + STA event_flag + LDY #OBJID_LEFEIN + LDA item_slab + JSR CheckTwoPartTurnInItem + ; 26 bytes + + +;;; inlining these takes fewer bytes +; CheckRod: + LDA item_rod + BEQ NoRod + LDA #ROD + STA icon_buf,X + LDY #OBJID_RODPLATE + JSR IsObjectVisible + BCS RodPlateNotCleared + LDA #FILLEDCH + BNE RodCheckbox + RodPlateNotCleared: + LDA #EMPTYCH + RodCheckbox: + STA icon_buf+30,X + NoRod: + INX + +; CheckLute: + LDA item_lute + BEQ NoLute + LDA #LUTE + STA icon_buf,X + LDY #OBJID_LUTEPLATE + JSR IsObjectVisible + BCS LutePlateNotCleared + LDA #FILLEDCH + BNE LuteCheckbox + LutePlateNotCleared: + LDA #EMPTYCH + LuteCheckbox: + STA icon_buf+30,X + NoLute: + INX + + +; CheckKey: + LDY #KEY + LDA item_mystickey + JSR CheckPassiveItem + ; (8 bytes) + + LDY #OXYALE + LDA item_oxyale + JSR CheckPassiveItem + ; (8 bytes) + + LDY #CHIME + LDA item_chime + JSR CheckPassiveItem + ; (8 bytes) + + LDY #CUBE + LDA item_cube + JSR CheckPassiveItem + ; (8 bytes) + +; TODO: Check Sprint Shoes and Repel + INX + INX + ; (2 bytes for now) + + INX ; space to offset NPCs from Items + +; Check Princess + LDA #SARA + STA icon_id1 + + LDY #OBJID_PRINCESS_2 + JSR IsObjectVisible + ROL + AND #1 + + JSR CheckNPC ;; Y still has Sara's OBJID + ; (15 bytes) + + +; Check King + + LDA #KING + STA icon_id1 + LDY lut_NPCReqs + BNE KingReq + LDA #1 + BNE KingID + KingReq: + JSR CheckGameEventFlag + ROL + AND #1 + KingID: + LDY #OBJID_KING + JSR CheckNPC + ; (17 bytes) + +; Check Bikke + LDA #BIKKE + STA icon_id1 + LDY #OBJID_PIRATETERR_1 + JSR IsObjectVisible + ROL + AND #1 + LDY #OBJID_BIKKE + JSR CheckNPC + ; (17 bytes) + + +; Check Canoe Sage + LDA #SAGE + STA icon_id1 + LDY lut_NPCReqs+1 + BNE SageReq + LDA #1 + BNE SageID + SageReq: + LDA items,Y + SageID: + LDY #OBJID_CANOESAGE + JSR CheckNPC + ; (11 bytes) + + +; Check Sarda + LDA #SARDA + STA icon_id1 + LDY lut_NPCReqs+2 + BNE SardaReq + LDA #1 + BNE SardaID + SardaReq: + JSR CheckGameEventFlag + ROL + AND #1 + SardaID + LDY #OBJID_SARDA + JSR CheckNPC + ; (19 bytes) + + +; Check Robot + LDA #ROBOT + STA icon_id1 + ;; requirement logic ;;;;;; + LDA #1 ;; + ;; end requirement logic ;; + LDY #OBJID_CUBEBOT + JSR CheckNPC + ; (11 bytes) + +; Check ShopItem + LDA #SHOP + STA icon_buf,X + LDY #OBJID_SHOPITEM + JSR CheckGameEventFlag + BCC NoShopItem + LDA #FILLEDCH + BNE ShopItemCheckbox + NoShopItem: + LDA #EMPTYCH + ShopItemCheckbox: + STA icon_buf+30,X + + + + + JMP DrawIconsToItemMenu + +;;;;;; copied from bank $0E +CheckGameEventFlag: + LDA game_flags,Y ; Get the game flags using Y as index + LSR A ; and shift the event flag into C + LSR A + RTS + ; 6 bytes + +IsObjectVisible: + LDA game_flags,Y ; get the game flags using object ID as index + LSR A ; shift object visibility flag into C + RTS ; and exit + ; 5 bytes + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; CheckTurnInItem +;; +;; Check item that is removed from +;; inventory after turn-in +;; +;; applies to these items: +;; crystal +;; adamant +;; tnt +;; +;; Not ruby -- Titan's event flag is not updated in talk routines +;; +;; in: +;; A: item flag +;; X: pointer to location in icon_buf +;; Y: npc #OBJID +;; icon_id1: icon to draw +;; out: +;; X: incremented icon_buf pointer +;; +;; setup: (this could be done more elegantly with a lut, but there aren't enough checks to worry about it) +;; LDA #ITEMICON +;; STA icon_id1 +;; LDY #OBJID +;; LDA item flag +;; JSR CheckTurnInItem +;; (11 bytes per check) +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +CheckTurnInItem: + ; CMP #0 ; not strictly needed since setup should load the item flag just before entering + ; BEQ NoTurnInItem + ; LDA #EMPTYCH + ; BNE TurnInCheckbox + ; NoTurnInItem: + ; JSR CheckGameEventFlag + ; BCC NoTurnIn + ; LDA #FILLEDCH + ; TurnInCheckbox: + ; STA icon_buf+30,X + ; LDA icon_id1 + ; STA icon_buf,X + ; NoTurnin: + ; INX + ; RTS + ; ; (25 bytes) + STA tmp + JSR CheckGameEventFlag + BCC NoTurnIn + LDA #FILLEDCH + BNE TurnInCheckbox + NoTurnIn: + LDA tmp + BEQ NoTurnInItem + LDA #EMPTYCH + TurnInCheckbox: + STA icon_buf+30,X + LDA icon_id1 + STA icon_buf,X + NoTurnInItem: + INX + RTS + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; CheckTwoPartTurnInItem +;; Check item that is removed from +;; inventory after an event, and that +;; then requires talking to an NPC to +;; receive the item. +;; +;; applies to these: +;; ITEM -> EVENT -> NPC +;; herb -> talk to Elf Doc -> Elf Prince +;; slab -> talk to Unne -> Lefein Guy +;; bottle -> open bottle -> Fairy +;; +;; in: +;; A: item flag +;; X: pointer to location in icon_buf +;; Y: final npc #OBJID +;; event_flag: talked to Elf Doc, Talked to Unne, Released Fairy +;; icon_id1: icon to draw if item held +;; icon_id2: icon to draw if event has taken place +;; +;; out: +;; X: incremented icon_buf pointer +;; +;; setup: +;; LDA #ITEMICON1 +;; STA icon_id1 +;; LDA #ITEMICON2 +;; STA icon_id2 +;; LDY eventOBJID +;; JSR CheckGameEventFlag OR IsObjectVisible +;; ROL +;; AND #1 +;; STA event_flag +;; LDY #OBJID +;; LDA item flag +;; JSR CheckTwoPartTurnInItem +;; (26 bytes per check) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +CheckTwoPartTurnInItem: + STA tmp + JSR CheckGameEventFlag + BCC NoFinalTurnIn + LDA icon_id2 + STA icon_buf,X + LDA #FILLEDCH + BNE TwoPartCheckbox + NoFinalTurnin: + LDA event_flag + BEQ NoInitialEvent + LDA icon_id2 + STA icon_buf,X + BNE TwoPartEmptyCheckbox + NoInitialEvent: + LDA tmp + BEQ NoTwoPartItem + LDA icon_id1 + STA icon_buf,X + TwoPartEmptyCheckbox: + LDA #EMPTYCH + TwoPartCheckbox: + STA icon_buf+30,X + NoTwoPartItem: + INX + RTS + ; CMP #0 + ; BEQ NoTwoPartItem + ; LDA icon_id1 + ; STA icon_buf,X + ; BNE NoFinalTurnIn + ; NoTwoPartItem: + ; LDA event_flag + ; BEQ NoTwoPartTurnIn + ; LDA icon_id2 + ; STA icon_buf,X + ; JSR CheckGameEventFlag ; Y still set + ; BCC NoFinalTurnIn + ; LDA #FILLEDCH + ; BNE TwoPartCheckBox + ; NoFinalTurnIn: + ; LDA #EMPTYCH + ; TwoPartCheckBox: + ; STA icon_buf+30,X + ; NoTwoPartTurnIn: + ; INX + ; RTS + ; (36 bytes) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; CheckPassiveItem +;; +;; Check item that +;; opens a path without turn-in +;; +;; applies to these items: +;; key +;; oxyale +;; chime +;; cube +;; +;; in: +;; A: item flag +;; X: pointer to icon_buf location +;; Y: item icon +;; out: +;; X: incremented icon_buf pointer +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +CheckPassiveItem: + CMP #0 + BEQ NoPassiveItem + TYA ; STY with X index only works on zero page + STA icon_buf,X + NoPassiveItem: + INX + RTS + ; (10 bytes) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; CheckNPC +;; +;; Checks a Key Item NPC +;; +;; Applies to: +;; Sara +;; King +;; Bikke +;; Canoe Sage +;; Sarda +;; Robot +;; +;; in: +;; A: requirement for NPC to give item +;; X: pointer to icon_buf location +;; Y: NPC id +;; icon_id1: NPC icon +;; out: +;; X: incremented icon_buf pointer +;; +;; setup: +;; +;; LDA #ITEMICON +;; STA icon_id1 +;; LDA ;; logic to determine whether requirement met +;; LDY #OBJID +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +CheckNPC: + CMP #0 + BEQ NoNPC + LDA icon_id1 + STA icon_buf,X + JSR CheckGameEventFlag + BCC NoTalkNPC + LDA #FILLEDCH + BNE NPCCheckbox + NoTalkNPC: + LDA #EMPTYCH + NPCCheckbox: + STA icon_buf+30,x + NoNPC: + INX + RTS + ; (26 bytes) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Drawing Routines +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +DrawIconsToItemMenu: + LDA #0 + STA $2001 + LDX #$00 + LDA #30 + STA tmp + JSR DrawIcons + INC dest_y + JSR CoordToNTAddr + LDA #30 + STA tmp + TAX + JSR DrawIcons + LDA #SOURCE_BANK + JMP SwapPRG ;;; END ITEM MENU TRACKER + ; 29 bytes + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Draw Icons +;; +;; in: tmp = number of icons to draw +;; X = index in icon_buf +;; out: X = index in icon_buf we ended at -- can be reused +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +DrawIcons: + LDA icon_buf,X + LDY $2002 + LDY ppu_dest+1 + STY $2006 + LDY ppu_dest + STY $2006 + STA $2007 + INX + INC ppu_dest + DEC tmp + BNE DrawIcons + RTS + ; 27 bytes + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + + diff --git a/FF1Lib/asm/1B_A400_NewDrawShardBox_MainMenuTracker.asm b/FF1Lib/asm/1B_A400_NewDrawShardBox_MainMenuTracker.asm new file mode 100644 index 000000000..7f8bcce6e --- /dev/null +++ b/FF1Lib/asm/1B_A400_NewDrawShardBox_MainMenuTracker.asm @@ -0,0 +1,190 @@ +tmp = $10 +event_flag = tmp+$0D +icon_id1 = tmp+$0E +icon_id2 = tmp+$0F +dest_x = $3A +dest_y = $3B +ppu_dest = $54 +display_buf = $6E10 +unsram = $6000 ; $400 bytes +ship_vis = unsram+$00 +airship_vis = unsram+$04 +bridge_vis = unsram+$08 +canal_vis = unsram+$0C +has_canoe = unsram+$12 ; (not to be confused with item_canoe) + +items = unsram+$20 + +item_lute = items+$01 +item_crown = items+$02 +item_crystal = items+$03 +item_herb = items+$04 +item_mystickey = items+$05 +item_tnt = items+$06 +item_adamant = items+$07 +item_slab = items+$08 +item_ruby = items+$09 +item_rod = items+$0A +item_floater = items+$0B +item_chime = items+$0C +item_tail = items+$0D +item_cube = items+$0E +item_bottle = items+$0F +item_oxyale = items+$10 +orb_earth = items+$11 +orb_fire = items+$12 +orb_water = items+$13 +orb_air = items+$14 +shards = items+$15 + +game_flags = unsram+$200 + + + + +DrawMainItemBox = $B8EF ; bank 0E + +CoordToNTAddr = $DCAB ; bank 1F +MenuCondStall = $E12E ; bank 1F +SwapPRG = $FE03 ; bnak 1F + +SOURCE_BANK = $0E +DEST_BANK = $1B + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; New DrawShardBox routine +;; +;; This has to work well with Deep Dungeon, which also makes changes to the orb box. +;; In the Deep Dungeon asm, it calls the DrawOrbBox routine before making changes. +;; Therefore, this needs to be called from within the DrawOrbBox routine itself. +;; Randomizer needs to handle the dimensions of the box and the location of Deep Dungeon's +;; floor +DrawShardDisplay = $A403 + + +;; redirect from bank $0E to bank $1B +.ORG $B8A5 ; bank $0E, in DrawOrbBox +LDA #>(DrawShardDisplay-1) +PHA +LDA #<(DrawShardDisplay-1) +PHA +LDA #DEST_BANK +JMP SwapPRG +NOP +NOP +NOP +NOP +NOP +NOP +NOP + + ; (18 bytes) + +.ORG $A400 ; bank $1B + +;written by randomizer +; the number of shards needed, and the two number tiles associated with them + +lut_ShardGoal: +.BYTE 24 $82 $84 + +.ORG $A403 +;; stuff from $0E overwritten by the redirect above +LDA $2002 ; reset PPU toggle +LDA #>$23C9 +STA $2006 +LDA #<$23C9 +STA $2006 ; attribute byte at $23C9 +LDA tmp+7 ; load computed attribute byte +STA $2007 ; and draw it + +;;; at this point, the orb box and orbs have been drawn, as well as the attributes bytes for lit orbs. + +;; now we assemble the display tiles to the buffer. Could use game's string routines, but those are overkill for this little +;; bit. Printing 2-digit numbers is in bank $0E, but that's a minefield and it's easier to just do it here. +LDX #0 +LDA #$63 ; the shard tile +STA display_buf,X +INX +LDY #0 +LDA shards ;; load the current number of collected shards +CMP #10 +BCS TwoDigits + LDA #$FF ; blank tile + STA display_buf,X + INX + LDA shards + JMP OnesPlace +TwoDigits: + INY + SEC + SBC #10 + CMP #10 + BCS TwoDigits + PHA + TYA + ORA #$80 + STA display_buf,X + INX + PLA +OnesPlace: + ORA #$80 + STA display_buf,X + INX + ;;; now the slash, and the tiles stored in the lut above + LDA #$7A ; slash + STA display_buf,X + INX + LDA lut_ShardGoal+1 + STA display_buf,X + INX + LDA lut_ShardGoal+2 + STA display_buf,X + ;; draw + LDA #$03 + STA dest_x + LDA #$02 + STA dest_y + JSR CoordToNTAddr + LDX #0 + LDA #6 ; 6 tiles to draw + STA tmp +DrawShardTiles: + LDA display_buf,X + LDY $2002 ; reset PPU toggle + LDY ppu_dest+1 + STY $2006 + LDY ppu_dest + STY $2006 + STA $2007 + INX + INC ppu_dest + DEC tmp + BNE DrawShardTiles +; draw attribute bytes +LDA #%00111111 ; lower right metatile has palette 0; all others have palette 3 +LDY $2002 ; reset PPU toggle +LDY #>$23C0 ; enter address for this attribute byte +STY $2006 +LDY #<$23C0 +STY $2006 +STA $2007 +;; check if we've met goal +LDA shards +CMP lut_ShardGoal +BCC GoalNotMet ; if goal not met, we don't need to do anything. Otherwise... + LDA #%11001111 ; lower left metatile has palette 0; all others have palette 3 + LDY $2002 ; reset PPU toggle + LDY #>$23C1 ; enter address for this attribute byte + STY $2006 + LDY #<$23C1 + STY $2006 + STA $2007 +GoalNotMet: +;; swap to bank $0E and return from JSR DrawOrbBox +LDA #SOURCE_BANK +JMP SwapPRG + + diff --git a/FF1Lib/imagedata/icons/orbs_shards.png b/FF1Lib/imagedata/icons/orbs_shards.png new file mode 100644 index 000000000..e2c259e71 Binary files /dev/null and b/FF1Lib/imagedata/icons/orbs_shards.png differ diff --git a/FF1Lib/imagedata/icons/tracker_icons.png b/FF1Lib/imagedata/icons/tracker_icons.png new file mode 100644 index 000000000..0df6a0fc6 Binary files /dev/null and b/FF1Lib/imagedata/icons/tracker_icons.png differ