shithub: zelda3

Download patch

ref: e6e88979ca7fe9ab4bfd55324450bbb88c72fd46
parent: d113951d387132a75f6350c9c4574a6fa57c010c
author: Snesrev <snesrev@protonmail.com>
date: Sat Sep 17 12:14:27 EDT 2022

L/R for item switching, and reordering of inventory

Hold Y and press arrows to reorder inventory items.

--- a/README.md
+++ b/README.md
@@ -14,8 +14,20 @@
 
 I got much assistance from spannierism's Zelda 3 JP disassembly and the other ones that documented loads of function names and variables.
 
-The game also supports enhanced aspect ratios of 16:9 or 16:10 (see ExtendedAspectRatio in zelda3.ini). It also supports MSU higher quality music soundtracks.
+## Additional features
 
+Some features have been added that are not supported by the original game.
+
+Support for MSU audio tracks.
+
+Support for enhanced aspect ratios of 16:9 or 16:10.
+
+Switching current item with L/R keys.
+
+Reordering of inventory by pressing Y+Arrows.
+
+Higher quality map screen.
+
 ## Dependencies
 
 - the `libsdl2-dev` library
@@ -77,8 +89,8 @@
 | B      | Z           |
 | X      | S           |
 | Y      | A           |
-| L      | D           |
-| R      | C           |
+| L      | C           |
+| R      | V           |
 
 The keys can be reconfigured in zelda3.ini
 
--- a/config.c
+++ b/config.c
@@ -22,7 +22,7 @@
 #define N 0
 static const uint16 kDefaultKbdControls[kKeys_Total] = {
   // Controls
-  _(SDLK_UP), _(SDLK_DOWN), _(SDLK_LEFT), _(SDLK_RIGHT), _(SDLK_RSHIFT), _(SDLK_RETURN), _(SDLK_x), _(SDLK_z), _(SDLK_s), _(SDLK_a), _(SDLK_d), _(SDLK_c),
+  _(SDLK_UP), _(SDLK_DOWN), _(SDLK_LEFT), _(SDLK_RIGHT), _(SDLK_RSHIFT), _(SDLK_RETURN), _(SDLK_x), _(SDLK_z), _(SDLK_s), _(SDLK_a), _(SDLK_c), _(SDLK_v),
   // LoadState
   _(SDLK_F1), _(SDLK_F2), _(SDLK_F3), _(SDLK_F4), _(SDLK_F5), _(SDLK_F6), _(SDLK_F7), _(SDLK_F8), _(SDLK_F9), _(SDLK_F10), N, N, N, N, N, N, N, N, N, N,
   // SaveState
@@ -194,6 +194,8 @@
     return 2;
   if (StringEqualsNoCase(s, "[General]"))
     return 3;
+  if (StringEqualsNoCase(s, "[Features]"))
+    return 4;
   return -1;
 }
 
@@ -234,7 +236,7 @@
       g_config.audio_samples = (uint16)strtol(value, (char**)NULL, 10);
       return true;
     } else if (StringEqualsNoCase(key, "EnableMSU")) {
-      g_config.enable_msu = (uint16)strtol(value, (char **)NULL, 10);
+      g_config.enable_msu = (bool)strtol(value, (char **)NULL, 10);
       return true;
     }
   } else if (section == 3) {
@@ -261,7 +263,11 @@
       g_config.display_perf_title = (bool)strtol(value, (char**)NULL, 10);
       return true;
     }
-
+  } else if (section == 4) {
+    if (StringEqualsNoCase(key, "ItemSwitchLR")) {
+      g_config.item_switch_lr = (bool)strtol(value, (char **)NULL, 10);
+      return true;
+    }
   }
   return false;
 }
--- a/config.h
+++ b/config.h
@@ -46,6 +46,7 @@
   bool extended_aspect_ratio_nospr;
   bool display_perf_title;
   bool enable_msu;
+  bool item_switch_lr;
 } Config;
 
 extern Config g_config;
--- a/dungeon.c
+++ b/dungeon.c
@@ -6605,6 +6605,7 @@
       main_module_index = 14;
       return;
     }
+    Hud_HandleItemSwitchInputs();
   }
   Link_Main();
 }
