Skip to content

Instantly share code, notes, and snippets.

@charasyn
Last active November 7, 2020 21:35
Show Gist options
  • Save charasyn/86eea71e55dfbda8de607aa4f56ec4d4 to your computer and use it in GitHub Desktop.
Save charasyn/86eea71e55dfbda8de607aa4f56ec4d4 to your computer and use it in GitHub Desktop.
bsnes-as code for ALTTP USA 1.2 to enable warping in the overworld
// The main concept here is that we will create a backup of all of these addresses
// when the "Save Position" button is pressed.
// When the user hits the "Warp to Position" button, then we will switch to
// the flute gamemode, and restore this data in the middle of the flute routine.
// This will cause the user to warp to the position we saved, instead of a
// predetermined flute point.
// This list was built by examining the flute routines. All of these values
// are read from tables in the ROM when fluting normally.
array<uint32> position_addr = {
0x7E00E6,
0x7E00E8,
0x7E0122,
0x7E0124,
0x7E00E0,
0x7E00E2,
0x7E011E,
0x7E0120,
0x7E0020,
0x7E0022,
0x7E0624,
0x7E0626,
0x7E0628,
0x7E062A,
0x7E008A,
0x7E040A,
0x7E0084,
0x7E0088,
0x7E0086,
0x7E0618,
0x7E061A,
0x7E061C,
0x7E061E,
0x7E0696,
0x7E0698
};
bool position_ready;
array<uint16> position_data;
// Some global flags used to coordinate different parts of the code
bool save_pos; // Set from GUI
bool warp_to_pos; // Set from GUI
bool overwrite_flute_with_saved_data; // Set from code below
bool skip_bird; // Set from code below
void post_power(bool _reset) {
// This should be the same interceptor as used in ALTTPO normally
cpu::register_pc_interceptor(0x008053, @on_main_alttp);
// This additional interceptor is required to overwrite the position data
// in the middle of the flute routine
cpu::register_pc_interceptor(0x02eccc, @on_flute);
// This interceptor is just to avoid having the bird show up when restoring position.
// $53982
cpu::register_pc_interceptor(0x0ab982, @on_bird);
// Set up global flags used to enable/disable parts of the code.
// This was hacked together and may be greatly improvable
save_pos = false;
warp_to_pos = false;
overwrite_flute_with_saved_data = false;
skip_bird = false;
// Initialize position_data to blank array.
position_ready = false;
position_data = array<uint16>(position_addr.length());
@mainwindow = MainWindow();
}
void on_main_alttp(uint32 pc) {
if (save_pos) {
save_pos = false;
// Save position data into array.
for (int i = 0; i < position_addr.length(); i += 1) {
uint32 addr = position_addr[i];
uint16 data = bus::read_u16(addr);
position_data[i] = data;
}
position_ready = true;
}
if (warp_to_pos) {
warp_to_pos = false;
if (position_ready) {
// Set flag so we will overwrite the position in the flute interceptor.
overwrite_flute_with_saved_data = true;
// Also skip the bird animation.
skip_bird = true;
///////////////////////////////////////////////////////////////////////
// Set us to be returning from flute
// Return to gamemode 09 (overworld)
bus::write_u8(0x7e010c, 0x09);
// Set current mode:submode to 0e:0a (flute mode)
bus::write_u8(0x7e0010, 0x0e);
bus::write_u8(0x7e0011, 0x0a);
// Set flute sub-submode to 6, meaning "OverworldMap_PrepExit".
// This will do a nice fadeout and load the correct graphics, then
// it will go to sub-submode 7, which is "BirdTravel_LoadTargetArea".
// This is read in the "Messaging_BirdTravel" subroutine.
bus::write_u16(0x7e0200, 6);
} else {
message("Unable to warp - no previous position data!");
}
}
}
void on_flute(uint32 pc) {
if (overwrite_flute_with_saved_data) {
overwrite_flute_with_saved_data = false;
// If we have saved position data, restore it.
if (position_ready) {
for (int i = 0; i < position_addr.length(); i += 1) {
uint32 addr = position_addr[i];
uint16 data = position_data[i];
bus::write_u16(addr, data);
}
// A few patches after looking at:
// https://github.com/spannerisms/lttphack/blob/master/src/presets.asm
// "Set link to be in the Overworld"
bus::write_u8(0x7e001b, 0);
// Set light / dark world properly
bus::write_u8(0x7ef3ca, bus::read_u8(0x7e008a) & 0x40);
// "Reset which BG to interact with"
bus::write_u8(0x7e00ee, 0);
} else {
message("Unable to overwrite in flute routine - no previous position data!");
}
}
}
void on_bird(uint32 pc) {
if (skip_bird) {
// We just skip over the JSL that spawns the bird
cpu::r.pc = pc + 4;
}
}
MainWindow @mainwindow;
class MainWindow {
private GUI::Window @window;
private GUI::Button @btnSavePos;
private GUI::Button @btnWarpToPos;
MainWindow() {
@window = GUI::Window(140,32,true);
window.title = "Control Window";
window.size = GUI::Size(300, 300);
auto vl = GUI::VerticalLayout();
vl.setSpacing();
vl.setPadding(5,5);
window.append(vl);
@btnSavePos = GUI::Button();
btnSavePos.text = "Save Position";
btnSavePos.onActivate(@GUI::Callback(btnSavePosCallback));
vl.append(btnSavePos, GUI::Size(-1,-1));
@btnWarpToPos = GUI::Button();
btnWarpToPos.text = "Warp to Position";
btnWarpToPos.onActivate(@GUI::Callback(btnWarpToPosCallback));
vl.append(btnWarpToPos, GUI::Size(-1,-1));
window.visible = true;
window.setFocused();
}
private void btnSavePosCallback() {
save_pos = true;
}
private void btnWarpToPosCallback() {
warp_to_pos = true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment