diff --git a/INSTALL.md b/INSTALL.md index e04a70c8..830e76a4 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,11 +1,13 @@ ## Installing gz The currently supported games are: -- The Legend of Zelda: Ocarina of Time (NTSC Version 1.0) -- The Legend of Zelda: Ocarina of Time (NTSC Version 1.2) +- The Legend of Zelda: Ocarina of Time (NTSC-U/J 1.0) +- The Legend of Zelda: Ocarina of Time (NTSC-U/J 1.1) +- The Legend of Zelda: Ocarina of Time (NTSC-U/J 1.2) There are two methods of using this software, described below. ### Using gz with a Gameshark +*Note: Gameshark support has been deprecated. It is still provided, but untested and without guarantees.* You will need: - An N64 with a supported game cartridge, and an Expansion Pak. - A Gameshark with a functional parallel port (note that some 3.3s only have dummy ports). @@ -15,9 +17,9 @@ You will need: Follow these steps; -1. Boot the Gameshark with your game cartridge. If your game requires a special keycode to boot, you'll first - need to boot the Gameshark with a game that works with the default keycode, select the the required keycode in - the Key Codes menu, and then reboot with the game you wish to use with gz. +1. Boot the Gameshark with your game cartridge. If your game requires a special keycode to boot, + you'll first need to boot the Gameshark with a game that works with the default keycode, + select the the required keycode in the Key Codes menu, and then reboot with the game you wish to use with gz. 2. In the Select Cheat Codes menu, select your game and make sure the `(M)` code is active, if one exists. 3. In the Start Game submenu, enable the Code Generator option, and select Start Game With Selected Codes. 4. Connect the Gameshark to your computer with your Parallel to USB adapter cable. @@ -29,8 +31,9 @@ Follow these steps; 8. When the upload is completed, you can disconnect the USB cable and start playing. ### Using gz with an emulator / flash cart -Drag and drop the rom you wish to patch onto the `patch.bat` script. A patched rom will be -created in the same directory as the script. The patched rom can be played with an emulator or transferred to a flash cart. +Drag and drop the rom you wish to patch onto the `patch.bat` script. +A patched rom will be created in the same directory as the script. +The patched rom can be played with an emulator or transferred to a flash cart. For emulator usage, you will need to enable Expansion Pak emulation. On some emulators, you may need to change CPU Core Style to Interpreter. For use with a flash cart, your N64 will need an Expansion Pak. diff --git a/README.md b/README.md index 5abfe0d3..bc0e9d6f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ ## About -This is a trainer / practice / testing utility for The Legend of Zelda: Ocarina of Time. +This is a trainer / practice / testing utility for The Legend of Zelda: Ocarina of Time, +formally known by its codename **gz**, but generally referred to as _"the practice rom"_. ## Download The latest release can be found on the [releases page](https://github.com/glankk/gz/releases). diff --git a/USAGE.md b/USAGE.md index fdf0f117..ba28b552 100644 --- a/USAGE.md +++ b/USAGE.md @@ -1,58 +1,279 @@ ## User manual -The bottom of the screen shows an input display. The two numbers are the x and y coordinate of the analog stick. -The buttons on the controller are represented by letters that appear when a button is pressed. -Press L to bring up the utility menu. Use the D-Pad to navigate the menu, and L to make a selection. - -The following builtin button-activated commands are available -- Z + L to levitate. -- Z + D-Pad left to save link's position. -- Z + D-Pad right to load link's position. -- Z + D-Pad down, hold to reload the current area. - Note that opening a file from the file select menu will not update the last entrance, so using this - command before loading another area will take you to the default entrance. -- Z + D-Pad up, hold to go to the file select menu. - -These commands are disabled while using the pause / frame advance feature. - -### Inventory -The inventory submenu allows you to modify all parts of your inventory. Most of the amount / capacity modifiers -are specified in hexadecimal (h) for simplicity. As such, every 10h of energy capacity corresponds to one full heart. -For defense hearts, 14h is the default for double defense. For magic capacity, 0h is the default, 1h is double magic. -For heart pieces, every 10h corresponds to one heart piece. -Equipment upgrades that are not normally obtainable without cheats or glitches are denoted with an asterisk. -For dungeon keys, ffh means the key amount will not be visible. Note that not all dungeon items are available in some -dungeons, and therefore modifying them will not have any effect. - -### Equips -The equips submenu lets you modify what equipment is selected on the equipment screen on the pause menu. -In order for changed equips to take effect, you must pause and unpause. -For the B and c button modifiers, ffh means the button is blank. Other values correspond to their respective items. -The B button can only be blank if the swordless flag is set. -A list of item indices can be found [here](http://wiki.cloudmodding.com/oot/Item_List). - -### Misc -The misc submenu has various uncategorized options. -The `reset all gs` option will restore all gold skulltulas that have been killed, everywhere. -The `clear / set scene flags` option will change all permanent and temporary scene flags which keep track of things -like which chests have been opened, which items have been collected, which enemies have been defeated etc. -The `teleport slot` lets you store several positions with the Z + D-Pad left / right commands. -Furhermore there is language selection, pausing, and a frame advance feature. - -### Cheats -The cheats submenu lets you toggle the builtin cheats on and off. -Energy / magic / item amounts / rupees will be set to your current maximum capacity. -If an item capacity is at zero, the amount will be set to one. -`nayru's love` will prevent nayru's love from expiring, when active. If nayru's love is not active when -this cheat is enabled, going to a new area will activate it. -`advance time` will speed up the flow of daytime when enabled, even in areas where time does not normally pass. - -### Warps -The `warp` option will go to the specified entrance, with link at the specified age. -Note that the entrance index is subject to age/daytime/cutscene modifiers, which may give unexpected results. -Generally, during normal gameplay, selecting the first in a quad of entrances will always give the desired result. -The `clear cutscene pointer` is useful for preventing certain wrong warps from crashing. -A list of entrance indices and where they go can be found [here](http://wiki.cloudmodding.com/oot/Entrance_Table_(Data)). - -### Watches -The watches submenu provides a minimal memory watch utility. -Select `+` to add a new watch. Enter an address and a data type, it's value will be displayed to the right. +The main interface for accessing the provided tools is the utility menu. +By default, this menu is brought up by pressing `R + L`, +but this button combination can be changed (See [Settings](#settings-menu)). +Use the D-Pad to navigate the menu, and L to make a selection. +For a description of each of the submenus, see their respective section below. + +Beyond the tools provided by the utility menu there is also; +- **An input display.** The two numbers represent the x and y coordinate of the control stick. +The button icons that appear represent the buttons that are pressed on the controller. +*Enabled by default.* +- **A lag counter.** Displays lag by subtracting the number of game frames +passed from the game's vertical interrupt counter. +Displayed in units of frames (60Hz, default), or seconds. *Disabled by default.* +- **A timer.** Measures real-time using the CPU counter. *Disabled by default.* + +These features can be configured from the settings menu (See [Settings](#settings-menu)). + +### Warps menu +The **places** menu provides a list of all scenes and their respective entrances, +grouped into eight categories. +Selecting a scene with multiple entrances will show a list of all entrances for that scene. +Selecting an entrance will instantly warp you to that entrance. +Scenes with only one entrance will warp you to that entrance when selected, +without showing an entrance list. +If you want to warp to a specific entrance index, +you can enter that index in the warps menu and select **warp**. +The **age** and **cutscene** options specify which age link will be at when performing a +warp, and which cutscene should be played for that scene. +These options apply to both the places menu and warping using an entrance index. + +**clear cs pointer** will point the cutscene pointer to an empty cutscene, +which is useful for preventing certain wrong warps from crashing. +The bottom of the warps menu shows information about the game's current warp parameters. + +### Scene menu +Selecting **explorer** will bring up the scene explorer, +which shows an overlay of the current scene. +Use the D-Pad up and down to navigate forwards and backwards through the scene, and +D-Pad left and right to rotate the view. +While holding Z, the D-Pad up and down will navigate vertically through the scene, and +D-Pad left and right will move sideways. +Use the `explore prev room` and `explore next room` commands +to cycle through the rooms of the scene (bound to `R + D-Down` and `R + D-Up` by default). +Pressing L will teleport link to location and orientation of the crosshair and close the scene explorer. + +**clear flags** and **set flags** modifies the temporary and permanent flags for the current scene, +which keep track of things such as which chests have been opened, +which items have been collected, which enemies have been defeated, etc. +The **load room** option loads the room with the specified index, +if a room which such an index exists within the current scene. +If that room is already the currently loaded room, it will be unloaded. +**teleport slot** selects which position to save and load when using the +teleportation commands (this can also be bound a button combination, but is unbound by default). +The bottom of the scene menu shows information about the current scene. + +### Cheats menu +This menu allows toggling the builtin cheats on and off. +The following cheats are available: +- **energy:** Gives full health +- **magic:** Gives full magic +- **_various items:_** Sets item amounts to the current capacity of that item, +or to one if the capacity is zero or unlimited. +- **small keys:** Sets the number of small keys to one within the current dungeon, if any. +- **rupees:** Sets the rupee amount to the capacity of the current wallet. +- **nayru's love:** Prevents nayru's love from expiring, if active. +If nayru's love is not currently active, +entering a scene while this cheat is enabled will activate it. +- **freeze time:** Prevents the current time of day from advancing. +Does not affect sun's song or the time of day modifiers in the file menu. +- **no music:** Stops background music from playing. +- **items usable:** Clears all item restriction flags, +allowing all items to be used regardless of location. +- **no minimap:** Keeps the minimap hidden at all times. + +To undo the effects of the *no music*, *items usable*, and *no minimap* cheats, +turn the cheat off and enter a new scene, or reload the current scene. + +### Inventory menu +The **equipment and items** menu lets you modify your equipment, C-button items, +and passive equipment items. +There is also an option to set whether or not the Giant's Knife has been broken, +and whether or not the Biggoron's Sword has been obtained. +Pressing a bottle, trade item, or equipment item will bring up an item wheel where you can +select from the possible items for that slot. +Use the D-Pad left and right to cycle through the items, +and the D-Pad up and down to cycle three items at a time. +Capacity equipment items (e.g. quiver, bomb bag etc.) that are not normally obtainable without +using cheats or glitches are denoted with an asterisk. + +The **quest items** menu lets you modify all items on the Quest Status screen, +as well as your energy and magic. +There are also options to modify the dungeon items and small key amount of a specified dungeon. +**energy cap.**, **defense**, **magic cap.**, and **heart pieces** are specified in +hexadecimal for simplicity (from here on denoted with an *h*). +For energy capacity, 10h corresponds to one heart container. +The defense hearts is specified in number of heart containers. +For magic capacity, 0h is the normal capacity, 1h is double magic, 2h is what would be triple magic etc. +For heart pieces, 10h corresponds to one heart piece. + +The **amounts** menu lets you modify the ammo of your C-button items, +your magic and energy amount, number of hits left on Giant's Knife, and rupee amount. +The number of Giant's Knife hits left is what decides whether or not the Giant's Knife / Biggoron's Sword +appears to be broken when Link wields it. +Magic, energy, and Giant's Knife hits are specified in hexadecimal. +30h magic is the max for normal magic capacity, and 60h is the max for double magic capacity. +For energy, 10h corresponds to one heart container. +Though the highest amount of rupees that can be specified is 99999, +entering a value greater than or equal to 65536 will wrap the amount around to zero and on. + +### Equips menu +This menu lets you modify your current equips, and the items equipped to your B and C-buttons. +Pressing a piece of equipment that is already equipped will unequip it. +Unequpping a sword or equipping nothing to the B button will +automatically set the current file's swordless flag. +**_Warning:_** Unequipping boots will have strange effects, and usually cause an immediate crash. + +### File menu +The **restore skulltulas** option clears all the gold skulltula flags in the current file, +thus restoring all destroyed gold skulltulas. The gold skulltulas in the current scene are not +affected until the scene is reloaded. +**call navi** sets Navi's advice timer to a value which will make her want to talk to you. +This will not have any effect until you enter an area where Navi normally appears. +The **memory file** selects the memory file to save to and load from when using the memory file commands +(see (Settings)[#settings-menu]). +**carpenters freed**, **intro cutscenes**, and **rewards obtained** let's you set and clear +various flags in the current file. +Pressing the checkmark will set the pertinent flags such that they have been "completed", +and the cross will clear them (setting them to the state that they were in when the file was created). + +The **timer 1** and **timer 2** options display and modify the state of the two types of timers in the game. +The first timer is used for hot rooms and races, and the second timer is used for trade items +and the Castle Collapse sequence. +The numbers represent the number of seconds left on the timers, +and the options show the current state of the timers. Both of these can be modified manually. +*Note:* When the first timer is set to a heat state, +the game will instantly deactivate it if the current room is not "hot". +**_Warning:_** Modifying the state of the timers can yield strange behavior. +In general, it is safest to use the the "starting" and "stopped" states when doing this. + +The **file index** option decides which file the game will be saved to when saving from +the start menu or the Game Over screen. +When the file index is set to FFh, as it is by default on the title screen, +saving will have no effect. +There's also a **language** and **z targeting** option, which apply to the current file. + +### Watches menu +This menu lets you add custom RAM watches to observe arbitrary parts of game's memory in real-time. +Pressing the plus icon will add a new watch, +and pressing the cross next to a watch will remove that watch. +After adding a watch, enter a memory address and value type to display the value at that address. +These watch types are available: +- **u8:** 8-bit value, unsigned. +- **s8:** 8-bit value, signed. +- **x8:** 8-bit value, hexadecimal. +- **u16:** 16-bit value, unsigned. +- **s16:** 16-bit value, signed. +- **x16:** 16-bit value, hexadecimal. +- **u32:** 32-bit value, unsigned. +- **s32:** 32-bit value, signed. +- **x32:** 32-bit value, hexadecimal. +- **f32:** 32-bit value, IEEE-754 floating point. + +Pressing the anchor button next to a watch will release the watch from the watches menu +so that it's always visible, even when the menu is closed. +When a watch is released, a positioning button will appear which lets you change the position +of the watch on the screen. +Holding Z when positioning the watch will move it faster. + +### Settings menu +This is where most of the functionality of gz is configured. +The **profile** option selects which profile to save and load settings to and from. +When the game starts, the settings saved to profile zero are automatically loaded, if any. +The appearance of the menu can be configured with the **font** and **drop shadow** options. +Disabling drop shadow can reduce the graphical computation impact of the menu, but may also reduce readability. +The visibility of the on-screen display elements can be configured with the **input display**, +**lag counter**, **timer**, and **pause display** options. +The screen position of the utility menu, input display, lag counter, and timer can be configured +by their respective positioning buttons. +Holding Z when positiong an element will move it faster. +The display unit of the lag counter can be set to *frames* or *seconds*. +The *break type* option decides how the *break free* command will function. +When the break type is *normal*, the command will end textboxes, certain events and traditional cutscenes. +The *aggressive* break type will cause the command to also try to reset the camera and some of Link's state flags. +**save settings** and **load settings** will save and load the current settings to the +currently selected profile. +**restore defaults** will restore all saved settings to their default values (Does not affect saved profiles). +If the saved settings were to become corrupted in such a way that they prevent the game from starting, +holding the Start button when the game is starting will load the default settings instead of loading profile zero. +The following settings are saved: +- Menu and on-screen displays appearances and settings. +- Saved positions and the currently selected position slot number. +- Watches. +- Command button binds. +- Activated cheats. +- Warp menu age, cutscene index, and entrance index. + +The **commands** menu lets you bind commands to custom button combinations and/or activate them manually. +Pressing the name of a command will activate that command, +and pressing the button combo in the right column will bind a button combo to the corresponding command. +If you want to unbind a command, press and keep holding L when starting the binding. +A button combo for any given command can contain at most four buttons. +When activating a command with a button combo, +the button combo must explicitly be input the way it appears in the commands menu. +For example, +a command with the button combo `R + A` will only be activated if you press R first and then A, +or R and A at the same time. +`A + R`, or `R + B + A` will not activate the corresponding command. + +The following commands are available: +- **show/hide menu**: Opens the utility menu if it's closed, closes it if it's opened. +*Default: `R + L`* +- **return from menu:** Returns to the previous menu, as if the *return* button was pressed. +*Default: `R + D-Left`* +- **break free:** Attempts to break any effect that removes control of Link. +*Default: `C-Up + L`* +- **levitate:** The classic L to levitate command. +*Default: `L`* +- **save position:** Saves Link's current position and orientation to the current position slot. +*Default: `D-Left`* +- **load position:** Teleports Link to the position in the current position slot. +*Default: `D-Right`* +- **save memfile:** Saves the current state of the game to the memory file in the current memory file slot. +Everything that would be saved when saving the game normally is saved to the memory file. +*Default: `R + D-Left`* +- **load memfile:** Loads the state of the current memory file. +*Default: `R + D-Right`* +- **reset lag counter:** Resets the number of lag frames recorded to zero. +*Default: `R + B + D-Right`* +- **start/stop timer:** Starts the on-screen timer if it is stopped, stops it if it's running. +*Default: `R + A + D-Left`* +- **reset timer:** Sets the time of the on-screen timer to zero. +*Default: `R + B + D-Left`* +- **pause/unpause:** Pauses the gameplay, effectively freezing the state of the game. +If the game is already frozen, resumes gameplay as normal. +While the game is frozen, a pause icon will appear on the top-left of the screen +(enabled by default, can be turned off). +*Default: `D-Down`* +- **frame advance:** If the game is frozen by the pause command, advances one frame of gameplay. +Otherwise, freezes the game as if the pause command was activated. +*Default: `D-Up`* +- **file select:** Returns (or proceeds) to the game's file select menu. +*Default: `B + L`* +- **reload scene:** Reloads the current scene, starting from the last scene entrance. +*Default: `A + L`* +- **void out:** Reloads the current scene, +starting from the last room entrance as if Link voided out. +*Default: `A + B + L`* +- **turbo:** Sets Link's linear velocity to 27. +*Default: `unbound`* +- **fall:** Makes Link fall through the floor, as if there was no floor. +*Default: `unbound`* +- **toggle age:** Toggles between Adult and Child Link. Takes effect when entering a new area. +*Default: `unbound`* +- **start timer:** Starts the on-screen timer if it is stopped. +*Default: `unbound`* +- **stop timer:** Stops the on-screen timer if it is running. +*Default: `unbound`* +- **previous position:** Selects the previous position slot to be used for teleportation. +*Default: `unbound`* +- **next position:** Selects the next position slot to be used for teleportation. +*Default: `unbound`* +- **previous memfile:** Selects the previous memory file slot. +*Default: `unbound`* +- **next memfile:** Selects the next memory file slot. +*Default: `unbound`* +- **explore prev room:** Loads the previous room while using the scene explorer. +*Default: `R + D-Down`* +- **explore next room:** Loads the next room while using the scene explorer. +*Default: `R + D-Up`* + +**_Warning:_** Unbinding the *show/hide menu* or *return from menu* commands, +or binding them to a button combination that will interfere with menu navigation +can make it impossible to use the utility menu. +If this happens, you can restore the default settings by entering the following button sequence: +`D-Up D-Up D-Down D-Down D-Left D-Right D-Left D-Right B A`. + +_Note:_ Button combos that interfere with menu navigation for commands that aren't related to +menuing are disabled while the utility menu is active. diff --git a/oot-1.0/upload.bat b/oot-1.0/upload.bat index d6fdaa68..6c0d2604 100644 --- a/oot-1.0/upload.bat +++ b/oot-1.0/upload.bat @@ -1,6 +1,6 @@ @echo off pushd . cd %~dp0 -gs -w bin 0x80600000 1024 ../bin/gz/oot-1.0/gz.bin -w text text_hook.gsc -u +gs -w bin 0x80400060 1024 ../bin/gz/oot-1.0/gz.bin -w text text_hook.gsc -u popd pause diff --git a/oot-1.1/upload.bat b/oot-1.1/upload.bat index dfecf59b..21492de9 100644 --- a/oot-1.1/upload.bat +++ b/oot-1.1/upload.bat @@ -1,6 +1,6 @@ @echo off pushd . cd %~dp0 -gs -w bin 0x80600000 1024 ../bin/gz/oot-1.1/gz.bin -w text text_hook.gsc -u +gs -w bin 0x80400060 1024 ../bin/gz/oot-1.1/gz.bin -w text text_hook.gsc -u popd pause diff --git a/oot-1.2/upload.bat b/oot-1.2/upload.bat index b58e3eac..2ba11979 100644 --- a/oot-1.2/upload.bat +++ b/oot-1.2/upload.bat @@ -1,6 +1,6 @@ @echo off pushd . cd %~dp0 -gs -w bin 0x80600000 1024 ../bin/gz/oot-1.2/gz.bin -w text text_hook.gsc -u +gs -w bin 0x80400060 1024 ../bin/gz/oot-1.2/gz.bin -w text text_hook.gsc -u popd pause diff --git a/res/gz/pause_icons.png b/res/gz/pause_icons.png new file mode 100644 index 00000000..f24fd485 Binary files /dev/null and b/res/gz/pause_icons.png differ diff --git a/res/resources.json b/res/resources.json index a4729f9c..88a38741 100644 --- a/res/resources.json +++ b/res/resources.json @@ -117,6 +117,13 @@ "format": "ia8", "tile_height": 8 }, + "pause_icons.png": + { + "type": "texture", + "name": "pause_icons", + "format": "ia8", + "tile_height": 16 + }, "crosshair.png": { "type": "texture", diff --git a/src/gz/explorer.c b/src/gz/explorer.c index d532cfd1..47bd9270 100644 --- a/src/gz/explorer.c +++ b/src/gz/explorer.c @@ -8,6 +8,7 @@ #include "input.h" #include "menu.h" #include "resource.h" +#include "settings.h" #include "z64.h" #include "zu.h" @@ -190,14 +191,18 @@ static void draw_crosshair(struct menu_item *item) static int enter_proc(struct menu_item *item) { input_reserve(BUTTON_D_UP | BUTTON_D_DOWN | BUTTON_D_LEFT | BUTTON_D_RIGHT | - BUTTON_Z | BUTTON_R); + BUTTON_Z); + input_bind_set_override(COMMAND_PREVROOM, 1); + input_bind_set_override(COMMAND_NEXTROOM, 1); return 0; } static int leave_proc(struct menu_item *item) { input_free(BUTTON_D_UP | BUTTON_D_DOWN | BUTTON_D_LEFT | BUTTON_D_RIGHT | - BUTTON_Z | BUTTON_R); + BUTTON_Z); + input_bind_set_override(COMMAND_PREVROOM, 0); + input_bind_set_override(COMMAND_NEXTROOM, 0); return 0; } @@ -448,18 +453,6 @@ static int draw_proc(struct menu_item *item, return 1; } -static int navigate_proc(struct menu_item *item, enum menu_navigation nav) -{ - struct item_data *data = item->data; - if (data->state == STATE_RENDER && (input_pad() & BUTTON_R)) { - if (nav == MENU_NAVIGATE_UP) - data->room_next = (data->room_next + 1) % data->no_rooms; - else if (nav == MENU_NAVIGATE_DOWN) - data->room_next = (data->room_next + data->no_rooms - 1) % data->no_rooms; - } - return 1; -} - static int activate_proc(struct menu_item *item) { struct item_data *data = item->data; @@ -494,6 +487,21 @@ void explorer_create(struct menu *menu) item->leave_proc = leave_proc; item->think_proc = think_proc; item->draw_proc = draw_proc; - item->navigate_proc = navigate_proc; item->activate_proc = activate_proc; } + +void explorer_room_prev(struct menu *menu) +{ + struct menu_item *item = menu->items.first; + struct item_data *data = item->data; + if (data->state == STATE_RENDER) + data->room_next = (data->room_next + data->no_rooms - 1) % data->no_rooms; +} + +void explorer_room_next(struct menu *menu) +{ + struct menu_item *item = menu->items.first; + struct item_data *data = item->data; + if (data->state == STATE_RENDER) + data->room_next = (data->room_next + 1) % data->no_rooms; +} diff --git a/src/gz/explorer.h b/src/gz/explorer.h index 3cbc4879..2c6e3c37 100644 --- a/src/gz/explorer.h +++ b/src/gz/explorer.h @@ -3,5 +3,7 @@ #include "menu.h" void explorer_create(struct menu *menu); +void explorer_room_prev(struct menu *menu); +void explorer_room_next(struct menu *menu); #endif diff --git a/src/gz/gz.c b/src/gz/gz.c index 4a892775..a218653d 100644 --- a/src/gz/gz.c +++ b/src/gz/gz.c @@ -18,12 +18,13 @@ struct equipment_item_option { - int shift; - uint32_t mask; - int x; - int y; - int start; - int length; + int shift; + uint32_t mask; + int x; + int y; + int start; + int length; + const char *tooltip; }; struct equipment_item_data @@ -36,12 +37,13 @@ struct equipment_item_data struct capacity_item_option { - int shift; - int x; - int y; - int item_tile; - _Bool multi_tile; - int amount_tiles[8]; + int shift; + int x; + int y; + int item_tile; + _Bool multi_tile; + int amount_tiles[8]; + const char *tooltip; }; struct capacity_item_data @@ -97,6 +99,20 @@ struct scene_category const uint8_t *scenes; }; +enum cmdact +{ + CMDACT_HOLD, + CMDACT_PRESS, + CMDACT_PRESS_ONCE, +}; + +struct command_info +{ + const char *name; + void (*proc)(void); + enum cmdact activation_type; +}; + struct byte_option { void *data; @@ -117,8 +133,10 @@ struct memory_file uint32_t scene_flags[9]; }; +#define CPU_COUNTER_FREQ 46875000 static uint8_t profile = 0; static struct menu menu_main; +static struct menu menu_explorer; static struct menu menu_global_watches; static struct menu_item *menu_font_option; static struct menu_item *menu_watchlist; @@ -126,9 +144,10 @@ static _Bool menu_active = 0; static int32_t frames_queued = -1; static int32_t frame_counter = 0; static int32_t lag_vi_offset; +static int64_t cpu_counter = 0; static _Bool timer_active = 0; -static int32_t timer_vi_offset; -static int32_t timer_vi_prev; +static int64_t timer_counter_offset; +static int64_t timer_counter_prev; static uint16_t day_time_prev; static int target_day_time = -1; static struct memory_file *memfile; @@ -150,18 +169,33 @@ static uint16_t menu_font_options[] = static struct equipment_item_option equipment_item_list[] = { - {6, 0b111, 0, 3, Z64_ITEM_GORONS_BRACELET, 6}, - {9, 0b111, 0, 4, Z64_ITEM_SILVER_SCALE, 6}, - {12, 0b11, 0, 5, Z64_ITEM_ADULTS_WALLET, 3}, + {6, 0b111, 0, 3, Z64_ITEM_GORONS_BRACELET, 6, "strength upgrade"}, + {9, 0b111, 0, 4, Z64_ITEM_SILVER_SCALE, 6, "diving upgrade"}, + {12, 0b11, 0, 5, Z64_ITEM_ADULTS_WALLET, 3, "wallet"}, }; static struct capacity_item_option capacity_item_list[] = { - {14, 0, 0, Z64_ITEM_BULLET_BAG_30, 1, {0, 3, 4, 5, 7, 8, 9, 10}}, - {0, 0, 1, Z64_ITEM_QUIVER_30, 1, {0, 3, 4, 5, 7, 9, 10, 11}}, - {3, 0, 2, Z64_ITEM_BOMB_BAG_20, 1, {0, 2, 3, 4, 7, 7, 7, 7}}, - {17, 1, 5, Z64_ITEM_STICK, 0, {0, 1, 2, 3, 7, 9, 10, 11}}, - {20, 2, 5, Z64_ITEM_NUT, 0, {0, 2, 3, 4, 7, 13, 7, 13}}, + { + 14, 0, 0, Z64_ITEM_BULLET_BAG_30, 1, + {0, 3, 4, 5, 7, 8, 9, 10}, "bullet bag", + }, + { + 0, 0, 1, Z64_ITEM_QUIVER_30, 1, + {0, 3, 4, 5, 7, 9, 10, 11}, "quiver", + }, + { + 3, 0, 2, Z64_ITEM_BOMB_BAG_20, 1, + {0, 2, 3, 4, 7, 7, 7, 7}, "bomb bag", + }, + { + 17, 1, 5, Z64_ITEM_STICK, 0, + {0, 1, 2, 3, 7, 9, 10, 11}, "stick capacity", + }, + { + 20, 2, 5, Z64_ITEM_NUT, 0, + {0, 2, 3, 4, 7, 13, 7, 13}, "nut capacity", + }, }; static struct equipment_switch equipment_list[] = @@ -336,58 +370,52 @@ static const char *cheat_names[] = "freze time", "no music", "items usable", + "no minimap", }; -static const char *command_names[] = -{ - "show/hide menu", - "return from menu", - "break free", - "void out", - "reload scene", - "file select", - "levitate", - "turbo", - "save position", - "load position", - "save memfile", - "load memfile", - "reset lag counter", - "start/stop timer", - "reset timer", - "pause/unpause", - "frame advance", -}; +void command_break(void) +{ + if (z64_game.event_flag != -1) + z64_game.event_flag = 0x0000; + if (z64_game.cutscene_state != 0x00) + z64_game.cutscene_state = 0x03; + if (z64_game.textbox_state_1 != 0x00) { + z64_game.textbox_state_1 = 0x36; + z64_game.textbox_state_2 = 0x00; + z64_game.textbox_state_3 = 0x02; + } + if (settings->menu_settings.break_type == SETTINGS_BREAK_AGGRESSIVE) { + z64_game.camera_mode = 0x0001; + z64_game.camera_flag_1 = 0x0000; + z64_link.state_flags_1 = 0x00000000; + z64_link.state_flags_2 = 0x00000000; + if (z64_link.action != 0x00) + z64_link.action = 0x07; + } +} -static int8_t bottle_options[] = +void command_levitate(void) { - Z64_ITEM_NULL, Z64_ITEM_BOTTLE, Z64_ITEM_RED_POTION, - Z64_ITEM_GREEN_POTION, Z64_ITEM_BLUE_POTION, Z64_ITEM_FAIRY, - Z64_ITEM_FISH, Z64_ITEM_MILK, Z64_ITEM_LETTER, - Z64_ITEM_BLUE_FIRE, Z64_ITEM_BUG, Z64_ITEM_BIG_POE, - Z64_ITEM_HALF_MILK, Z64_ITEM_POE, -}; + z64_link.common.vel_1.y = 6.34375f; +} -static int8_t adult_trade_options[] = +void command_savepos(void) { - Z64_ITEM_NULL, Z64_ITEM_POCKET_EGG, - Z64_ITEM_POCKET_CUCCO, Z64_ITEM_COJIRO, - Z64_ITEM_ODD_MUSHROOM, Z64_ITEM_ODD_POTION, - Z64_ITEM_POACHERS_SAW, Z64_ITEM_BROKEN_GORONS_SWORD, - Z64_ITEM_PRESCRIPTION, Z64_ITEM_EYEBALL_FROG, - Z64_ITEM_EYE_DROPS, Z64_ITEM_CLAIM_CHECK, -}; + uint8_t slot = settings->teleport_slot; + settings->teleport_pos[slot] = z64_link.common.pos_2; + settings->teleport_rot[slot] = z64_link.common.rot_2.y; +} -static int8_t child_trade_options[] = +void command_loadpos(void) { - Z64_ITEM_NULL, Z64_ITEM_WEIRD_EGG, Z64_ITEM_CHICKEN, - Z64_ITEM_ZELDAS_LETTER, Z64_ITEM_KEATON_MASK, Z64_ITEM_SKULL_MASK, - Z64_ITEM_SPOOKY_MASK, Z64_ITEM_BUNNY_HOOD, Z64_ITEM_GORON_MASK, - Z64_ITEM_ZORA_MASK, Z64_ITEM_GERUDO_MASK, Z64_ITEM_MASK_OF_TRUTH, - Z64_ITEM_SOLD_OUT, -}; + uint8_t slot = settings->teleport_slot; + z64_link.common.pos_1 = settings->teleport_pos[slot]; + z64_link.common.pos_2 = settings->teleport_pos[slot]; + z64_link.common.rot_2.y = settings->teleport_rot[slot]; + z64_link.target_yaw = settings->teleport_rot[slot]; +} -static void save_memory_file(void) +void command_savememfile(void) { struct memory_file *file = &memfile[memfile_slot]; memcpy(&file->z_file, &z64_file, sizeof(file->z_file)); @@ -397,7 +425,7 @@ static void save_memory_file(void) memfile_saved[memfile_slot] = 1; } -static void load_memory_file(void) +void command_loadmemfile(void) { if (!memfile_saved[memfile_slot]) return; @@ -405,9 +433,12 @@ static void load_memory_file(void) /* keep some data intact to prevent glitchiness */ int8_t seq_index = z64_file.seq_index; uint8_t minimap_index = z64_file.minimap_index; + int32_t link_age = z64_file.link_age; memcpy(&z64_file, &file->z_file, sizeof(file->z_file)); + z64_game.link_age = z64_file.link_age; z64_file.seq_index = seq_index; z64_file.minimap_index = minimap_index; + z64_file.link_age = link_age; if (file->scene_index == z64_game.scene_index) memcpy(&z64_game.switch_flags, &file->scene_flags, sizeof(file->scene_flags)); @@ -428,6 +459,199 @@ static void load_memory_file(void) z64_UpdateEquipment(&z64_game, &z64_link); } +void command_resetlag(void) +{ + frame_counter = 0; + lag_vi_offset = -(int32_t)z64_vi_counter; +} + +void command_timer(void) +{ + timer_active = !timer_active; +} + +void command_resettimer(void) +{ + timer_counter_offset = -cpu_counter; + timer_counter_prev = cpu_counter; +} + +static void input_hook() +{ + if (frames_queued != 0) + ((void(*)())z64_frame_input_func_addr)(); +} + +static void update_hook() +{ + if (frames_queued != 0) { + if (frames_queued > 0) + --frames_queued; + ((void(*)())z64_frame_update_func_addr)(); + } +} + +void command_pause(void) +{ + uint32_t *input_call = (void*)z64_frame_input_call_addr; + *input_call = MIPS_JAL(&input_hook); + uint32_t *update_call = (void*)z64_frame_update_call_addr; + *update_call = MIPS_JAL(&update_hook); + if (frames_queued >= 0) + frames_queued = -1; + else + frames_queued = 0; +} + +void command_advance(void) +{ + if (frames_queued >= 0) + ++frames_queued; + else + command_pause(); +} + +void command_fileselect(void) +{ + zu_execute_filemenu(); +} + +static void do_warp(int16_t entrance_index, uint16_t cutscene_index) +{ + override_offset = 1; + zu_execute_game(entrance_index, cutscene_index); +} + +void command_reload(void) +{ + do_warp(z64_file.entrance_index, 0x0000); +} + +void command_void(void) +{ + zu_void(); +} + +void command_turbo(void) +{ + z64_link.linear_vel = 27.f; +} + +void command_fall(void) +{ + z64_link.common.pos_1.y = -32768.f; +} + +void command_age(void) +{ + z64_game.link_age = !z64_game.link_age; +} + +void command_starttimer(void) +{ + if (!timer_active) + command_timer(); +} + +void command_stoptimer(void) +{ + if (timer_active) + command_timer(); +} + +void command_prevpos(void) +{ + settings->teleport_slot = (settings->teleport_slot + SETTINGS_TELEPORT_MAX - + 1) % SETTINGS_TELEPORT_MAX; +} + +void command_nextpos(void) +{ + settings->teleport_slot = (settings->teleport_slot + + 1) % SETTINGS_TELEPORT_MAX; +} + +void command_prevfile(void) +{ + memfile_slot = (memfile_slot + SETTINGS_MEMFILE_MAX - + 1) % SETTINGS_MEMFILE_MAX; +} + +void command_nextfile(void) +{ + memfile_slot = (memfile_slot + 1) % SETTINGS_MEMFILE_MAX; +} + +static struct command_info command_info[] = +{ + {"show/hide menu", NULL, CMDACT_PRESS_ONCE}, + {"return from menu", NULL, CMDACT_PRESS_ONCE}, + {"break free", command_break, CMDACT_HOLD}, + {"levitate", command_levitate, CMDACT_HOLD}, + {"save position", command_savepos, CMDACT_HOLD}, + {"load position", command_loadpos, CMDACT_HOLD}, + {"save memfile", command_savememfile, CMDACT_PRESS_ONCE}, + {"load memfile", command_loadmemfile, CMDACT_PRESS_ONCE}, + {"reset lag counter", command_resetlag, CMDACT_HOLD}, + {"start/stop timer", command_timer, CMDACT_PRESS_ONCE}, + {"reset timer", command_resettimer, CMDACT_HOLD}, + {"pause/unpause", command_pause, CMDACT_PRESS_ONCE}, + {"frame advance", command_advance, CMDACT_PRESS}, + {"file select", command_fileselect, CMDACT_PRESS_ONCE}, + {"reload scene", command_reload, CMDACT_PRESS_ONCE}, + {"void out", command_void, CMDACT_PRESS_ONCE}, + {"turbo", command_turbo, CMDACT_HOLD}, + {"fall", command_fall, CMDACT_HOLD}, + {"toggle age", command_age, CMDACT_PRESS_ONCE}, + {"start timer", command_starttimer, CMDACT_PRESS_ONCE}, + {"stop timer", command_stoptimer, CMDACT_PRESS_ONCE}, + {"previous position", command_prevpos, CMDACT_PRESS_ONCE}, + {"next position", command_nextpos, CMDACT_PRESS_ONCE}, + {"previous memfile", command_prevfile, CMDACT_PRESS_ONCE}, + {"next memfile", command_nextfile, CMDACT_PRESS_ONCE}, + {"explore prev room", NULL, CMDACT_PRESS}, + {"explore next room", NULL, CMDACT_PRESS}, +}; + +static int8_t bottle_options[] = +{ + Z64_ITEM_NULL, Z64_ITEM_BOTTLE, Z64_ITEM_RED_POTION, + Z64_ITEM_GREEN_POTION, Z64_ITEM_BLUE_POTION, Z64_ITEM_FAIRY, + Z64_ITEM_FISH, Z64_ITEM_MILK, Z64_ITEM_LETTER, + Z64_ITEM_BLUE_FIRE, Z64_ITEM_BUG, Z64_ITEM_BIG_POE, + Z64_ITEM_HALF_MILK, Z64_ITEM_POE, +}; + +static int8_t adult_trade_options[] = +{ + Z64_ITEM_NULL, Z64_ITEM_POCKET_EGG, + Z64_ITEM_POCKET_CUCCO, Z64_ITEM_COJIRO, + Z64_ITEM_ODD_MUSHROOM, Z64_ITEM_ODD_POTION, + Z64_ITEM_POACHERS_SAW, Z64_ITEM_BROKEN_GORONS_SWORD, + Z64_ITEM_PRESCRIPTION, Z64_ITEM_EYEBALL_FROG, + Z64_ITEM_EYE_DROPS, Z64_ITEM_CLAIM_CHECK, +}; + +static int8_t child_trade_options[] = +{ + Z64_ITEM_NULL, Z64_ITEM_WEIRD_EGG, Z64_ITEM_CHICKEN, + Z64_ITEM_ZELDAS_LETTER, Z64_ITEM_KEATON_MASK, Z64_ITEM_SKULL_MASK, + Z64_ITEM_SPOOKY_MASK, Z64_ITEM_BUNNY_HOOD, Z64_ITEM_GORON_MASK, + Z64_ITEM_ZORA_MASK, Z64_ITEM_GERUDO_MASK, Z64_ITEM_MASK_OF_TRUTH, + Z64_ITEM_SOLD_OUT, +}; + +static void update_cpu_counter() +{ + static uint32_t count = 0; + uint32_t new_count; + __asm__ volatile ("mfc0 $t0, $9\n" + "nop\n" + "sw $t0, %0" : "=m"(new_count) :: "t0"); + cpu_counter += new_count - count; + count = new_count; +} + static int cheat_proc(struct menu_item *item, enum menu_callback_reason reason, void *data) @@ -455,6 +679,19 @@ static int input_display_proc(struct menu_item *item, return 0; } +static int pause_display_proc(struct menu_item *item, + enum menu_callback_reason reason, + void *data) +{ + if (reason == MENU_CALLBACK_SWITCH_ON) + settings->menu_settings.pause_display = 1; + else if (reason == MENU_CALLBACK_SWITCH_OFF) + settings->menu_settings.pause_display = 0; + else if (reason == MENU_CALLBACK_THINK) + menu_checkbox_set(item, settings->menu_settings.pause_display); + return 0; +} + static int lag_counter_proc(struct menu_item *item, enum menu_callback_reason reason, void *data) @@ -792,6 +1029,20 @@ static int byte_option_proc(struct menu_item *item, return 0; } +static int halfword_optionmod_proc(struct menu_item *item, + enum menu_callback_reason reason, + void *data) +{ + uint16_t *v = data; + if (reason == MENU_CALLBACK_THINK_INACTIVE) { + if (menu_option_get(item) != *v) + menu_option_set(item, *v); + } + else if (reason == MENU_CALLBACK_DEACTIVATE) + *v = menu_option_get(item); + return 0; +} + static int age_option_proc(struct menu_item *item, enum menu_callback_reason reason, void *data) @@ -818,6 +1069,19 @@ static int lag_unit_proc(struct menu_item *item, return 0; } +static int break_type_proc(struct menu_item *item, + enum menu_callback_reason reason, + void *data) +{ + if (reason == MENU_CALLBACK_THINK_INACTIVE) { + if (menu_option_get(item) != settings->menu_settings.break_type) + menu_option_set(item, settings->menu_settings.break_type); + } + else if (reason == MENU_CALLBACK_DEACTIVATE) + settings->menu_settings.break_type = menu_option_get(item); + return 0; +} + static int button_item_proc(struct menu_item *item, enum menu_callback_reason reason, void *data) @@ -956,16 +1220,26 @@ static void slot_inc_proc(struct menu_item *item, void *data) *info->data = (*info->data + 1) % info->max; } -static void do_warp(int16_t entrance_index, int16_t cutscene_index) +static void warp_proc(struct menu_item *item, void *data) { - override_offset = 1; - zu_execute_game(entrance_index, cutscene_index); + z64_game.link_age = settings->menu_settings.warp_age; + uint16_t cutscene = settings->menu_settings.warp_cutscene; + if (cutscene > 0x0000) + cutscene += 0xFFEF; + do_warp(settings->warp_entrance, cutscene); } -static void warp_proc(struct menu_item *item, void *data) +static int cutscene_option_proc(struct menu_item *item, + enum menu_callback_reason reason, + void *data) { - z64_game.link_age = settings->menu_settings.warp_age; - do_warp(settings->warp_entrance, 0x0000); + if (reason == MENU_CALLBACK_THINK_INACTIVE) { + if (menu_option_get(item) != settings->menu_settings.warp_cutscene) + menu_option_set(item, settings->menu_settings.warp_cutscene); + } + else if (reason == MENU_CALLBACK_DEACTIVATE) + settings->menu_settings.warp_cutscene = menu_option_get(item); + return 0; } static void places_proc(struct menu_item *item, void *data) @@ -977,7 +1251,10 @@ static void places_proc(struct menu_item *item, void *data) z64_entrance_table_t *e = &z64_entrance_table[i]; if (e->scene_index == scene_index && e->entrance_index == entrance_index) { z64_game.link_age = settings->menu_settings.warp_age; - do_warp(i, 0x0000); + uint16_t cutscene = settings->menu_settings.warp_cutscene; + if (cutscene > 0x0000) + cutscene += 0xFFEF; + do_warp(i, cutscene); if (zu_scene_info[scene_index].no_entrances > 1) menu_return(&menu_main); menu_return(&menu_main); @@ -1060,6 +1337,13 @@ static int generic_position_proc(struct menu_item *item, return 0; } +static void activate_command_proc(struct menu_item *item, void *data) +{ + int command_index = (int)data; + if (command_info[command_index].proc) + command_info[command_index].proc(); +} + static void apply_settings() { size_t no_font_options = sizeof(menu_font_options) / @@ -1104,6 +1388,28 @@ static void clear_csp_proc(struct menu_item *item, void *data) z64_game.cutscene_ptr = &null_cs; } +static int warp_info_draw_proc(struct menu_item *item, + struct menu_draw_params *draw_params) +{ + gfx_mode_set(GFX_MODE_COLOR, (draw_params->color << 8) | draw_params->alpha); + struct gfx_font *font = draw_params->font; + int ch = menu_get_cell_height(item->owner, 1); + int x = draw_params->x; + int y = draw_params->y; + if (z64_game.link_age == 0) + gfx_printf(font, x, y + ch * 0, "current age adult"); + else + gfx_printf(font, x, y + ch * 0, "current age child"); + gfx_printf(font, x, y + ch * 1, + "current entrance %04" PRIx16, z64_file.entrance_index); + if (z64_file.cutscene_index >= 0xFFF0 && z64_file.cutscene_index <= 0xFFFF) + gfx_printf(font, x, y + ch * 2, "current cutscene %" PRIu16, + z64_file.cutscene_index - 0xFFEF); + else + gfx_printf(font, x, y + ch * 2, "current cutscene none"); + return 1; +} + static void load_room_proc(struct menu_item *item, void *data) { uint8_t new_room_index = menu_intinput_get(data); @@ -1120,45 +1426,75 @@ static void load_room_proc(struct menu_item *item, void *data) } } -static void input_hook() +static void tab_prev_proc(struct menu_item *item, void *data) { - if (frames_queued != 0) - ((void(*)())z64_frame_input_func_addr)(); + menu_tab_previous(data); } -static void update_hook() +static void tab_next_proc(struct menu_item *item, void *data) { - if (frames_queued != 0) { - if (frames_queued > 0) - --frames_queued; - ((void(*)())z64_frame_update_func_addr)(); - } -} - -static void pause_proc(struct menu_item *item, void *data) -{ - uint32_t *input_call = (void*)z64_frame_input_call_addr; - *input_call = MIPS_JAL(&input_hook); - uint32_t *update_call = (void*)z64_frame_update_call_addr; - *update_call = MIPS_JAL(&update_hook); - if (frames_queued >= 0) - frames_queued = -1; - else - frames_queued = 0; -} - -static void advance_proc(struct menu_item *item, void *data) -{ - if (frames_queued >= 0) - ++frames_queued; - else - pause_proc(item, data); + menu_tab_next(data); } void main_hook() { - gfx_mode_init(); + update_cpu_counter(); input_update(); + gfx_mode_init(); + + { + /* emergency settings reset */ + uint16_t pad_pressed = input_pressed(); + if (pad_pressed) { + static const uint16_t input_list[] = + { + BUTTON_D_UP, + BUTTON_D_UP, + BUTTON_D_DOWN, + BUTTON_D_DOWN, + BUTTON_D_LEFT, + BUTTON_D_RIGHT, + BUTTON_D_LEFT, + BUTTON_D_RIGHT, + BUTTON_B, + BUTTON_A, + }; + static int input_pos = 0; + size_t input_list_length = sizeof(input_list) / sizeof(*input_list); + if (pad_pressed == input_list[input_pos]) { + ++input_pos; + if (input_pos == input_list_length) { + input_pos = 0; + settings_load_default(); + apply_settings(); + } + } + else + input_pos = 0; + } + } + + if (menu_active) { + if (input_bind_pressed_raw(COMMAND_MENU)) + hide_menu(); + else if (input_bind_pressed(COMMAND_RETURN)) + menu_return(&menu_main); + else { + uint16_t pad_pressed = input_pressed(); + if (pad_pressed & BUTTON_D_UP) + menu_navigate(&menu_main, MENU_NAVIGATE_UP); + if (pad_pressed & BUTTON_D_DOWN) + menu_navigate(&menu_main, MENU_NAVIGATE_DOWN); + if (pad_pressed & BUTTON_D_LEFT) + menu_navigate(&menu_main, MENU_NAVIGATE_LEFT); + if (pad_pressed & BUTTON_D_RIGHT) + menu_navigate(&menu_main, MENU_NAVIGATE_RIGHT); + if (pad_pressed & BUTTON_L) + menu_activate(&menu_main); + } + } + else if (input_bind_pressed_raw(COMMAND_MENU)) + show_menu(); if (settings->cheats & (1 << CHEAT_ENERGY)) z64_file.energy = z64_file.energy_capacity; @@ -1208,93 +1544,35 @@ void main_hook() if (settings->cheats & (1 << CHEAT_NOMUSIC)) { zu_setmusic(0x100000FF); zu_setmusic(0x110000FF); + zu_setmusic(0x130000FF); z64_file.seq_index = -1; } if (settings->cheats & (1 << CHEAT_USEITEMS)) memset(&z64_game.restriction_flags, 0, sizeof(z64_game.restriction_flags)); - - while (menu_think(&menu_global_watches)) - ; - if (menu_active) { - if (input_bind_pressed_raw(COMMAND_MENU)) - hide_menu(); - else if (input_bind_pressed(COMMAND_RETURN)) - menu_return(&menu_main); - else { - uint16_t pad_pressed = input_pressed(); - if (pad_pressed & BUTTON_D_UP) - menu_navigate(&menu_main, MENU_NAVIGATE_UP); - if (pad_pressed & BUTTON_D_DOWN) - menu_navigate(&menu_main, MENU_NAVIGATE_DOWN); - if (pad_pressed & BUTTON_D_LEFT) - menu_navigate(&menu_main, MENU_NAVIGATE_LEFT); - if (pad_pressed & BUTTON_D_RIGHT) - menu_navigate(&menu_main, MENU_NAVIGATE_RIGHT); - if (pad_pressed & BUTTON_L) - menu_activate(&menu_main); + if (settings->cheats & (1 << CHEAT_NOMAP)) + z64_gameinfo.minimap_disabled = 1; + + for (int i = 0; i < COMMAND_MAX; ++i) { + _Bool active = 0; + switch (command_info[i].activation_type) { + case CMDACT_HOLD: active = input_bind_held(i); break; + case CMDACT_PRESS: active = input_bind_pressed(i); break; + case CMDACT_PRESS_ONCE: active = input_bind_pressed_raw(i); break; } - while (menu_think(&menu_main)) - ; - } - else if (input_bind_pressed_raw(COMMAND_MENU)) - show_menu(); - if (input_bind_held(COMMAND_BREAK)) { - z64_game.camera_mode = 0x0001; - z64_game.camera_flag_1 = 0x0000; - if (z64_game.event_flag != -1) - z64_game.event_flag = 0x0000; - if (z64_game.cutscene_state != 0x00) - z64_game.cutscene_state = 0x03; - if (z64_game.textbox_state_1 != 0x00) { - z64_game.textbox_state_1 = 0x36; - z64_game.textbox_state_2 = 0x00; - z64_game.textbox_state_3 = 0x02; - } - z64_link.state_flags_1 = 0x00000000; - z64_link.state_flags_2 = 0x00000000; - if (z64_link.action != 0x00) - z64_link.action = 0x07; - } - if (input_bind_held(COMMAND_LEVITATE)) - z64_link.common.vel_1.y = 6.34375f; - if (input_bind_held(COMMAND_TURBO)) - z64_link.linear_vel = 27.f; - if (input_bind_held(COMMAND_SAVEPOS)) { - uint8_t slot = settings->teleport_slot; - settings->teleport_pos[slot] = z64_link.common.pos_2; - settings->teleport_rot[slot] = z64_link.common.rot_2.y; - } - if (input_bind_held(COMMAND_LOADPOS)) { - uint8_t slot = settings->teleport_slot; - z64_link.common.pos_1 = settings->teleport_pos[slot]; - z64_link.common.pos_2 = settings->teleport_pos[slot]; - z64_link.common.rot_2.y = settings->teleport_rot[slot]; - z64_link.target_yaw = settings->teleport_rot[slot]; - } - if (input_bind_pressed_raw(COMMAND_SAVEMEMFILE)) - save_memory_file(); - if (input_bind_pressed_raw(COMMAND_LOADMEMFILE)) - load_memory_file(); - if (input_bind_held(COMMAND_RESETLAG)) { - frame_counter = 0; - lag_vi_offset = -(int32_t)z64_vi_counter; + if (command_info[i].proc && active) + command_info[i].proc(); } - if (input_bind_pressed_raw(COMMAND_TIMER)) - timer_active = !timer_active; - if (input_bind_held(COMMAND_RESETTIMER)) { - timer_vi_offset = -(int32_t)z64_vi_counter; - timer_vi_prev = z64_vi_counter; - } - if (input_bind_pressed_raw(COMMAND_PAUSE)) - pause_proc(NULL, NULL); - if (input_bind_pressed(COMMAND_ADVANCE)) - advance_proc(NULL, NULL); - if (input_bind_pressed_raw(COMMAND_FILESELECT)) - zu_execute_filemenu(); - if (input_bind_pressed_raw(COMMAND_RELOAD)) - do_warp(z64_file.entrance_index, 0x0000); - if (input_bind_pressed_raw(COMMAND_VOID)) - zu_void(); + if (input_bind_pressed(COMMAND_PREVROOM) && + menu_get_front(&menu_main) == &menu_explorer) + explorer_room_prev(&menu_explorer); + if (input_bind_pressed(COMMAND_NEXTROOM) && + menu_get_front(&menu_main) == &menu_explorer) + explorer_room_next(&menu_explorer); + + while (menu_active && menu_think(&menu_main)) + ; + while (menu_think(&menu_global_watches)) + ; /* update daytime after menu processing to avoid desync */ if (target_day_time != -1) { @@ -1315,6 +1593,17 @@ void main_hook() int cw = menu_get_cell_width(&menu_main, 1); int ch = menu_get_cell_height(&menu_main, 1); + if (settings->menu_settings.pause_display && frames_queued != -1) { + struct gfx_texture *t = resource_get(RES_ICON_PAUSE); + struct gfx_sprite sprite = + { + t, frames_queued == 0 ? 0 : 1, + 32, 32, 1.f, 1.f, + }; + gfx_mode_set(GFX_MODE_COLOR, GPACK_RGBA8888(0xC0, 0xC0, 0xC0, alpha)); + gfx_sprite_draw(&sprite); + } + if (settings->menu_settings.input_display) { struct gfx_texture *texture = resource_get(RES_ICON_BUTTONS); gfx_mode_set(GFX_MODE_COLOR, GPACK_RGBA8888(0xC0, 0xC0, 0xC0, alpha)); @@ -1354,11 +1643,11 @@ void main_hook() frame_counter += z64_gameinfo.update_rate; if (!timer_active) - timer_vi_offset -= (int32_t)z64_vi_counter - timer_vi_prev; - timer_vi_prev = z64_vi_counter; + timer_counter_offset -= cpu_counter - timer_counter_prev; + timer_counter_prev = cpu_counter; if (settings->menu_settings.timer) { - int32_t frames = (int32_t)z64_vi_counter + timer_vi_offset; - int tenths = frames / 6; + int64_t count = cpu_counter + timer_counter_offset; + int tenths = count * 10 / CPU_COUNTER_FREQ; int seconds = tenths / 10; int minutes = seconds / 60; int hours = minutes / 60; @@ -1387,18 +1676,18 @@ void main_hook() --splash_time; gfx_mode_set(GFX_MODE_COLOR, GPACK_RGBA8888(0xC0, 0x00, 0x00, alpha)); gfx_printf(font, 16, Z64_SCREEN_HEIGHT - 6 - ch, - "gz-0.2.0 github.com/glankk/gz"); + "gz-0.3.0 github.com/glankk/gz"); static struct gfx_texture *logo_texture = NULL; if (!logo_texture) logo_texture = resource_load_grc_texture("logo"); gfx_mode_set(GFX_MODE_COLOR, GPACK_RGBA8888(0xFF, 0xFF, 0xFF, alpha)); - for (int y = 0; y < logo_texture->tiles_y; ++y) { + for (int i = 0; i < logo_texture->tiles_y; ++i) { struct gfx_sprite logo_sprite = { - logo_texture, y, + logo_texture, i, Z64_SCREEN_WIDTH - 12 - logo_texture->tile_width, - Z64_SCREEN_HEIGHT - 6 - ch - (logo_texture->tiles_y - - y) * logo_texture->tile_height, + Z64_SCREEN_HEIGHT - 6 - ch * 2 - + (logo_texture->tiles_y - i) * logo_texture->tile_height, 1.f, 1.f, }; gfx_sprite_draw(&logo_sprite); @@ -1454,9 +1743,10 @@ ENTRY void _start() *(uint32_t*)z64_minimap_disable_2_addr = MIPS_BEQ(MIPS_R0, MIPS_R0, 0x98); /* initialize variables */ + update_cpu_counter(); lag_vi_offset = -(int32_t)z64_vi_counter; - timer_vi_offset = -(int32_t)z64_vi_counter; - timer_vi_prev = z64_vi_counter; + timer_counter_offset = -cpu_counter; + timer_counter_prev = cpu_counter; day_time_prev = z64_file.day_time; /* initialize menus */ @@ -1517,25 +1807,33 @@ ENTRY void _start() cat->category_name); } menu_add_submenu(&menu_warps, 0, 1, &places, "places"); - menu_add_static(&menu_warps, 0, 2, "entrance", 0xC0C0C0); - menu_add_intinput(&menu_warps, 9, 2, 16, 4, - halfword_mod_proc, &settings->warp_entrance); - menu_add_static(&menu_warps, 0, 3, "age", 0xC0C0C0); - menu_add_option(&menu_warps, 9, 3, "adult\0""child\0", + menu_add_static(&menu_warps, 0, 2, "age", 0xC0C0C0); + menu_add_option(&menu_warps, 9, 2, "adult\0""child\0", age_option_proc, NULL); - menu_add_button(&menu_warps, 0, 4, "warp", warp_proc, NULL); - menu_add_button(&menu_warps, 0, 5, "clear cutscene pointer", + menu_add_static(&menu_warps, 0, 3, "entrance", 0xC0C0C0); + menu_add_intinput(&menu_warps, 9, 3, 16, 4, + halfword_mod_proc, &settings->warp_entrance); + menu_add_static(&menu_warps, 0, 4, "cutscene", 0xC0C0C0); + menu_add_option(&menu_warps, 9, 4, + "none\0""1\0""2\0""3\0""4\0""5\0""6\0""7\0""8\0" + "9\0""10\0""11\0""12\0""13\0""14\0""15\0""16\0", + cutscene_option_proc, NULL); + menu_add_button(&menu_warps, 0, 5, "warp", warp_proc, NULL); + menu_add_button(&menu_warps, 0, 6, "clear cs pointer", clear_csp_proc, NULL); + { + struct menu_item *item = menu_item_add(&menu_warps, 0, 7, + NULL, 0xC0C0C0); + item->selectable = 0; + item->draw_proc = warp_info_draw_proc; + } /* scene */ menu_init(&menu_scene, MENU_NOVALUE, MENU_NOVALUE, MENU_NOVALUE); menu_scene.selector = menu_add_submenu(&menu_scene, 0, 0, NULL, "return"); - { - static struct menu explorer; - explorer_create(&explorer); - menu_add_submenu(&menu_scene, 0, 1, &explorer, "explorer"); - } + explorer_create(&menu_explorer); + menu_add_submenu(&menu_scene, 0, 1, &menu_explorer, "explorer"); menu_add_button(&menu_scene, 0, 2, "clear flags", clear_scene_flags_proc, NULL); menu_add_button(&menu_scene, 0, 3, "set flags", @@ -1622,6 +1920,7 @@ ENTRY void _start() equipment_item_proc, data); item->pxoffset = option->x * 18; item->pyoffset = option->y * 18; + item->tooltip = option->tooltip; } size_t capacity_item_list_length = sizeof(capacity_item_list) / sizeof(*capacity_item_list); @@ -1651,6 +1950,7 @@ ENTRY void _start() capacity_item_proc, data); item->pxoffset = option->x * 18; item->pyoffset = option->y * 18; + item->tooltip = option->tooltip; } size_t equipment_list_length = sizeof(equipment_list) / sizeof(*equipment_list); @@ -1698,6 +1998,9 @@ ENTRY void _start() NULL, NULL); item->pxoffset = 76 + i * 18; item->pyoffset = 72; + char *tooltip = malloc(9); + sprintf(tooltip, "bottle %d", i + 1); + item->tooltip = tooltip; } size_t adult_trade_options_length = sizeof(adult_trade_options) / sizeof(*adult_trade_options); @@ -1711,6 +2014,7 @@ ENTRY void _start() NULL, NULL); item->pxoffset = 76 + 4 * 18; item->pyoffset = 72; + item->tooltip = "adult trade item"; size_t child_trade_options_length = sizeof(child_trade_options) / sizeof(*child_trade_options); item = item_option_create(&menu_equipment_items, 0, 2, @@ -1722,7 +2026,13 @@ ENTRY void _start() NULL, NULL); item->pxoffset = 76 + 5 * 18; item->pyoffset = 72; + item->tooltip = "child trade item"; } + struct menu_item *item; + item = menu_add_tooltip(&menu_equipment_items, 0, 2, &menu_main, + 0xC0C0C0); + item->pxoffset = 76; + item->pyoffset = 90; } menu_init(&menu_quest_items, MENU_NOVALUE, MENU_NOVALUE, MENU_NOVALUE); menu_quest_items.selector = menu_add_submenu(&menu_quest_items, 0, 0, NULL, @@ -1950,43 +2260,59 @@ ENTRY void _start() &memfile_slot, SETTINGS_MEMFILE_MAX, }; menu_add_static(&menu_file, 0, 3, "memory file", 0xC0C0C0); - menu_add_watch(&menu_file, 20, 3, + menu_add_watch(&menu_file, 19, 3, (uint32_t)memfile_slot_info.data, WATCH_TYPE_U8); - menu_add_button(&menu_file, 18, 3, "-", + menu_add_button(&menu_file, 17, 3, "-", slot_dec_proc, &memfile_slot_info); - menu_add_button(&menu_file, 22, 3, "+", + menu_add_button(&menu_file, 21, 3, "+", slot_inc_proc, &memfile_slot_info); } { struct gfx_texture *t = resource_get(RES_ICON_DAYTIME); menu_add_static(&menu_file, 0, 4, "time of day", 0xC0C0C0); - menu_add_button_icon(&menu_file, 18, 4, t, 0, 0xFFC800, + menu_add_button_icon(&menu_file, 17, 4, t, 0, 0xFFC800, set_time_proc, (void*)0x4AB0); - menu_add_button_icon(&menu_file, 20, 4, t, 1, 0xA0A0E0, + menu_add_button_icon(&menu_file, 19, 4, t, 1, 0xA0A0E0, set_time_proc, (void*)0xC010); - menu_add_intinput(&menu_file, 22, 4, 16, 4, + menu_add_intinput(&menu_file, 21, 4, 16, 4, halfword_mod_proc, &z64_file.day_time); } { struct gfx_texture *t = resource_get(RES_ICON_CHECK); - menu_add_static(&menu_file, 0, 5, "carpenter's freed", 0xC0C0C0); - menu_add_button_icon(&menu_file, 18, 5, t, 0, 0x00FF00, + menu_add_static(&menu_file, 0, 5, "carpenters freed", 0xC0C0C0); + menu_add_button_icon(&menu_file, 17, 5, t, 0, 0x00FF00, set_carpenter_flags_proc, NULL); - menu_add_button_icon(&menu_file, 20, 5, t, 1, 0xFF0000, + menu_add_button_icon(&menu_file, 19, 5, t, 1, 0xFF0000, clear_carpenter_flags_proc, NULL); menu_add_static(&menu_file, 0, 6, "intro cutscenes", 0xC0C0C0); - menu_add_button_icon(&menu_file, 18, 6, t, 0, 0x00FF00, + menu_add_button_icon(&menu_file, 17, 6, t, 0, 0x00FF00, set_intro_flags_proc, NULL); - menu_add_button_icon(&menu_file, 20, 6, t, 1, 0xFF0000, + menu_add_button_icon(&menu_file, 19, 6, t, 1, 0xFF0000, clear_intro_flags_proc, NULL); menu_add_static(&menu_file, 0, 7, "rewards obtained", 0xC0C0C0); - menu_add_button_icon(&menu_file, 18, 7, t, 0, 0x00FF00, + menu_add_button_icon(&menu_file, 17, 7, t, 0, 0x00FF00, set_reward_flags_proc, NULL); - menu_add_button_icon(&menu_file, 20, 7, t, 1, 0xFF0000, + menu_add_button_icon(&menu_file, 19, 7, t, 1, 0xFF0000, clear_reward_flags_proc, NULL); } - menu_add_static(&menu_file, 0, 8, "file index", 0xC0C0C0); - menu_add_intinput(&menu_file, 18, 8, 16, 2, + menu_add_static(&menu_file, 0, 8, "timer 1", 0xC0C0C0); + menu_add_intinput(&menu_file, 17, 8, 10, 5, + halfword_mod_proc, &z64_file.timer_1_value); + menu_add_option(&menu_file, 23, 8, + "inactive\0""heat starting\0""heat initial\0" + "heat moving\0""heat active\0""race starting\0" + "race initial\0""race moving\0""race active\0" + "race stopped\0""race ending\0", + halfword_optionmod_proc, &z64_file.timer_1_state); + menu_add_static(&menu_file, 0, 9, "timer 2", 0xC0C0C0); + menu_add_intinput(&menu_file, 17, 9, 10, 5, + halfword_mod_proc, &z64_file.timer_2_value); + menu_add_option(&menu_file, 23, 9, + "inactive\0""starting\0""initial\0" + "moving\0""active\0""stopped\0", + halfword_optionmod_proc, &z64_file.timer_2_state); + menu_add_static(&menu_file, 0, 10, "file index", 0xC0C0C0); + menu_add_intinput(&menu_file, 17, 10, 16, 2, byte_mod_proc, &z64_file.file_index); { static int8_t language_options[] = {0x00, 0x01}; @@ -1994,16 +2320,16 @@ ENTRY void _start() { &z64_file.language, language_options, 2, }; - menu_add_static(&menu_file, 0, 9, "language", 0xC0C0C0); - menu_add_option(&menu_file, 18, 9, "japanese\0""english\0", + menu_add_static(&menu_file, 0, 11, "language", 0xC0C0C0); + menu_add_option(&menu_file, 17, 11, "japanese\0""english\0", byte_option_proc, &language_option_data); static int8_t target_options[] = {0x00, 0x01}; static struct byte_option target_option_data = { &z64_file.z_targeting, target_options, 2, }; - menu_add_static(&menu_file, 0, 10, "z targeting", 0xC0C0C0); - menu_add_option(&menu_file, 18, 10, "switch\0""hold\0", + menu_add_static(&menu_file, 0, 12, "z targeting", 0xC0C0C0); + menu_add_option(&menu_file, 17, 12, "switch\0""hold\0", byte_option_proc, &target_option_data); } @@ -2059,19 +2385,47 @@ ENTRY void _start() menu_add_checkbox(&menu_settings, 14, 7, timer_proc, NULL); menu_add_positioning(&menu_settings, 16, 7, generic_position_proc, &settings->timer_x); - static struct menu menu_binds; - menu_add_submenu(&menu_settings, 0, 8, &menu_binds, "button maps"); - menu_add_button(&menu_settings, 0, 9, "save settings", + menu_add_static(&menu_settings, 0, 8, "pause display", 0xC0C0C0); + menu_add_checkbox(&menu_settings, 14, 8, pause_display_proc, NULL); + menu_add_static(&menu_settings, 0, 9, "break type", 0xC0C0C0); + menu_add_option(&menu_settings, 14, 9, "normal\0""aggressive\0", + break_type_proc, NULL); + static struct menu menu_commands; + menu_add_submenu(&menu_settings, 0, 10, &menu_commands, "commands"); + menu_add_button(&menu_settings, 0, 11, "save settings", save_settings_proc, NULL); - menu_add_button(&menu_settings, 0, 10, "load settings", + menu_add_button(&menu_settings, 0, 12, "load settings", load_settings_proc, NULL); - menu_add_button(&menu_settings, 0, 11, "restore defaults", + menu_add_button(&menu_settings, 0, 13, "restore defaults", restore_settings_proc, NULL); - menu_init(&menu_binds, MENU_NOVALUE, MENU_NOVALUE, MENU_NOVALUE); - menu_binds.selector = menu_add_submenu(&menu_binds, 0, 0, NULL, "return"); - for (int i = 0; i < COMMAND_MAX; ++i) { - menu_add_static(&menu_binds, 0, 1 + i, command_names[i], 0xC0C0C0); - binder_create(&menu_binds, 18, 1 + i, i); + menu_init(&menu_commands, MENU_NOVALUE, MENU_NOVALUE, MENU_NOVALUE); + menu_commands.selector = menu_add_submenu(&menu_commands, 0, 0, + NULL, "return"); + { + const int page_length = 16; + int no_pages = (COMMAND_MAX + page_length - 1) / page_length; + struct menu *pages = malloc(sizeof(*pages) * no_pages); + struct menu_item *tab = menu_add_tab(&menu_commands, 0, 1, + pages, no_pages); + for (int i = 0; i < no_pages; ++i) { + struct menu *page = &pages[i]; + menu_init(page, MENU_NOVALUE, MENU_NOVALUE, MENU_NOVALUE); + for (int j = 0; j < page_length; ++j) { + int n = i * page_length + j; + if (n >= COMMAND_MAX) + break; + if (command_info[n].proc) + menu_add_button(page, 0, j, command_info[n].name, + activate_command_proc, (void*)n); + else + menu_add_static(page, 0, j, command_info[n].name, 0xC0C0C0); + binder_create(page, 18, j, n); + } + } + if (no_pages > 0) + menu_tab_goto(tab, 0); + menu_add_button(&menu_commands, 8, 0, "<", tab_prev_proc, tab); + menu_add_button(&menu_commands, 10, 0, ">", tab_next_proc, tab); } input_bind_set_override(COMMAND_MENU, 1); input_bind_set_override(COMMAND_RETURN, 1); diff --git a/src/gz/menu.c b/src/gz/menu.c index 1999b897..00b339fa 100644 --- a/src/gz/menu.c +++ b/src/gz/menu.c @@ -165,6 +165,20 @@ int menu_cell_screen_y(struct menu *menu, int y) menu_get_pyoffset(menu, 1); } +struct menu_item *menu_get_selector(struct menu *menu) +{ + if (menu->child) + return menu_get_selector(menu->child); + return menu->selector; +} + +struct menu *menu_get_front(struct menu *menu) +{ + if (menu->child) + return menu_get_front(menu->child); + return menu; +} + int menu_think(struct menu *menu) { if (menu->child) diff --git a/src/gz/menu.h b/src/gz/menu.h index ebaed718..2d863efb 100644 --- a/src/gz/menu.h +++ b/src/gz/menu.h @@ -137,6 +137,8 @@ uint8_t menu_get_alpha_i(struct menu *menu, _Bool inherit); void menu_set_alpha(struct menu *menu, float alpha); int menu_cell_screen_x(struct menu *menu, int cell_x); int menu_cell_screen_y(struct menu *menu, int cell_y); +struct menu_item *menu_get_selector(struct menu *menu); +struct menu *menu_get_front(struct menu *menu); int menu_think(struct menu *menu); void menu_draw(struct menu *menu); void menu_navigate(struct menu *menu, enum menu_navigation nav); @@ -172,6 +174,12 @@ struct menu_item *menu_add_tooltip(struct menu *menu, int x, int y, struct menu *tool_menu, uint32_t color); struct menu_item *menu_add_imenu(struct menu *menu, int x, int y, struct menu **p_imenu); +struct menu_item *menu_add_tab(struct menu *menu, int x, int y, + struct menu *tabs, int no_tabs); +void menu_tab_goto(struct menu_item *item, int tab_index); +void menu_tab_previous(struct menu_item *item); +void menu_tab_next(struct menu_item *item); + struct menu_item *menu_add_intinput(struct menu *menu, int x, int y, int base, int length, diff --git a/src/gz/menu_def.c b/src/gz/menu_def.c index 8d7da875..47815187 100644 --- a/src/gz/menu_def.c +++ b/src/gz/menu_def.c @@ -135,3 +135,72 @@ struct menu_item *menu_add_imenu(struct menu *menu, int x, int y, *p_imenu = imenu; return item; } + +struct tab_data +{ + struct menu *tabs; + int no_tabs; + int current_tab; +}; + +static int tab_destroy_proc(struct menu_item *item) +{ + item->imenu = NULL; + return 0; +} + +struct menu_item *menu_add_tab(struct menu *menu, int x, int y, + struct menu *tabs, int no_tabs) +{ + struct tab_data *data = malloc(sizeof(*data)); + data->tabs = tabs; + data->no_tabs = no_tabs; + data->current_tab = -1; + struct menu_item *item = menu_item_add(menu, x, y, NULL, 0); + item->data = data; + item->selectable = 0; + item->think_proc = imenu_think_proc; + item->navigate_proc = imenu_navigate_proc; + item->activate_proc = imenu_activate_proc; + item->destroy_proc = tab_destroy_proc; + return item; +} + +void menu_tab_goto(struct menu_item *item, int tab_index) +{ + struct tab_data *data = item->data; + if (data->tabs) { + if (data->current_tab >= 0) { + struct menu *tab = &data->tabs[data->current_tab]; + struct menu_item *selector = menu_get_selector(tab); + if (selector) + menu_select_top(item->owner, NULL); + tab->parent = NULL; + item->imenu = NULL; + } + data->current_tab = tab_index; + if (data->current_tab >= 0) { + struct menu *tab = &data->tabs[data->current_tab]; + tab->parent = item->owner; + item->imenu = tab; + } + } +} + +void menu_tab_previous(struct menu_item *item) +{ + struct tab_data *data = item->data; + if (data->no_tabs >= 0) { + int tab_index = (data->current_tab + data->no_tabs - 1) % data->no_tabs; + menu_tab_goto(item, tab_index); + } +} + +void menu_tab_next(struct menu_item *item) +{ + struct tab_data *data = item->data; + if (data->no_tabs >= 0) { + int tab_index = (data->current_tab + 1) % data->no_tabs; + menu_tab_goto(item, tab_index); + } +} diff --git a/src/gz/menu_option.c b/src/gz/menu_option.c index e4c1fb08..d4e5c8fd 100644 --- a/src/gz/menu_option.c +++ b/src/gz/menu_option.c @@ -34,8 +34,9 @@ static int navigate_proc(struct menu_item *item, enum menu_navigation nav) { struct item_data *data = item->data; - if (data->callback_proc && data->callback_proc(item, nav, - data->callback_data)) + if (data->callback_proc && + data->callback_proc(item, MENU_CALLBACK_NAV_UP + nav, + data->callback_data)) return 1; int value = data->value; switch (nav) { diff --git a/src/gz/resource.c b/src/gz/resource.c index 2b1e23c8..c05467dc 100644 --- a/src/gz/resource.c +++ b/src/gz/resource.c @@ -210,6 +210,11 @@ static void *rc_icon_buttons(void) return resource_load_grc_texture("button_icons"); } +static void *rc_icon_pause(void) +{ + return resource_load_grc_texture("pause_icons"); +} + static void *rc_texture_crosshair(void) { return resource_load_grc_texture("crosshair"); @@ -245,6 +250,7 @@ static void *(*res_ctor[RES_MAX])(void) = rc_icon_daytime, rc_icon_amount, rc_icon_buttons, + rc_icon_pause, rc_texture_crosshair, }; @@ -270,6 +276,7 @@ static void (*res_dtor[RES_MAX])() = gfx_texture_free, gfx_texture_free, gfx_texture_free, + gfx_texture_free, }; /* resource interface */ diff --git a/src/gz/resource.h b/src/gz/resource.h index 562fc9b3..e978db4b 100644 --- a/src/gz/resource.h +++ b/src/gz/resource.h @@ -23,6 +23,7 @@ enum resource_id RES_ICON_DAYTIME, RES_ICON_AMOUNT, RES_ICON_BUTTONS, + RES_ICON_PAUSE, RES_TEXTURE_CROSSHAIR, RES_MAX, }; diff --git a/src/gz/settings.c b/src/gz/settings.c index 922a96a2..94a73ae2 100644 --- a/src/gz/settings.c +++ b/src/gz/settings.c @@ -36,11 +36,14 @@ void settings_load_default(void) struct settings_data *d = &settings_store.data; d->menu_settings.font_resource = RES_FONT_PRESSSTART2P; d->menu_settings.drop_shadow = 1; - d->menu_settings.input_display = 0; + d->menu_settings.input_display = 1; d->menu_settings.lag_counter = 0; d->menu_settings.lag_unit = SETTINGS_LAG_FRAMES; d->menu_settings.timer = 0; + d->menu_settings.pause_display = 1; + d->menu_settings.break_type = SETTINGS_BREAK_NORMAL; d->menu_settings.warp_age = 0; + d->menu_settings.warp_cutscene = 0; d->menu_x = 16; d->menu_y = 64; d->input_display_x = 16; @@ -62,25 +65,33 @@ void settings_load_default(void) d->binds[COMMAND_MENU] = input_bind_make(2, BUTTON_R, BUTTON_L); d->binds[COMMAND_RETURN] = input_bind_make(2, BUTTON_R, BUTTON_D_LEFT); d->binds[COMMAND_BREAK] = input_bind_make(2, BUTTON_C_UP, BUTTON_L); - d->binds[COMMAND_VOID] = input_bind_make(3, BUTTON_A, BUTTON_B, BUTTON_L); - d->binds[COMMAND_RELOAD] = input_bind_make(2, BUTTON_A, BUTTON_L); - d->binds[COMMAND_FILESELECT] = input_bind_make(2, BUTTON_B, BUTTON_L); d->binds[COMMAND_LEVITATE] = input_bind_make(1, BUTTON_L); - d->binds[COMMAND_TURBO] = input_bind_make(0); d->binds[COMMAND_SAVEPOS] = input_bind_make(1, BUTTON_D_LEFT); d->binds[COMMAND_LOADPOS] = input_bind_make(1, BUTTON_D_RIGHT); - d->binds[COMMAND_SAVEMEMFILE] = input_bind_make(2, BUTTON_R, - BUTTON_D_LEFT); - d->binds[COMMAND_LOADMEMFILE] = input_bind_make(2, BUTTON_R, - BUTTON_D_RIGHT); + d->binds[COMMAND_SAVEMEMFILE] = input_bind_make(2, BUTTON_R, BUTTON_D_LEFT); + d->binds[COMMAND_LOADMEMFILE] = input_bind_make(2, BUTTON_R, BUTTON_D_RIGHT); d->binds[COMMAND_RESETLAG] = input_bind_make(3, BUTTON_R, BUTTON_B, - BUTTON_D_RIGHT); + BUTTON_D_RIGHT); d->binds[COMMAND_TIMER] = input_bind_make(3, BUTTON_R, BUTTON_A, BUTTON_D_LEFT); d->binds[COMMAND_RESETTIMER] = input_bind_make(3, BUTTON_R, BUTTON_B, BUTTON_D_LEFT); d->binds[COMMAND_PAUSE] = input_bind_make(1, BUTTON_D_DOWN); d->binds[COMMAND_ADVANCE] = input_bind_make(1, BUTTON_D_UP); + d->binds[COMMAND_FILESELECT] = input_bind_make(2, BUTTON_B, BUTTON_L); + d->binds[COMMAND_RELOAD] = input_bind_make(2, BUTTON_A, BUTTON_L); + d->binds[COMMAND_VOID] = input_bind_make(3, BUTTON_A, BUTTON_B, BUTTON_L); + d->binds[COMMAND_TURBO] = input_bind_make(0); + d->binds[COMMAND_FALL] = input_bind_make(0); + d->binds[COMMAND_AGE] = input_bind_make(0); + d->binds[COMMAND_STARTTIMER] = input_bind_make(0); + d->binds[COMMAND_STOPTIMER] = input_bind_make(0); + d->binds[COMMAND_PREVPOS] = input_bind_make(0); + d->binds[COMMAND_NEXTPOS] = input_bind_make(0); + d->binds[COMMAND_PREVFILE] = input_bind_make(0); + d->binds[COMMAND_NEXTFILE] = input_bind_make(0); + d->binds[COMMAND_PREVROOM] = input_bind_make(2, BUTTON_R, BUTTON_D_DOWN); + d->binds[COMMAND_NEXTROOM] = input_bind_make(2, BUTTON_R, BUTTON_D_UP); } void settings_save(int profile) diff --git a/src/gz/settings.h b/src/gz/settings.h index 85025945..a1eee96b 100644 --- a/src/gz/settings.h +++ b/src/gz/settings.h @@ -4,19 +4,22 @@ #include "input.h" #include "z64.h" -#define SETTINGS_ADDRESS 0x7A00 -#define SETTINGS_MAXSIZE (0x8000-(SETTINGS_ADDRESS)) -#define SETTINGS_PADSIZE ((sizeof(struct settings)+1)/2*2) -#define SETTINGS_PROFILE_MAX ((SETTINGS_MAXSIZE)/(SETTINGS_PADSIZE)) -#define SETTINGS_VERSION 0x0000 +#define SETTINGS_ADDRESS 0x7A00 +#define SETTINGS_MAXSIZE (0x8000-(SETTINGS_ADDRESS)) +#define SETTINGS_PADSIZE ((sizeof(struct settings)+1)/2*2) +#define SETTINGS_PROFILE_MAX ((SETTINGS_MAXSIZE)/(SETTINGS_PADSIZE)) +#define SETTINGS_VERSION 0x0000 -#define SETTINGS_WATCHES_MAX 14 -#define SETTINGS_TELEPORT_MAX 7 -#define SETTINGS_MEMFILE_MAX 10 -#define SETTINGS_BIND_MAX COMMAND_MAX +#define SETTINGS_WATCHES_MAX 18 +#define SETTINGS_TELEPORT_MAX 9 +#define SETTINGS_MEMFILE_MAX 10 +#define SETTINGS_BIND_MAX COMMAND_MAX -#define SETTINGS_LAG_FRAMES 0 -#define SETTINGS_LAG_SECONDS 1 +#define SETTINGS_LAG_FRAMES 0 +#define SETTINGS_LAG_SECONDS 1 + +#define SETTINGS_BREAK_NORMAL 0 +#define SETTINGS_BREAK_AGGRESSIVE 1 enum cheats { @@ -35,6 +38,7 @@ enum cheats CHEAT_FREEZETIME, CHEAT_NOMUSIC, CHEAT_USEITEMS, + CHEAT_NOMAP, CHEAT_MAX, }; @@ -43,11 +47,7 @@ enum commands COMMAND_MENU, COMMAND_RETURN, COMMAND_BREAK, - COMMAND_VOID, - COMMAND_RELOAD, - COMMAND_FILESELECT, COMMAND_LEVITATE, - COMMAND_TURBO, COMMAND_SAVEPOS, COMMAND_LOADPOS, COMMAND_SAVEMEMFILE, @@ -57,6 +57,20 @@ enum commands COMMAND_RESETTIMER, COMMAND_PAUSE, COMMAND_ADVANCE, + COMMAND_FILESELECT, + COMMAND_RELOAD, + COMMAND_VOID, + COMMAND_TURBO, + COMMAND_FALL, + COMMAND_AGE, + COMMAND_STARTTIMER, + COMMAND_STOPTIMER, + COMMAND_PREVPOS, + COMMAND_NEXTPOS, + COMMAND_PREVFILE, + COMMAND_NEXTFILE, + COMMAND_PREVROOM, + COMMAND_NEXTROOM, COMMAND_MAX, }; @@ -75,7 +89,10 @@ struct menu_settings uint16_t lag_counter : 1; uint16_t lag_unit : 1; uint16_t timer : 1; + uint16_t pause_display : 1; + uint16_t break_type : 1; uint16_t warp_age : 1; + uint16_t warp_cutscene : 5; }; struct settings_data diff --git a/src/gz/z64.h b/src/gz/z64.h index 4ecab714..900b9e0f 100644 --- a/src/gz/z64.h +++ b/src/gz/z64.h @@ -265,10 +265,12 @@ typedef struct uint16_t magic_bar_x; /* 0x0AF6 */ uint16_t magic_bar_y; /* 0x0AF8 */ uint16_t magic_fill_x; /* 0x0AFA */ - char unk_12_[0x0498]; /* 0x0AFC */ + char unk_12_[0x02D6]; /* 0x0AFC */ + int16_t minimap_disabled; /* 0x0DD2 */ + char unk_13_[0x01C0]; /* 0x0DD4 */ uint16_t item_ammo_x[4]; /* 0x0F94 */ uint16_t item_ammo_y[4]; /* 0x0F9C */ - char unk_13_[0x0008]; /* 0x0FA4 */ + char unk_14_[0x0008]; /* 0x0FA4 */ uint16_t item_icon_space[4]; /* 0x0FAC */ uint16_t item_button_space[4]; /* 0x0FB4 */ /* 0x0FBC */ @@ -279,7 +281,7 @@ typedef struct int32_t entrance_index; /* 0x0000 */ int32_t link_age; /* 0x0004 */ char unk_00_[0x0002]; /* 0x0008 */ - int16_t cutscene_index; /* 0x000A */ + uint16_t cutscene_index; /* 0x000A */ uint16_t day_time; /* 0x000C */ char unk_01_[0x0002]; /* 0x000E */ int32_t night_flag; /* 0x0010 */ @@ -444,9 +446,12 @@ typedef struct uint32_t unk_flags_4; /* 0x1380 */ char unk_12_[0x0044]; /* 0x1384 */ uint16_t nayrus_love_timer; /* 0x13C8 */ - char unk_13_[0x0008]; /* 0x13CA */ - uint16_t timer; /* 0x13D2 */ - char unk_14_[0x000C]; /* 0x13D4 */ + char unk_13_[0x0004]; /* 0x13CA */ + int16_t timer_1_state; /* 0x13CE */ + int16_t timer_1_value; /* 0x13D0 */ + int16_t timer_2_state; /* 0x13D2 */ + int16_t timer_2_value; /* 0x13D4 */ + char unk_14_[0x000A]; /* 0x13D6 */ int8_t seq_index; /* 0x13E0 */ char unk_15_[0x0022]; /* 0x13E1 */ uint8_t minimap_index; /* 0x1403 */ diff --git a/src/gz/zu.c b/src/gz/zu.c index a4918e16..b0e53324 100644 --- a/src/gz/zu.c +++ b/src/gz/zu.c @@ -288,13 +288,16 @@ void zu_void(void) zu_execute_game(z64_file.void_entrance, 0x0000); } -void zu_execute_game(int16_t entrance_index, int16_t cutscene_index) +void zu_execute_game(int16_t entrance_index, uint16_t cutscene_index) { - if (z64_file.entrance_index != z64_game.entrance_index) { + if (entrance_index != z64_file.entrance_index || + cutscene_index != z64_file.cutscene_index) + { z64_file.seq_index = -1; zu_setmusic(0x101E00FF); } zu_setmusic(0x111E00FF); + zu_setmusic(0x131E00FF); z64_file.entrance_index = entrance_index; z64_file.cutscene_index = cutscene_index; z64_file.interface_flag = 0; diff --git a/src/gz/zu.h b/src/gz/zu.h index bffa3f00..741e6325 100644 --- a/src/gz/zu.h +++ b/src/gz/zu.h @@ -90,7 +90,7 @@ void zu_sram_write(void *dram_addr, uint32_t sram_addr, size_t size); void zu_void(void); -void zu_execute_game(int16_t entrance_index, int16_t cutscene_index); +void zu_execute_game(int16_t entrance_index, uint16_t cutscene_index); void zu_execute_filemenu(void); void zu_setmusic(uint32_t command);