--- a/hud.c
+++ b/hud.c
@@ -19,11 +19,10 @@
 const uint8 kMaxArrowsForLevel[] = { 30, 35, 40, 45, 50, 55, 60, 70 };
 static const uint8 kMaxHealthForLevel[] = { 9, 9, 9, 9, 9, 9, 9, 9, 17, 17, 17, 17, 17, 17, 17, 25, 25, 25, 25, 25, 25 };
 static const uint16 kHudItemInVramPtr[20] = {
-  0x11c8, 0x11ce, 0x11d4, 0x11da,
-  0x11e0, 0x1288, 0x128e, 0x1294,
-  0x129a, 0x12a0, 0x1348, 0x134e,
-  0x1354, 0x135a, 0x1360, 0x1408,
-  0x140e, 0x1414, 0x141a, 0x1420,
+  0x11c8, 0x11ce, 0x11d4, 0x11da, 0x11e0,
+  0x1288, 0x128e, 0x1294, 0x129a, 0x12a0,
+  0x1348, 0x134e, 0x1354, 0x135a, 0x1360,
+  0x1408, 0x140e, 0x1414, 0x141a, 0x1420,
 };
 static const uint16 kHudBottlesGfx[128] = {
   0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x255c, 0x2564, 0x2562, 0x2557, 0x2561, 0x255e, 0x255e, 0x255c,
@@ -349,6 +348,10 @@
 };
 static const uint16 kDungFloorIndicator_Gfx0[11] = { 0x2508, 0x2509, 0x2509, 0x250a, 0x250b, 0x250c, 0x250d, 0x251d, 0xe51c, 0x250e, 0x7f };
 static const uint16 kDungFloorIndicator_Gfx1[11] = { 0x2518, 0x2519, 0xa509, 0x251a, 0x251b, 0x251c, 0x2518, 0xa51d, 0xe50c, 0xa50e, 0x7f };
+
+static int Hud_GetCurrentItemPosition();
+static void Hud_ReorderItem(int direction);
+
 void Hud_RefreshIcon() {
   Hud_SearchForEquippedItem();
   Hud_UpdateHud();
@@ -374,14 +377,35 @@
   }
 }
 
+// Returns the zero based index of the currently selected hud item
+static int Hud_GetCurrentItemPosition() {
+  if (hud_inventory_order[0] != 0) {
+    int i = 0;
+    for (; i < 19 && hud_inventory_order[i] != hud_cur_item; i++) {}
+    return i;
+  } else {
+    return hud_cur_item ? hud_cur_item - 1 : hud_cur_item;
+  }
+}
+
 void Hud_GotoPrevItem() {
-  if (--hud_cur_item < 1)
-    hud_cur_item = 20;
+  if (hud_inventory_order[0] != 0) {
+    int i = Hud_GetCurrentItemPosition();
+    hud_cur_item = hud_inventory_order[i == 0 ? 19 : i - 1];
+  } else {
+    if (--hud_cur_item < 1)
+      hud_cur_item = 20;
+  }
 }
 
 void Hud_GotoNextItem() {
-  if (++hud_cur_item >= 21)
-    hud_cur_item = 1;
+  if (hud_inventory_order[0] != 0) {
+    int i = Hud_GetCurrentItemPosition();
+    hud_cur_item = hud_inventory_order[i >= 19 ? 0 : i + 1];
+  } else {
+    if (++hud_cur_item >= 21)
+      hud_cur_item = 1;
+  }
 }
 
 void Hud_FloorIndicator() {  // 8afd0c
@@ -698,7 +722,17 @@
     return;
   }
 
-  if (!BYTE(tmp1)) {
+  if (joypad1H_last & 0x40 && enhanced_features0 & kFeatures0_SwitchLR) {
+    if (filtered_joypad_H & 8) {
+      Hud_ReorderItem(-5);
+    } else if (filtered_joypad_H & 4) {
+      Hud_ReorderItem(5);
+    } else if (filtered_joypad_H & 2) {
+      Hud_ReorderItem(-1);
+    } else if (filtered_joypad_H & 1) {
+      Hud_ReorderItem(1);
+    }
+  } else if (!BYTE(tmp1)) {
     uint16 old_item = hud_cur_item;
     if (filtered_joypad_H & 8) {
       Hud_EquipItemAbove();
@@ -845,9 +879,11 @@
   Hud_DrawItem(0x1472, &kHudItemBottles[link_bottle_info[1]]);
   Hud_DrawItem(0x1572, &kHudItemBottles[link_bottle_info[2]]);
   Hud_DrawItem(0x1672, &kHudItemBottles[link_bottle_info[3]]);
-  Hud_DrawItem(0x1408, &kHudItemBottles[link_item_bottles ? link_bottle_info[link_item_bottles - 1] : 0]);
+  
+  int bottle_vram_pos = kHudItemInVramPtr[Hud_GetCurrentItemPosition()];
+  Hud_DrawItem(bottle_vram_pos, &kHudItemBottles[link_item_bottles ? link_bottle_info[link_item_bottles - 1] : 0]);
 
-  uint16 *p = (uint16 *)&g_ram[kHudItemInVramPtr[hud_cur_item - 1]];
+  uint16 *p = (uint16 *)&g_ram[bottle_vram_pos];
   uvram_screen.row[6].col[25] = p[0];
   uvram_screen.row[6].col[26] = p[1];
   uvram_screen.row[7].col[25] = p[32];
@@ -926,7 +962,6 @@
 
   if (or_all == 0) {
     hud_cur_item = 0;
-    hud_cur_item_hi = 0;
     hud_var1 = 0;
   } else {
     if (!hud_cur_item)
@@ -968,26 +1003,33 @@
   uvram_screen.row[5].col[3] = 0x246E;
   uvram_screen.row[5].col[4] = 0x246F;
 
-  Hud_DrawItem(0x11c8, &kHudItemBow[link_item_bow]);
-  Hud_DrawItem(0x11ce, &kHudItemBoomerang[link_item_boomerang]);
-  Hud_DrawItem(0x11d4, &kHudItemHookshot[link_item_hookshot]);
-  Hud_DrawItem(0x11da, &kHudItemBombs[link_item_bombs ? 1 : 0]);
-  Hud_DrawItem(0x11e0, &kHudItemMushroom[link_item_mushroom]);
-  Hud_DrawItem(0x1288, &kHudItemFireRod[link_item_fire_rod]);
-  Hud_DrawItem(0x128e, &kHudItemIceRod[link_item_ice_rod]);
-  Hud_DrawItem(0x1294, &kHudItemBombos[link_item_bombos_medallion]);
-  Hud_DrawItem(0x129a, &kHudItemEther[link_item_ether_medallion]);
-  Hud_DrawItem(0x12a0, &kHudItemQuake[link_item_quake_medallion]);
-  Hud_DrawItem(0x1348, &kHudItemTorch[link_item_torch]);
-  Hud_DrawItem(0x134e, &kHudItemHammer[link_item_hammer]);
-  Hud_DrawItem(0x1354, &kHudItemFlute[link_item_flute]);
-  Hud_DrawItem(0x135a, &kHudItemBugNet[link_item_bug_net]);
-  Hud_DrawItem(0x1360, &kHudItemBookMudora[link_item_book_of_mudora]);
-  Hud_DrawItem(0x1408, &kHudItemBottles[link_item_bottles ? link_bottle_info[link_item_bottles - 1] : 0]);
-  Hud_DrawItem(0x140e, &kHudItemCaneSomaria[link_item_cane_somaria]);
-  Hud_DrawItem(0x1414, &kHudItemCaneByrna[link_item_cane_byrna]);
-  Hud_DrawItem(0x141a, &kHudItemCape[link_item_cape]);
-  Hud_DrawItem(0x1420, &kHudItemMirror[link_item_mirror]);
+  const ItemBoxGfx *item_box_gfxs[] = {
+    &kHudItemBow[link_item_bow],
+    &kHudItemBoomerang[link_item_boomerang],
+    &kHudItemHookshot[link_item_hookshot],
+    &kHudItemBombs[link_item_bombs ? 1 : 0],
+    &kHudItemMushroom[link_item_mushroom],
+    &kHudItemFireRod[link_item_fire_rod],
+    &kHudItemIceRod[link_item_ice_rod],
+    &kHudItemBombos[link_item_bombos_medallion],
+    &kHudItemEther[link_item_ether_medallion],
+    &kHudItemQuake[link_item_quake_medallion],
+    &kHudItemTorch[link_item_torch],
+    &kHudItemHammer[link_item_hammer],
+    &kHudItemFlute[link_item_flute],
+    &kHudItemBugNet[link_item_bug_net],
+    &kHudItemBookMudora[link_item_book_of_mudora],
+    &kHudItemBottles[link_item_bottles ? link_bottle_info[link_item_bottles - 1] : 0],
+    &kHudItemCaneSomaria[link_item_cane_somaria],
+    &kHudItemCaneByrna[link_item_cane_byrna],
+    &kHudItemCape[link_item_cape],
+    &kHudItemMirror[link_item_mirror],
+  };
+
+  for (int i = 0; i < 20; i++) {
+    int j = hud_inventory_order[i];
+    Hud_DrawItem(kHudItemInVramPtr[i], item_box_gfxs[j == 0 ? i: j - 1]);
+  }
 }
 
 void Hud_DrawUnknownBox(uint16 palmask) {  // 8de647
@@ -1151,7 +1193,7 @@
 }
 
 void Hud_DrawSelectedYButtonItem() {  // 8deb3a
-  uint16 *p = (uint16 *)&g_ram[kHudItemInVramPtr[hud_cur_item - 1]];
+  uint16 *p = (uint16 *)&g_ram[kHudItemInVramPtr[Hud_GetCurrentItemPosition()]];
   uvram_screen.row[6].col[25] = p[0];
   uvram_screen.row[6].col[26] = p[1];
   uvram_screen.row[7].col[25] = p[32];
@@ -1495,4 +1537,45 @@
 
 const uint16 *Hud_GetItemBoxPtr(int item) {
   return kHudItemBoxGfxPtrs[item]->v;
+}
+
+void Hud_HandleItemSwitchInputs() {
+  if (filtered_joypad_L & (0x20 | 0x10)) {  // left/right shoulder
+    int old_item = hud_cur_item;
+    for (int i = 0; ; i++) {
+      if (i >= 20) {
+        hud_cur_item = 0;
+        break;
+      }
+      if (filtered_joypad_L & 0x20)
+        Hud_GotoPrevItem();
+      else
+        Hud_GotoNextItem();
+      if (Hud_DoWeHaveThisItem())
+        break;
+    }
+    if (hud_cur_item != old_item) {
+      sound_effect_2 = 32;
+      Hud_UpdateEquippedItem();
+      Hud_UpdateItemBox();
+    }
+  }
+}
+
+void Hud_ReorderItem(int direction) {
+  // Initialize inventory order on first use
+  if (hud_inventory_order[0] == 0) {
+    for (int i = 0; i < 24; i++)
+      hud_inventory_order[i] = i + 1;
+  }
+  int old_pos = Hud_GetCurrentItemPosition(), new_pos = old_pos + direction;
+  if (new_pos < 0)
+    new_pos += 20;
+  else if (new_pos >= 20)
+    new_pos -= 20;
+  uint8 t = hud_inventory_order[old_pos];
+  hud_inventory_order[old_pos] = hud_inventory_order[new_pos];
+  hud_inventory_order[new_pos] = t;
+  Hud_DrawYButtonItems(Hud_GetPaletteMask(1));
+  sound_effect_2 = 32;
 }
--- a/hud.h
+++ b/hud.h
@@ -61,4 +61,6 @@
 void Hud_Update_IgnoreItemBox();
 void Hud_Update_IgnoreHealth();
 void Hud_UpdateHearts(uint16 *dst, const uint16 *src, int n);
-const uint16 *Hud_GetItemBoxPtr(int item);
\ No newline at end of file
+const uint16 *Hud_GetItemBoxPtr(int item);
+
+void Hud_HandleItemSwitchInputs();
--- a/main.c
+++ b/main.c
@@ -182,7 +182,16 @@
   ZeldaInitialize();
   g_zenv.ppu->extraLeftRight = UintMin(g_config.extended_aspect_ratio, kPpuExtraLeftRight);
   g_snes_width = 2 * (g_config.extended_aspect_ratio * 2 + 256);
-  g_wanted_zelda_features = (g_zenv.ppu->extraLeftRight && !g_config.extended_aspect_ratio_nospr) ? kFeatures0_ExtendScreen64 : 0;
+
+
+  // Delay actually setting those features in ram until any snapshots finish playing.
+  {
+    uint32 f = 0;
+    f |= (g_zenv.ppu->extraLeftRight && !g_config.extended_aspect_ratio_nospr) ? kFeatures0_ExtendScreen64 : 0;
+    f |= g_config.item_switch_lr * kFeatures0_SwitchLR;
+    g_wanted_zelda_features = f;
+  }
+
   g_ppu_render_flags = g_config.new_renderer * kPpuRenderFlags_NewRenderer | g_config.enhanced_mode7 * kPpuRenderFlags_4x4Mode7;
   msu_enabled = g_config.enable_msu;
 
--- a/overworld.c
+++ b/overworld.c
@@ -775,6 +775,7 @@
       main_module_index = 14;
       return;
     }
+    Hud_HandleItemSwitchInputs();
   }
   if (trigger_special_entrance)
     Overworld_AnimateEntrance();
--- a/variables.h
+++ b/variables.h
@@ -184,7 +184,7 @@
 #define stk_return_addr (*(uint16*)(g_ram+0x1FE))
 #define overworld_map_state (*(uint8*)(g_ram+0x200))
 #define hud_cur_item (*(uint8*)(g_ram+0x202))
-#define hud_cur_item_hi (*(uint8*)(g_ram+0x203))
+
 #define hud_var1 (*(uint8*)(g_ram+0x204))
 #define byte_7E0205 (*(uint8*)(g_ram+0x205))
 #define byte_7E0206 (*(uint8*)(g_ram+0x206))
--- a/zelda3.ini
+++ b/zelda3.ini
@@ -8,6 +8,7 @@
 # replays will be incompatible
 ExtendedAspectRatio = 4:3
 
+
 [Graphics]
 # Fullscreen mode (0=windowed, 1=desktop fullscreen, 2=fullscreen w/mode change)
 Fullscreen = 0
@@ -31,12 +32,17 @@
 EnableMSU = 0
 
 
+[Features]
+# Item switch on L/R. Also allows reordering of items in inventory by pressing Y+direction
+ItemSwitchLR = 1
+
+
 [KeyMap]
 # Change what keyboard keys map to the joypad
 # Order: Up, Down, Left, Right, Select, Start, A, B, X, Y, L, R
 
 # This default is suitable for QWERTY keyboards.
-Controls = Up, Down, Left, Right, Right Shift, Return, x, z, s, a, d, c
+Controls = Up, Down, Left, Right, Right Shift, Return, x, z, s, a, c, v
 
 # This default is suitable for QWERTZ keyboards.
 #Controls = Up, Down, Left, Right, Right Shift, Return, x, y, s, a, d, c
--- a/zelda_rtl.c
+++ b/zelda_rtl.c
@@ -339,10 +339,6 @@
   1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
 };
 
-#define msu_curr_sample (*(uint32*)(g_ram+0x650))
-#define msu_volume (*(uint8*)(g_ram+0x654))
-#define msu_track (*(uint8*)(g_ram+0x655))
-
 bool ZeldaIsMusicPlaying() {
   if (msu_track) {
     return msu_file != NULL;
--- a/zelda_rtl.h
+++ b/zelda_rtl.h
@@ -103,10 +103,6 @@
   kRam_CrystalRotateCounter = 0x649,
   kRam_BugsFixed = 0x64a,
   kRam_Features0 = 0x64c,
-
-  // 4 bytes holding the current msu playback sample, then 2 more more msu misc
-  kRam_MsuCurrSample = 0x650,
-
 };
 
 enum {
@@ -114,10 +110,20 @@
   kBugFix_PolyRenderer = 1,
   kBugFix_AncillaOverwrites = 1,
   kBugFix_Latest = 1,
+};
 
-  // kRam_Features0
+// Enum values for kRam_Features0
+enum {
   kFeatures0_ExtendScreen64 = 1,
+  kFeatures0_SwitchLR = 2,
 };
+
+#define enhanced_features0 (*(uint32*)(g_ram+0x64c))
+#define msu_curr_sample (*(uint32*)(g_ram+0x650))
+#define msu_volume (*(uint8*)(g_ram+0x654))
+#define msu_track (*(uint8*)(g_ram+0x655))
+
+#define hud_inventory_order ((uint8*)(g_ram + 0x225)) // 4x6 bytes
 
 extern uint32 g_wanted_zelda_features;