diff --git a/patches/ips/crash_handle_autoreserves.ips b/patches/ips/crash_handle_autoreserves.ips new file mode 100644 index 000000000..1813e5068 Binary files /dev/null and b/patches/ips/crash_handle_autoreserves.ips differ diff --git a/patches/ips/crash_handle_base.ips b/patches/ips/crash_handle_base.ips new file mode 100644 index 000000000..e6be8eb7c Binary files /dev/null and b/patches/ips/crash_handle_base.ips differ diff --git a/patches/ips/crash_handle_springball.ips b/patches/ips/crash_handle_springball.ips new file mode 100644 index 000000000..0cb943754 Binary files /dev/null and b/patches/ips/crash_handle_springball.ips differ diff --git a/patches/ips/crash_handle_xmode.ips b/patches/ips/crash_handle_xmode.ips new file mode 100644 index 000000000..f15618ca7 Binary files /dev/null and b/patches/ips/crash_handle_xmode.ips differ diff --git a/patches/ips/crash_handle_yapping.ips b/patches/ips/crash_handle_yapping.ips new file mode 100644 index 000000000..17457d6bc Binary files /dev/null and b/patches/ips/crash_handle_yapping.ips differ diff --git a/patches/ips/vanilla_bugfixes.ips b/patches/ips/vanilla_bugfixes.ips index 0bf3f141b..d905effe4 100644 Binary files a/patches/ips/vanilla_bugfixes.ips and b/patches/ips/vanilla_bugfixes.ips differ diff --git a/patches/rom_map/Bank 80.txt b/patches/rom_map/Bank 80.txt index 26575e8d3..26292fe90 100644 --- a/patches/rom_map/Bank 80.txt +++ b/patches/rom_map/Bank 80.txt @@ -6,7 +6,7 @@ D140 - D200: oob_death.asm D200 - D240: vanilla_bugfixes.asm D240 - D310: stats.asm D310 - D330: fast_reload.asm -D330 - D340: [FREE] +D330 - D340: crash_handle_base.asm [also referenced in patch.rs] D340 - DA00: reserve_hud.asm DA00 - DD00: msu1.asm DD00 - E100: Palette pointer table for Mosaic diff --git a/patches/rom_map/Bank 82.txt b/patches/rom_map/Bank 82.txt index 2d0926b14..861abc1cf 100644 --- a/patches/rom_map/Bank 82.txt +++ b/patches/rom_map/Bank 82.txt @@ -1,21 +1,21 @@ C262 - C27D: disableable_etanks.asm -C27D - C298: -C2A6 - C2B7: +C27D - C298: vanilla_bugfixes.asm +C2A6 - C2B7: split_speed.asm C362 - C369: C37E - C385: C3B6 - C3BD: map_area.asm C3C4 - C3D9: map_area.asm C411 - C42D: map_area.asm -C42D - C465: +C42D - C465: split_speed.asm F70F - F810: map_area.asm -F810 - F830: vanilla_bugfixes.asm +F810 - F830: crash_handle_springball.asm F830 - F9E0: disable_etanks.asm F9E0 - FA00: reserve_backward_fill.asm FA00 - FA80: alternate_door_colors.asm FA80 - FA90: fix_water_fx_bug.asm FA90 - FBB0: seed_hash_display.asm FBB0 - FBF0: fix_kraid_hud.asm -FBF0 - FC30: vanilla_bugfixes.asm +FBF0 - FC30: crash_handle_autoreserve.asm FC30 - FC50: fix_transition_bad_tiles.asm FC50 - FCD0: map_area.asm FCD0 - FD00: escape.asm diff --git a/patches/rom_map/Bank 83.txt b/patches/rom_map/Bank 83.txt index 46816f635..a10d6c739 100644 --- a/patches/rom_map/Bank 83.txt +++ b/patches/rom_map/Bank 83.txt @@ -3,8 +3,7 @@ AD66 - AD72: escape_autosave.asm B000 - B700: [FREE] B700 - B800: rng_fix.asm B800 - BA00: Mosaic (Area FX.asm) -BA00 - BA15: vanilla_bugfixes.asm -BA15 - BB00: [FREE] +BA00 - BB00: [FREE] BB00 - BC40: disableable_etanks.asm BC40 - E000: [FREE] E000 - F000: item_dots_disappear.asm diff --git a/patches/rom_map/Bank 84.txt b/patches/rom_map/Bank 84.txt index 934cc1b76..d141231be 100644 --- a/patches/rom_map/Bank 84.txt +++ b/patches/rom_map/Bank 84.txt @@ -1,3 +1,4 @@ +$858C - $85B2: vanilla_bugfixes.asm (unused vanilla code, repurposed for main street save station plm check.) $EFD3 - $EFD7: load_plms_early.asm $EFD7 - $F000: vanilla_bugfixes.asm $F000 - $F0E2: walljump_plm.asm diff --git a/patches/rom_map/Bank 85.txt b/patches/rom_map/Bank 85.txt index 318025ef4..48bec0a6f 100644 --- a/patches/rom_map/Bank 85.txt +++ b/patches/rom_map/Bank 85.txt @@ -16,7 +16,10 @@ $AA00 - $AAA0: pause_menu_objectives.asm $AAA0 - $AB00: [FREE] $AB00 - $ACA0: map_area.asm $ACA0 - $AD00: load_flash_suit -$AD00 - $AE20: [FREE] +$AD00 - $AD43: crash_handle_springball.asm +$AD43 - $AE20: [FREE] $AE20 - $B000: reserve_backward_fill.asm -$B000 - $B600: vanilla_bugfixes.asm +$B000 - $B5B4: crash_handle_base.asm +$B5B4 - $B5F1: crash_handle_yapping.asm +$B5F1 - $B600: [free] $B600 - $BA00: map_area.asm \ No newline at end of file diff --git a/patches/rom_map/Bank 90.txt b/patches/rom_map/Bank 90.txt index f47e31b18..3901f8d28 100644 --- a/patches/rom_map/Bank 90.txt +++ b/patches/rom_map/Bank 90.txt @@ -7,6 +7,6 @@ $F900 - $F910: escape_timer.asm $F980 - $FA00: respin.asm $FA00 - $FC00: map_progress_maintain.asm (list of cross-area tiles to reveal) $FC00 - $FC10: Fake Lava.asm -$FC10 - $FC20: vanilla_bugfixes.asm +$FC10 - $FC20: crash_handle_yapping.asm $FC20 - $FC40: remove_spikesuit.asm $FC40 - $FD00: split_speed.asm \ No newline at end of file diff --git a/patches/rom_map/Bank 91.txt b/patches/rom_map/Bank 91.txt index f393e4660..7641f5073 100644 --- a/patches/rom_map/Bank 91.txt +++ b/patches/rom_map/Bank 91.txt @@ -1,4 +1,4 @@ +95bc - 965a: {unused vanilla code - demo instructions} - crash_handle_xmode.asm F7F4 - F88C: {unused vanilla code} - split_speed.asm FC42 - FC66: {unsued vanilla code} - split_speed.asm - -FFEE - FFFE: mosaic\fake lava.asm +FFEE - FFFE: mosaic\fake lava.asm \ No newline at end of file diff --git a/patches/rom_map/RAM.txt b/patches/rom_map/RAM.txt index 9b4919b17..da84caccd 100644 --- a/patches/rom_map/RAM.txt +++ b/patches/rom_map/RAM.txt @@ -1,5 +1,6 @@ $95: temporary variable (unused debug location) - used by extended messageboxes, safe to use outside message boxes. $9B: decompression.asm +$CF: vanilla_bugfixes.asm (used by crash dialogues to prevent unpause from resetting gamestate.) $09EC: number of disabled ETanks (saved to SRAM) $0A1A: previous reserve health (reserve_hud.asm) $1F5B: map area (like $079F) @@ -44,7 +45,8 @@ $7EF594-$7EF596: fix_transition_bad_tiles.asm $7EF596: vanilla_bugfixes.asm $7EF597-$7EF599: split_speed.asm $7EF59A-$7EF59C: reserve_backward_fill.asm -$7EF59C-$7EFE00: [FREE] +$7EF59C-$7EF59E: crash_handle_xmode.asm +$7EF59E-$7EFE00: [FREE] $7EFE00: map area (copy of $1F5B to be saved to SRAM) $7EFE02: map area explored mask $7EFE04: copy of $1F5D (to be saved to SRAM for escape autosave) diff --git a/patches/rom_map/vanilla_hooks.txt b/patches/rom_map/vanilla_hooks.txt index f406e1ba1..33ab55fbe 100644 --- a/patches/rom_map/vanilla_hooks.txt +++ b/patches/rom_map/vanilla_hooks.txt @@ -990,6 +990,7 @@ $909e88 - walljump_plm.asm [BANK 91] $9181f4 - aim_anything.asm +$91cafe - crash_handle_xmode.asm $91e164 - fast_reload.asm $91e604 - fix_hyper_slowlock.asm $91deba - gravity_palette.asm diff --git a/patches/src/constants.asm b/patches/src/constants.asm index 483e01ffc..cef3b0386 100644 --- a/patches/src/constants.asm +++ b/patches/src/constants.asm @@ -56,3 +56,10 @@ ; target number of frames for the pause menu black screen to lag ; (to compensate for VRAM decompression being faster): !unpause_black_screen_lag_frames = $dfff07 + +; crash handler / sub patches variables + +!crash_toggles = $80D330 ; overwritten by patch.rs , defined in crash_handle_base.asm + +!kill_samus = $80D337 ; defined in crash_handle_base.asm +!bug_dialog = $80D332 ; defined in crash_handle_base.asm diff --git a/patches/src/crash_handle_autoreserves.asm b/patches/src/crash_handle_autoreserves.asm new file mode 100644 index 000000000..75bb1aa1a --- /dev/null +++ b/patches/src/crash_handle_autoreserves.asm @@ -0,0 +1,45 @@ +;;; Fix auto-reserve / pause bug +;;; +;;; This patch will initiate the death sequence if pause hit with auto-reserve enabled +;;; on exact frame that leads to crash. +;;; +;;; (thanks to Benox50 for his initial patch, nn44/aquanight for the light pillar fix) +;;; + + +incsrc "constants.asm" + +!bank_82_free_space_start = $82fbf0 ; pause / reserve bug +!bank_82_free_space_end = $82fc30 + + +;;; vanilla hooks + +org $828b3f + jsr pause_func : nop ; gamestate 1Ch (unused) handler + + +;;; custom code + +org !bank_82_free_space_start +pause_func: + lda !crash_toggles + and #$00F0 + beq .default + cmp #$0020 + beq .fix +.warn + lda #$0041 ; "fix" leaves the screen black like any pause close to fadeout, warning will relight the screen due to showing the dialog, could be changed to darken the screen again but it might be confusing. + jsl !bug_dialog +.fix + lda #$0008 + sta $0998 + rts +.default + lda #$0041 ; msg ID + jsl !bug_dialog + jsl !kill_samus + stz $9d6 ; clear reserve health + rts + +assert pc() <= !bank_82_free_space_end diff --git a/patches/src/crash_handle_base.asm b/patches/src/crash_handle_base.asm new file mode 100644 index 000000000..ef2d64da3 --- /dev/null +++ b/patches/src/crash_handle_base.asm @@ -0,0 +1,186 @@ +;;; crash handler (displays a custom message box explaining the crash) +;;; StagShot, toggle modifications - nn_357 +;;; +;;; Implementation of custom dialog boxes +;;; Requires hooking multiple functions to support extended msg IDs (0x40+) +;;; and additional lookup tables +;;; +;;; This code is used by other patches that handle hardlocks caused by the unequip springball, frame perfect pause on autoreserve trigger, yappingmaw shinespark +;;; X-mode solid tile collision. + +arch snes.cpu +lorom + +incsrc "constants.asm" + +!bank_80_free_space_start = $80D332 ; springboard for the sub patches.. They all point here for the crash loader / kill samus +!bank_80_free_space_end = $80D340 ; if moving the crash handler base patch then this needs to be changed too. + +!bank_85_free_space_start = $85b000 ; +!bank_85_free_space_end = $85b5b4 + +!msg_crash_timer_override = $7EF596 ; temporary variable used for overriding messagebox close delay times during crash box. + +;;; global code. +org !crash_toggles ; default at 80d330, srpringboard follows. + dw $0000 ; overwritten by patch.rs + +org !bank_80_free_space_start + jsl bug_dialog + rtl + jsl kill_samus + rtl + +assert pc() <= !bank_80_free_space_end + +;;; hooks into vanilla code + +org $858493 ; override messagebox delay if crash dialog + jsr hook_msgbox_delay + +org $858093 + jsr hook_message_box + +org $8582e5 + jsr hook_index_lookup + +org $8582ee + jsr hook_message_table + +org $85840c ; hook unpause to prevent resetting gamestate to 8 if crash ID set + jsr hook_button_lookup + +;;; custom code + +org !bank_85_free_space_start +bug_dialog: ; A = msg ID + and #$00ff + pha + sep #$20 + lda #$0f ; restore screen brightness to full + sta $51 + sta !msg_crash_timer_override ; messagebox timer will check if this is 0 (if its non zero load a longer time) + rep #$30 + jsl $808338 ; wait for NMI + + pla ; dlg box parameter + jsl $858080 ; dlg box + cmp #$0044 + bne .skipkill ; oob death (dlg 44) is removable via major glitches patch, if its thrown then the intent is to kill as it isn't toggleable. + jsl kill_samus +.skipkill + rtl + +hook_message_box: + rep #$30 + lda $1c1f + cmp #$0040 ; custom boxes >= 0x40 + bcs .custom + jmp $8241 ; original func + +.custom + ldx #(new_message_boxes-$869b) ; ptr for extended lookup table + jmp $824f + +hook_index_lookup: + lda $1c1f + cmp #$0040 + bcs .custom + rts + +.custom + sec + sbc #$0040 + rts + +hook_message_table: + adc $34 ; replaced code + tax ; + lda $1c1f + cmp #$0040 + bcs .custom + rts + +.custom + txa + clc + adc #(new_message_boxes-$869b) ; adjust ptr for extended table + tax + rts + +hook_button_lookup: + lda $1c1f + cmp #$0040 + bcs .custom + rts + +.custom + lda #$0001 ; blank button tilemap + ldy #(reserve_pause_msg-$8426) ; blank button letter + rts + +hook_msgbox_delay: + pha + lda !msg_crash_timer_override + beq .nochange + ldx #$005a ; (put 1.5 seconds on the clock) + lda #$00 + sta !msg_crash_timer_override ; clear our special variable so then next msgbox will have whatever timer was set on generation +.nochange + pla + jsr $8136 ; hi-jacked instruction. + rts + +; custom messages start at 0x41 +new_message_boxes: + dw $83c5, $825a, reserve_pause_msg ; 0x41 + dw $83c5, $825a, springball_msg ; 0x42 + dw $83c5, $825a, yapping_maw_msg ; 0x43 + dw $83c5, $825a, oob_msg ; 0x44 + dw $83c5, $825a, xmode_msg ; 0x45 + dw $0000, $0000, msg_end + +table "tables/dialog_chars.tbl",RTL + +reserve_pause_msg: + dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e + dw $000e,$000e,$000e, " ", $000e,$000e,$000e + dw $000e,$000e,$000e, " PAUSED ON EXACT FRAME ", $000e,$000e,$000e + dw $000e,$000e,$000e, " AUTO-REFILL STARTED! ", $000e,$000e,$000e + +springball_msg: + dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e + dw $000e,$000e,$000e, " ", $000e,$000e,$000e + dw $000e,$000e,$000e, " UNEQUIPPED SPRING BALL ", $000e,$000e,$000e + dw $000e,$000e,$000e, " IN NEUTRAL BOUNCE! ", $000e,$000e,$000e + +yapping_maw_msg: + dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e + dw $000e,$000e,$000e, " ", $000e,$000e,$000e + dw $000e,$000e,$000e, " YAPPING MAW SHINESPARK ", $000e,$000e,$000e + dw $000e,$000e,$000e, " END WITH NO INPUTS HELD! ", $000e,$000e,$000e + +oob_msg: + dw $000e,$000e,$000e, " ", $000e,$000e,$000e + dw $000e,$000e,$000e, " SAMUS OUT-OF-BOUNDS! ", $000e,$000e,$000e + dw $000e,$000e,$000e, " ", $000e,$000e,$000e + dw $000e,$000e,$000e, " ", $000e,$000e,$000e + +xmode_msg: + dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e + dw $000e,$000e,$000e, " ", $000e,$000e,$000e + dw $000e,$000e,$000e, " X-MODE TILE COLLISION ", $000e,$000e,$000e + dw $000e,$000e,$000e, "COLLIDED WITH A SOLID TILE", $000e,$000e,$000e + +msg_end: + +kill_samus: + lda #$8000 ; init death sequence (copied from $82db80) + sta $a78 + lda #$0011 + jsl $90f084 + lda #$0013 ; set gamestate + sta $998 + rtl + +assert pc() <= !bank_85_free_space_end \ No newline at end of file diff --git a/patches/src/crash_handle_springball.asm b/patches/src/crash_handle_springball.asm new file mode 100644 index 000000000..e7d322ccd --- /dev/null +++ b/patches/src/crash_handle_springball.asm @@ -0,0 +1,83 @@ +;;; Spring ball menu crash fix by strotlog. +;;; Fix obscure vanilla bug where: turning off spring ball while bouncing, can crash in $91:EA07, +;;; or exactly the same way as well in $91:F1FC. +;;; Adapted for map rando by Stag Shot: +;;; toggle-modifications nn_357. + +arch snes.cpu +lorom + +incsrc "constants.asm" + + +!bank_82_free_space_start = $82f810 ; hook unpause (springball crash) +!bank_82_free_space_end = $82f830 + +!bank_85_free_space_start = $85ad00 +!bank_85_free_space_end = $85ad45 + +;;; vanilla hooks + +org $8293bb + jmp check_unpause + +org $91ea07 + jsl spring_ball_crash + +org $91f1fc + jsl spring_ball_crash + +;;; custom code + + +org !bank_82_free_space_start +check_unpause: + php + sep #$20 + lda $00cf ; pending crash ID + stz $00cf + cmp #$42 ; springball? + bne .skip + plp + jmp $93c1 ; skip changing gamestate +.skip + plp + lda #$0008 ; replaced code + jmp $93be + +assert pc() <= !bank_82_free_space_end + + +org !bank_85_free_space_start +spring_ball_crash: + lda $0B20 ; morph bounce state + cmp #$0600 ; bugged? + bcc .skip + lda !crash_toggles + and #$000F + beq .default + cmp #$0002 + beq .fix +.warn + lda #$0042 + jsl !bug_dialog +.fix + lda #$0000 + stz $0b20 + rtl +.default + sep #$20 + lda #$42 ; bug ID + sta $00cf ; set flag to prevent unpause from resetting gamestate to 8 + rep #$30 + jsl !bug_dialog + jsl !kill_samus + lda #$0000 + stz $0B20 + rtl +.skip + lda $0B20 ; replaced code + asl ; + rtl + +assert pc() <= !bank_85_free_space_end diff --git a/patches/src/crash_handle_xmode.asm b/patches/src/crash_handle_xmode.asm new file mode 100644 index 000000000..1b436d44f --- /dev/null +++ b/patches/src/crash_handle_xmode.asm @@ -0,0 +1,69 @@ +;;; X-Mode collision fix - nn_357 +;;; rewrite original input handler for solid tile collision to free up space. +;;; thanks to StagShot for noticing the rep$30 isn't needed here, it's already initialized by the single caller to this function. +;;; whole fix can now fit in the original function. + +arch snes.cpu +lorom + +incsrc "constants.asm" + +;;; these variable are defined by the crash_handle_base.asm patch and patch.rs + + +!bank_91_free_space_start = $9195bc +!bank_91_free_space_end = $91965a + +!shown_warning = $7EF59C ; whether a warning has already been shown since X-Ray initialized + +org $91816f +xmodefix: + php + jsr $81a9 ; this fixes regular xmode collision by using the correct pose lookup routine for all collisions + lda $0a78 ; load frozen time variable + beq .skip ; if time is NOT frozen skip over the next instruction (jump to xmode crash handler.) + lda #$0045 ; load the msg bug id for X-Mode Collision + jsr xmode ; now in same bank so uses a jsr. +.skip + plp + rts + +assert pc() <= $918181 ; Make sure we don't overwrite the next routine. + +org $91cafe + jmp hook_setup_xray +return_setup_xray: + +;;; +;;; custom code - currently only using up to {9195D2} {approx 130bytes free, reserved for more future xmode warn fix.} +;;; +org !bank_91_free_space_start +xmode: + lda !crash_toggles + and #$F000 + beq .default + cmp #$2000 + beq .fix +.warn + lda !shown_warning + bne .fix + inc + sta !shown_warning ; set !shown_warning to 1 + lda #$0045 + jsl !bug_dialog +.fix + rts +.default + lda #$0045 + jsl !bug_dialog + jsl !kill_samus + rts + +; initialize shown_warning to 0 when starting to use x-ray +hook_setup_xray: + sta $0a78 ; run hi-jacked instruction: time-is-frozen flag = 1 + lda #$00 + sta !shown_warning + jmp return_setup_xray + +assert pc() <= !bank_91_free_space_end \ No newline at end of file diff --git a/patches/src/crash_handle_yapping.asm b/patches/src/crash_handle_yapping.asm new file mode 100644 index 000000000..417ff7d1d --- /dev/null +++ b/patches/src/crash_handle_yapping.asm @@ -0,0 +1,66 @@ +; Yapping maw shinespark crash +; Noted by PJBoy: https://patrickjohnston.org/bank/A8#fA68A + + +arch snes.cpu +lorom + +incsrc "constants.asm" + +;;; these variable are defined by the crash_handle_base.asm patch and patch.rs + + +!bank_85_free_space_start = $85b5b4 +!bank_85_free_space_end = $85b5f1 + +!bank_90_free_space_start = $90fc10 +!bank_90_free_space_end = $90fc20 + + +;;; hooks into vanilla code + +org $90d354 + jsr yapping_maw_crash + +;;; custom code + +org !bank_90_free_space_start +yapping_maw_crash: + cmp #$0005 ; valid table entries are 0-2 * 2 + bcc .skip + jsl ym_crash + rts +.skip + jmp ($d37d,x) ; valid entry + +assert pc() <= !bank_90_free_space_end + +org !bank_85_free_space_start +ym_crash: + lda !crash_toggles + and #$0f00 + beq .default + cmp #$0200 + beq .fix +.warn + lda #$0043 ; bug ID + jsl !bug_dialog +.fix + lda #$d3f3 + sta $0a58 + lda #$001e + sta $0aa2 + sta $0ac0 + sta $0ac2 + lda #$0000 + ldx #$0000 + ldy #$0004 + rtl +.default + lda #$0043 ; bug ID + jsl !bug_dialog + jsl !kill_samus + rtl + + +assert pc() <= !bank_85_free_space_end \ No newline at end of file diff --git a/patches/src/vanilla_bugfixes.asm b/patches/src/vanilla_bugfixes.asm index 324172bcb..3a6ec687e 100644 --- a/patches/src/vanilla_bugfixes.asm +++ b/patches/src/vanilla_bugfixes.asm @@ -8,30 +8,36 @@ arch snes.cpu lorom -!bank_80_free_space_start = $80D200 +incsrc "constants.asm" + +!bank_80_free_space_start = $80D200 ; camera alignment fix. !bank_80_free_space_end = $80D240 -!bank_86_free_space_start = $86F4B0 -!bank_86_free_space_end = $86F4D0 -!msg_crash_timer_override = $7EF596 +!bank_82_free_space_start = $82C27D ; reserve icon ui fix. +!bank_82_free_space_end = $82C298 + +!bank_84_free_space_start = $84EFD7 ; maridia tube fix +!bank_84_free_space_end = $84F000 + +!bank_86_free_space_start = $86F4B0 ; powamp projectile fix +!bank_86_free_space_end = $86F4D0 -incsrc "constants.asm" ; Fix the crash that occurs when you kill an eye door whilst a eye door projectile is alive ; See the comments in the bank logs for $86:B6B9 for details on the bug ; The fix here is setting the X register to the enemy projectile index, ; which can be done without free space due to an unnecessary RTS in the original code org $86B704 -BEQ ret -TYX + BEQ ret + TYX org $86B713 -ret: + ret: ;;; skips suits acquisition animation org $848717 ;rep 4 : nop - JSL $91DEBA ; call the load samus suit palette to update the suit. + JSL $91DEBA ; call the load samus suit palette to update the suit. assert pc() <= $84871B ; just to make sure the next instruction isn't overwritten @@ -43,21 +49,24 @@ org $91b629 ;;; fix screw attack select in pause menu org $82b4c4 cpx #$000c + ;;; In inventory menu, when having only a beam and a suit you have ;;; to press right+up to go from beam to suit. ;;; It's not natural, so fix it to only require right. + org $82b000 + bcc $64 ;; test of return of $B4B7 compare A and #$0000, ;; when no item found A==#$ffff, which sets the carry, ;; so when carry is clear it means that an item was found in misc. ;; if no item was found in misc, we check in boots then in suits, ;; so if an item is found in both boots and suits, as suits is ;; tested last the selection will be set on suits. - bcc $64 ;;; fix morph ball in hidden chozo PLM org $84e8ce db $04 + org $84ee02 db $04 @@ -83,6 +92,7 @@ assert pc() <= $a0f830 ;;; allow all possible save slots (needed for area rando extra stations) org $848d0c and #$001f + ;;; For an unknown reason, the save station we put in main street ;;; sometimes (circumstances unclear) spawn two detection PLMs ;;; instead of one. These PLMs are supposed to precisely detect @@ -96,14 +106,18 @@ org $848d0c ;;; hijack in detection block PLM code when samus is ;;; positioned correctly + org $84b5d4 search_loop_start: jmp save_station_check + org $84b5d9 search_loop_cont: + org $84b5df search_loop_found: -;;; some unused bank 84 space + +;;; some unused bank 84 space org $84858c save_station_check: cmp $1c87,x ; original block coord check @@ -119,7 +133,6 @@ save_station_check: pla jmp search_loop_found -;;; end of unused space assert pc() <= $8485b2 @@ -142,7 +155,6 @@ fix_big_boy: org $A9EF80 .done - ; Fix Bomb Torizo crumbling animation (which can be very messed up if the player earlier visited a room ; that maxed out enemy projectiles) org $86A8FD @@ -207,6 +219,7 @@ check_item_plm: rts assert pc() <= $848398 + org $848398 special_xray_end: @@ -216,27 +229,6 @@ special_xray_end: org $b4bda3 bpl $f8 ; was bne $f8 -; Fix auto-reserve / pause bug -; -; This patch will initiate the death sequence if pause hit with auto-reserve enabled -; on exact frame that leads to crash. -; -; (thanks to Benox50 for his initial patch, nn44/aquanight for the light pillar fix) - -!bank_82_free_space_start = $82fbf0 -!bank_82_free_space_end = $82fc30 - -org $828b3f - jsr pause_func : nop ; gamestate 1Ch (unused) handler - -org !bank_82_free_space_start -pause_func: - lda #$0041 ; msg ID - jsl bug_dialog - stz $9d6 ; clear reserve health - rts - -assert pc() <= !bank_82_free_space_end ; Fix for powamp projectile bug ; @@ -275,236 +267,6 @@ assert pc() <= !bank_86_free_space_end org $80a27a lda #$a29b -; Yapping maw shinespark crash -; Noted by PJBoy: https://patrickjohnston.org/bank/A8#fA68A -; If bug triggered, show dialog box and then initiate death sequence. - -org $90d354 - jsr yapping_maw_crash - -!bank_90_free_space_start = $90fc10 -!bank_90_free_space_end = $90fc20 - -org !bank_90_free_space_start -yapping_maw_crash: - cmp #$0005 ; valid table entries are 0-2 * 2 - bcc .skip - lda #$0043 ; bug ID - jsl bug_dialog - rts - -.skip - jmp ($d37d,x) ; valid entry - -assert pc() <= !bank_90_free_space_end - -;;; Spring ball menu crash fix by strotlog. -;;; Fix obscure vanilla bug where: turning off spring ball while bouncing, can crash in $91:EA07, -;;; or exactly the same way as well in $91:F1FC. -;;; Adapted for map rando by Stag Shot: -;;; If bug triggered, show dialog box and then initiate death sequence. - -org $91ea07 - jsl spring_ball_crash - -org $91f1fc - jsl spring_ball_crash - -!bank_85_free_space_start = $85b000 ; do not change, first jmp used externally -!bank_85_free_space_end = $85b5FF - -org !bank_85_free_space_start - jmp bug_dialog ; for external calls, do not move - -spring_ball_crash: - lda $0B20 ; morph bounce state - cmp #$0600 ; bugged? - bcc .skip - sep #$20 - lda #$42 ; bug ID - sta $00cf ; set flag to prevent unpause from resetting gamestate to 8 - rep #$30 - jsl bug_dialog - lda #$0000 - stz $0B20 - rtl - -.skip - lda $0B20 ; replaced code - asl ; - rtl - -;;; Implementation of custom dialog boxes -;;; Requires hooking multiple functions to support extended msg IDs (0x40+) -;;; and additional lookup tables - -bug_dialog: ; A = msg ID - and #$00ff - pha - sep #$20 - lda #$0f ; restore screen brightness to full - sta $51 - sta !msg_crash_timer_override ; messagebox timer will check if this is 0 (if its non zero load a longer time) - rep #$30 - jsl $808338 ; wait for NMI - - pla ; dlg box parameter - jsl $858080 ; dlg box - - lda #$8000 ; init death sequence (copied from $82db80) - sta $a78 - lda #$0011 - jsl $90f084 - - lda #$0013 ; set gamestate - sta $998 - rtl - -hook_message_box: - rep #$30 - lda $1c1f - cmp #$0040 ; custom boxes >= 0x40 - bcs .custom - jmp $8241 ; original func - -.custom - ldx #(new_message_boxes-$869b) ; ptr for extended lookup table - jmp $824f - -hook_index_lookup: - lda $1c1f - cmp #$0040 - bcs .custom - rts - -.custom - sec - sbc #$0040 - rts - -hook_message_table: - adc $34 ; replaced code - tax ; - lda $1c1f - cmp #$0040 - bcs .custom - rts - -.custom - txa - clc - adc #(new_message_boxes-$869b) ; adjust ptr for extended table - tax - rts - -hook_button_lookup: - lda $1c1f - cmp #$0040 - bcs .custom - rts - -.custom - lda #$0001 ; blank button tilemap - ldy #(reserve_pause_msg-$8426) ; blank button letter - rts - -; custom messages start at 0x41 -new_message_boxes: - dw $83c5, $825a, reserve_pause_msg ; 0x41 - dw $83c5, $825a, springball_msg ; 0x42 - dw $83c5, $825a, yapping_maw_msg ; 0x43 - dw $83c5, $825a, oob_msg ; 0x44 - dw $83c5, $825a, xmode_msg ; 0x45 - dw $0000, $0000, msg_end - -table "tables/dialog_chars.tbl",RTL - -reserve_pause_msg: - dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e - dw $000e,$000e,$000e, " ", $000e,$000e,$000e - dw $000e,$000e,$000e, " PAUSED ON EXACT FRAME ", $000e,$000e,$000e - dw $000e,$000e,$000e, " AUTO-REFILL STARTED! ", $000e,$000e,$000e - -springball_msg: - dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e - dw $000e,$000e,$000e, " ", $000e,$000e,$000e - dw $000e,$000e,$000e, "UNEQUIPPED SPRING BALL IN ", $000e,$000e,$000e - dw $000e,$000e,$000e, "UNDERWATER NEUTRAL BOUNCE!", $000e,$000e,$000e - -yapping_maw_msg: - dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e - dw $000e,$000e,$000e, " ", $000e,$000e,$000e - dw $000e,$000e,$000e, " YAPPING MAW SHINESPARK ", $000e,$000e,$000e - dw $000e,$000e,$000e, " END WITH NO INPUTS HELD! ", $000e,$000e,$000e - -oob_msg: - dw $000e,$000e,$000e, " ", $000e,$000e,$000e - dw $000e,$000e,$000e, " SAMUS OUT-OF-BOUNDS! ", $000e,$000e,$000e - dw $000e,$000e,$000e, " ", $000e,$000e,$000e - dw $000e,$000e,$000e, " ", $000e,$000e,$000e - -xmode_msg: - dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e - dw $000e,$000e,$000e, " ", $000e,$000e,$000e - dw $000e,$000e,$000e, " X-MODE TILE COLLISION ", $000e,$000e,$000e - dw $000e,$000e,$000e, "COLLIDED WITH A SOLID TILE", $000e,$000e,$000e - -msg_end: - -hook_msgbox_delay: - pha - lda !msg_crash_timer_override - beq .nochange - ldx #$005a ; (put 1.5 seconds on the clock) - lda #$00 - sta !msg_crash_timer_override ; clear our special variable so then next msgbox will have whatever timer was set on generation -.nochange - pla - jsr $8136 ; hi-jacked instruction. - rts - -assert pc() <= !bank_85_free_space_end - - ;$85:8493 20 36 81 JSR $8136 [$85:8136] ;\ -org $858493 ; before we do things, lets double check we are not in a crash dialog (if so we want a longer timer) - jsr hook_msgbox_delay - -org $858093 - jsr hook_message_box - -org $8582e5 - jsr hook_index_lookup - -org $8582ee - jsr hook_message_table - -org $85840c - jsr hook_button_lookup - -; hook unpause to prevent resetting gamestate to 8 if crash ID set - -org $8293bb - jmp check_unpause - -!bank_82_free_space2_start = $82f810 -!bank_82_free_space2_end = $82f830 - -org !bank_82_free_space2_start -check_unpause: - php - sep #$20 - lda $00cf ; pending crash ID - stz $00cf - cmp #$42 ; springball? - bne .skip - plp - jmp $93c1 ; skip changing gamestate -.skip - plp - lda #$0008 ; replaced code - jmp $93be - -assert pc() <= !bank_82_free_space2_end ; Map scrolling bug ; Leftmost edge function @ $829f4a has an off-by-one bug when scanning @@ -514,27 +276,9 @@ assert pc() <= !bank_82_free_space2_end org $829f90 adc #$7c -; X-Mode collision fix - nn_357 - -org $91816f ; rewrite original input handler for solid tile collision to free up space. -- thanks to StagShot for noticing the rep$30 isn't needed here, it's already initialized by the single caller to this function. - ; whole fix can now fit in the original function. - PHP - JSR $81A9 ; this fixes regular xmode collision by using the correct pose lookup routine for all collisions - LDA $0A78 ; load frozen time variable - BEQ $07 ; if time is NOT frozen skip over the next instruction (kill samus) - LDA #$0045 ; load the msg bug id for X-Mode Collision - JSL $85B000 ; collision with solid tile while time is frozen is only possible in x-mode so jump to code that will kill samus rather than hardlock (retain vanilla behaviour) - PLP - RTS - -assert pc() <= $918181 ; Make sure we don't overwrite the next routine. - ; (Maridia Tube Fix - written by AmoebaOfDoom) ;patches horizontal PLM updates to DMA tiles even when the PLM is above the screen if part of it is on the screen -!bank_84_free_space_start = $84EFD7 -!bank_84_free_space_end = $84F000 - org $848DA0 SkipEntry_Inject: JMP SkipEntry @@ -579,28 +323,19 @@ org $84B7EF ; PLM $B8AC (speed booster escape) LDA $09A4 [$7E:09A4] ; vanilla bugfix, ui shows reserve icon when only boots item is equipped. -; - -!bank_83_free_space_start = $83BA00 -!bank_83_free_space_end = $83BA15 - -org !bank_83_free_space_start - -BNE .found -INX -INX -CPX #$0006 -BMI .loop -JML $82AC15 +; this is due to a missing conditional branch instruction. +org $82AC03 + jmp $c27d ; !bank_82_free_space_start [bankless form of this] + nop #2 + +org !bank_82_free_space_start + bne .found + inx + inx + cpx #$0006 + jmp $AC08 .found -JML $82AC0C - -.loop -JML $82AC00 - -assert pc() <= !bank_83_free_space_end + jmp $AC0C -org $82AC03 - JML !bank_83_free_space_start - nop #5 \ No newline at end of file +assert pc() <= !bank_82_free_space_end diff --git a/rust/data/presets/full-settings/Community Race Season 4.json b/rust/data/presets/full-settings/Community Race Season 4.json index 58859f508..b8796bb3c 100644 --- a/rust/data/presets/full-settings/Community Race Season 4.json +++ b/rust/data/presets/full-settings/Community Race Season 4.json @@ -4544,6 +4544,13 @@ "all_items_spawn": true, "acid_chozo": true, "remove_climb_lava": true, + "crash_fixes": { + "preset": "Death", + "spring_ball": "Death", + "yapping_maw": "Death", + "auto_reserve": "Death", + "x_mode": "Death" + }, "etank_refill": "Full", "energy_station_reserves": true, "disableable_etanks": "Standard", diff --git a/rust/data/presets/full-settings/Default.json b/rust/data/presets/full-settings/Default.json index b7818e54d..82825ecb8 100644 --- a/rust/data/presets/full-settings/Default.json +++ b/rust/data/presets/full-settings/Default.json @@ -4544,6 +4544,13 @@ "all_items_spawn": true, "acid_chozo": true, "remove_climb_lava": true, + "crash_fixes": { + "preset": "Death", + "spring_ball": "Death", + "yapping_maw": "Death", + "auto_reserve": "Death", + "x_mode": "Death" + }, "energy_station_reserves": false, "etank_refill": "Vanilla", "disableable_etanks": "Standard", diff --git a/rust/data/presets/quality-of-life/Default.json b/rust/data/presets/quality-of-life/Default.json index d2f626b38..68a728d35 100644 --- a/rust/data/presets/quality-of-life/Default.json +++ b/rust/data/presets/quality-of-life/Default.json @@ -33,6 +33,13 @@ "all_items_spawn": true, "acid_chozo": true, "remove_climb_lava": true, + "crash_fixes": { + "preset": "Death", + "spring_ball": "Death", + "yapping_maw": "Death", + "auto_reserve": "Death", + "x_mode": "Death" + }, "energy_station_reserves": false, "etank_refill": "Vanilla", "disableable_etanks": "Standard", diff --git a/rust/data/presets/quality-of-life/High.json b/rust/data/presets/quality-of-life/High.json index fdcd0b9f9..26bfd1fef 100644 --- a/rust/data/presets/quality-of-life/High.json +++ b/rust/data/presets/quality-of-life/High.json @@ -33,6 +33,13 @@ "all_items_spawn": true, "acid_chozo": true, "remove_climb_lava": true, + "crash_fixes": { + "preset": "Death", + "spring_ball": "Death", + "yapping_maw": "Death", + "auto_reserve": "Death", + "x_mode": "Death" + }, "energy_station_reserves": true, "etank_refill": "Full", "disableable_etanks": "Standard", diff --git a/rust/data/presets/quality-of-life/Low.json b/rust/data/presets/quality-of-life/Low.json index 842fc58f6..9e85813fc 100644 --- a/rust/data/presets/quality-of-life/Low.json +++ b/rust/data/presets/quality-of-life/Low.json @@ -33,6 +33,13 @@ "all_items_spawn": false, "acid_chozo": false, "remove_climb_lava": false, + "crash_fixes": { + "preset": "Death", + "spring_ball": "Death", + "yapping_maw": "Death", + "auto_reserve": "Death", + "x_mode": "Death" + }, "energy_station_reserves": false, "etank_refill": "Vanilla", "disableable_etanks": "Standard", diff --git a/rust/data/presets/quality-of-life/Max.json b/rust/data/presets/quality-of-life/Max.json index a1f389638..c970895ed 100644 --- a/rust/data/presets/quality-of-life/Max.json +++ b/rust/data/presets/quality-of-life/Max.json @@ -33,6 +33,13 @@ "all_items_spawn": true, "acid_chozo": true, "remove_climb_lava": true, + "crash_fixes": { + "preset": "Silent", + "spring_ball": "Silent", + "yapping_maw": "Silent", + "auto_reserve": "Silent", + "x_mode": "Silent" + }, "energy_station_reserves": true, "etank_refill": "Full", "disableable_etanks": "Standard", diff --git a/rust/data/presets/quality-of-life/Off.json b/rust/data/presets/quality-of-life/Off.json index 93d16b12f..9d224a5de 100644 --- a/rust/data/presets/quality-of-life/Off.json +++ b/rust/data/presets/quality-of-life/Off.json @@ -33,6 +33,13 @@ "all_items_spawn": false, "acid_chozo": false, "remove_climb_lava": false, + "crash_fixes": { + "preset": "Death", + "spring_ball": "Death", + "yapping_maw": "Death", + "auto_reserve": "Death", + "x_mode": "Death" + }, "energy_station_reserves": false, "etank_refill": "Vanilla", "disableable_etanks": "Off", diff --git a/rust/maprando-web/src/randomize_helpers.rs b/rust/maprando-web/src/randomize_helpers.rs index 0ec7b32db..a263fd10a 100644 --- a/rust/maprando-web/src/randomize_helpers.rs +++ b/rust/maprando-web/src/randomize_helpers.rs @@ -10,9 +10,9 @@ use maprando::{ randomize::{DifficultyConfig, ItemPriorityGroup, Randomization, get_starting_items}, seed_repository::{Seed, SeedFile}, settings::{ - AreaAssignmentBaseOrder, AreaAssignmentPreset, DisableETankSetting, DoorLocksSize, - ETankRefill, FillerItemPriority, ItemCount, RandomizerSettings, SpeedBooster, WallJump, - get_objective_groups, + AreaAssignmentBaseOrder, AreaAssignmentPreset, CrashFixesPreset, DisableETankSetting, + DoorLocksSize, ETankRefill, FillerItemPriority, ItemCount, RandomizerSettings, + SpeedBooster, WallJump, get_objective_groups, }, spoiler_log::SpoilerLog, spoiler_map, @@ -139,6 +139,16 @@ impl SeedHeaderTemplate<'_> { .join(", ") } + fn crash_fix_preset(&self) -> &'static str { + match self.settings.quality_of_life_settings.crash_fixes.preset { + Some(CrashFixesPreset::Crash) => "Crash", + Some(CrashFixesPreset::Death) => "Death", + Some(CrashFixesPreset::Warn) => "Warn", + Some(CrashFixesPreset::Silent) => "Silent", + None => "Custom", + } + } + fn game_variations(&self) -> Vec<&str> { let mut game_variations = vec![]; let other_settings = &self.settings.other_settings; diff --git a/rust/maprando-web/templates/generate/crash_fixes.html b/rust/maprando-web/templates/generate/crash_fixes.html new file mode 100644 index 000000000..99bf2aa1e --- /dev/null +++ b/rust/maprando-web/templates/generate/crash_fixes.html @@ -0,0 +1,87 @@ + \ No newline at end of file diff --git a/rust/maprando-web/templates/generate/game_variations.html b/rust/maprando-web/templates/generate/game_variations.html index 87b09345f..47cd294de 100644 --- a/rust/maprando-web/templates/generate/game_variations.html +++ b/rust/maprando-web/templates/generate/game_variations.html @@ -28,18 +28,22 @@
- + - + - + - + - +
@@ -109,7 +113,8 @@
- + diff --git a/rust/maprando-web/templates/generate/help/quality/crash_fixes.html b/rust/maprando-web/templates/generate/help/quality/crash_fixes.html new file mode 100644 index 000000000..14cf9a730 --- /dev/null +++ b/rust/maprando-web/templates/generate/help/quality/crash_fixes.html @@ -0,0 +1,55 @@ + \ No newline at end of file diff --git a/rust/maprando-web/templates/generate/quality_of_life.html b/rust/maprando-web/templates/generate/quality_of_life.html index b8c77f3a8..25c5ab102 100644 --- a/rust/maprando-web/templates/generate/quality_of_life.html +++ b/rust/maprando-web/templates/generate/quality_of_life.html @@ -354,6 +354,33 @@

Quality-of-life options

+ +
+
+ + +
+
+ + + + + + + + + +
+
@@ -531,6 +558,7 @@

Quality-of-life options

{% include "help/quality/items_spawn.html" %} {% include "help/quality/acid_chozo.html" %} {% include "help/quality/remove_climb_lava.html" %} +{% include "help/quality/crash_fixes.html" %} {% include "help/quality/energy_station_reserves.html" %} {% include "help/quality/etank_refill.html" %} @@ -543,3 +571,4 @@

Quality-of-life options

{% include "help/quality/persist_blue_suit.html" %} {% include "initial_map_reveal.html" %} +{% include "crash_fixes.html" %} \ No newline at end of file diff --git a/rust/maprando-web/templates/generate/scripts.html b/rust/maprando-web/templates/generate/scripts.html index 4b432ea97..51348ed10 100644 --- a/rust/maprando-web/templates/generate/scripts.html +++ b/rust/maprando-web/templates/generate/scripts.html @@ -276,6 +276,13 @@ "all_items_spawn": formData.get("all_items_spawn") == "true", "acid_chozo": formData.get("acid_chozo") == "true", "remove_climb_lava": formData.get("remove_climb_lava") == "true", + "crash_fixes": { + "preset": formData.get("crash_fixes_preset"), + "spring_ball": formData.get("crash_fixes_springball"), + "yapping_maw": formData.get("crash_fixes_yappingmaw"), + "auto_reserve": formData.get("crash_fixes_autoreserve"), + "x_mode": formData.get("crash_fixes_xmode"), + }, "etank_refill": formData.get("etank_refill"), "energy_station_reserves": formData.get("energy_station_reserves") == "true", "disableable_etanks": formData.get("disableable_etanks"), @@ -518,6 +525,7 @@ applyRadioValue("allItemsSpawn", preset.all_items_spawn); applyRadioValue("acidChozo", preset.acid_chozo); applyRadioValue("removeClimbLava", preset.remove_climb_lava); + applyCrashFixes(preset.crash_fixes); applyRadioValue("etankRefill", preset.etank_refill); applyRadioValue("energyStationReserves", preset.energy_station_reserves); applyRadioValue("disableableETanks", preset.disableable_etanks); @@ -771,7 +779,6 @@ function areaAssignmentPresetChanged() { for (var presetEl of document.getElementsByClassName("area-assignment-preset-button")) { - console.log("Checking preset: " + presetEl.value); if (presetEl.checked) { let preset = presetEl.value; applyAreaAssignmentPreset(preset); @@ -788,6 +795,62 @@ document.getElementById("areaAssignmentPresetRandom").checked = false; } + function applyCrashFixesPreset(preset) { + if (preset == "Crash") { + document.getElementById("crashFixesPresetCrash").checked = true; + document.getElementById("crashFixesSpringballCrash").checked = true; + document.getElementById("crashFixesYappingmawCrash").checked = true; + document.getElementById("crashFixesAutoreserveCrash").checked = true; + document.getElementById("crashFixesXmodeCrash").checked = true; + } else if (preset == "Death") { + document.getElementById("crashFixesPresetDeath").checked = true; + document.getElementById("crashFixesSpringballDeath").checked = true; + document.getElementById("crashFixesYappingmawDeath").checked = true; + document.getElementById("crashFixesAutoreserveDeath").checked = true; + document.getElementById("crashFixesXmodeDeath").checked = true; + } else if (preset == "Warn") { + document.getElementById("crashFixesPresetWarn").checked = true; + document.getElementById("crashFixesSpringballWarn").checked = true; + document.getElementById("crashFixesYappingmawWarn").checked = true; + document.getElementById("crashFixesAutoreserveWarn").checked = true; + document.getElementById("crashFixesXmodeWarn").checked = true; + } else if (preset == "Silent") { + document.getElementById("crashFixesPresetSilent").checked = true; + document.getElementById("crashFixesSpringballSilent").checked = true; + document.getElementById("crashFixesYappingmawSilent").checked = true; + document.getElementById("crashFixesAutoreserveSilent").checked = true; + document.getElementById("crashFixesXmodeSilent").checked = true; + } + + } + +function applyCrashFixes(preset) { + applyRadioValue("crashFixesSpringball", preset.spring_ball); + applyRadioValue("crashFixesYappingmaw", preset.yapping_maw); + applyRadioValue("crashFixesAutoreserve", preset.auto_reserve); + applyRadioValue("crashFixesXmode", preset.x_mode); + applyCrashFixesPreset(preset.preset); +} + +function crashFixesPresetChanged() { + for (var presetEl of document.getElementsByClassName("crash-fixes-preset-button")) { + if (presetEl.checked) { + let preset = presetEl.value; + applyCrashFixesPreset(preset); + break; + } + } + qualityOfLifeSettingChanged(); +} + +function crashFixesChanged() { + document.getElementById("crashFixesPresetDeath").checked = false; + document.getElementById("crashFixesPresetWarn").checked = false; + document.getElementById("crashFixesPresetSilent").checked = false; + document.getElementById("crashFixesPresetCrash").checked = false; + qualityOfLifeSettingChanged(); +} + function processInitialMapRevealPreset() { let tileTypes = [ "Maps", diff --git a/rust/maprando-web/templates/seed/crash_fix_details.html b/rust/maprando-web/templates/seed/crash_fix_details.html new file mode 100644 index 000000000..0745d2511 --- /dev/null +++ b/rust/maprando-web/templates/seed/crash_fix_details.html @@ -0,0 +1,18 @@ +{% let cf = settings.quality_of_life_settings.crash_fixes %} +
+
Unequip Spring Ball bounce:
+
{{ cf.spring_ball.to_string() }}
+
+
+
Yapping Maw shinespark:
+
{{ cf.yapping_maw.to_string() }}
+
+
+
Frame-perfect pause auto-reserve:
+
{{ cf.auto_reserve.to_string() }}
+
+
+
X-Mode tile collision:
+
{{ cf.x_mode.to_string() }}
+
+ diff --git a/rust/maprando-web/templates/seed/doors_details.html b/rust/maprando-web/templates/seed/door_details.html similarity index 73% rename from rust/maprando-web/templates/seed/doors_details.html rename to rust/maprando-web/templates/seed/door_details.html index 5201f1fd8..e1f03c325 100644 --- a/rust/maprando-web/templates/seed/doors_details.html +++ b/rust/maprando-web/templates/seed/door_details.html @@ -1,30 +1,30 @@
-
Missile Doors:
+
Missile doors:
{{+ red_doors_count }}
-
Super Doors:
+
Super doors:
{{+ green_doors_count }}
-
Power Bomb Doors:
+
Power Bomb doors:
{{+ yellow_doors_count }}
-
Charge Beam Doors:
+
Charge Beam doors:
{{+ charge_doors_count }}
-
Ice Beam Doors:
+
Ice Beam doors:
{{+ ice_doors_count }}
-
Wave Beam Doors:
+
Wave Beam doors:
{{+ wave_doors_count }}
diff --git a/rust/maprando-web/templates/seed/quality_of_life_details.html b/rust/maprando-web/templates/seed/quality_of_life_details.html index b789aa788..e45474410 100644 --- a/rust/maprando-web/templates/seed/quality_of_life_details.html +++ b/rust/maprando-web/templates/seed/quality_of_life_details.html @@ -80,6 +80,10 @@
Lava removed from Climb:
{% if settings.quality_of_life_settings.remove_climb_lava %}Yes{% else %}No{% endif %}
+
+
Crash fixes:
+
{{+ self.crash_fix_preset() }}
+

E-Tank energy refill:
diff --git a/rust/maprando-web/templates/seed/seed_header.html b/rust/maprando-web/templates/seed/seed_header.html index 322e20aa1..b269d99de 100644 --- a/rust/maprando-web/templates/seed/seed_header.html +++ b/rust/maprando-web/templates/seed/seed_header.html @@ -130,10 +130,10 @@

- Doors details + Door details
- {% include "doors_details.html" %} + {% include "door_details.html" %}
@@ -146,6 +146,15 @@

+
+
+ Crash fix details +
+
+ {% include "crash_fix_details.html" %} +
+
+
Objective details diff --git a/rust/maprando/src/patch.rs b/rust/maprando/src/patch.rs index 183765b15..e13703bd3 100644 --- a/rust/maprando/src/patch.rs +++ b/rust/maprando/src/patch.rs @@ -18,9 +18,9 @@ use crate::{ patch::map_tiles::diagonal_flip_tile, randomize::{LockedDoor, Randomization, get_starting_items}, settings::{ - AreaAssignmentPreset, DisableETankSetting, ETankRefill, Fanfares, ItemCount, - MotherBrainFight, Objective, ObjectiveScreen, RandomizerSettings, SaveAnimals, - SpeedBooster, StartLocationMode, WallJump, + AreaAssignmentPreset, CrashFixes, CrashFixesPreset, DisableETankSetting, ETankRefill, + Fanfares, FixMode, ItemCount, MotherBrainFight, Objective, ObjectiveScreen, + RandomizerSettings, SaveAnimals, SpeedBooster, StartLocationMode, WallJump, }, }; use anyhow::{Context, Result, bail, ensure}; @@ -547,6 +547,30 @@ impl Patcher<'_> { self.rom.write_u16(snes2pc(0xdfff07), 40)?; } + if self.settings.quality_of_life_settings.crash_fixes.preset + != Some(CrashFixesPreset::Crash) + { + patches.push("crash_handle_base"); + } + + let fixes = &self.settings.quality_of_life_settings.crash_fixes; + + if fixes.spring_ball != FixMode::Crash { + patches.push("crash_handle_springball"); + } + + if fixes.yapping_maw != FixMode::Crash { + patches.push("crash_handle_yapping"); + } + + if fixes.auto_reserve != FixMode::Crash { + patches.push("crash_handle_autoreserves"); + } + + if fixes.x_mode != FixMode::Crash { + patches.push("crash_handle_xmode"); + } + match self.settings.other_settings.wall_jump { WallJump::Vanilla => {} WallJump::Collectible => { @@ -975,6 +999,19 @@ impl Patcher<'_> { Ok(()) } + pub fn write_crash_handler(&mut self, crash_fixes: &CrashFixes) -> Result<()> { + let crash_handler = 0x80D330; + + let word = crash_fixes.to_word(); + if word == 0x3333 { + return Ok(()); // no need to patch the rom if the crash handler isn't applied at all + } + let bytes = word.to_le_bytes(); + self.rom.write_n(snes2pc(crash_handler), &bytes)?; + + Ok(()) + } + fn clamp_samus_position( &mut self, extra_door_asm: &mut HashMap>, @@ -3543,6 +3580,7 @@ pub fn make_rom( patcher.write_room_name_font()?; patcher.write_room_name_data()?; patcher.remove_non_blue_doors()?; + patcher.write_crash_handler(&patcher.settings.quality_of_life_settings.crash_fixes)?; override_music(patcher.rom)?; if randomizer_settings.map_layout != "Vanilla" || randomizer_settings.other_settings.area_assignment.preset diff --git a/rust/maprando/src/settings.rs b/rust/maprando/src/settings.rs index 855a86ae5..5d2d7bc9c 100644 --- a/rust/maprando/src/settings.rs +++ b/rust/maprando/src/settings.rs @@ -167,6 +167,7 @@ pub struct QualityOfLifeSettings { pub all_items_spawn: bool, pub acid_chozo: bool, pub remove_climb_lava: bool, + pub crash_fixes: CrashFixes, // Energy and reserves pub etank_refill: ETankRefill, pub energy_station_reserves: bool, @@ -519,6 +520,64 @@ impl AreaAssignment { } } +#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)] +pub enum CrashFixesPreset { + Death, + Warn, + Silent, + Crash, +} + +#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +#[repr(u16)] +pub enum FixMode { + Death = 0, + Warn = 1, + Silent = 2, + Crash = 3, +} + +impl std::fmt::Display for FixMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:?}") + } +} + +#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CrashFixes { + pub preset: Option, + pub spring_ball: FixMode, + pub yapping_maw: FixMode, + pub auto_reserve: FixMode, + pub x_mode: FixMode, +} + +impl CrashFixes { + pub fn from_preset(preset: CrashFixesPreset) -> Self { + let mode = match preset { + CrashFixesPreset::Death => FixMode::Death, + CrashFixesPreset::Warn => FixMode::Warn, + CrashFixesPreset::Silent => FixMode::Silent, + CrashFixesPreset::Crash => FixMode::Crash, + }; + + CrashFixes { + preset: Some(preset), + spring_ball: mode, + yapping_maw: mode, + auto_reserve: mode, + x_mode: mode, + } + } + + pub fn to_word(&self) -> u16 { + ((self.x_mode as u16) << 12) + | ((self.yapping_maw as u16) << 8) + | ((self.auto_reserve as u16) << 4) + | (self.spring_ball as u16) + } +} + #[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)] pub enum WallJump { Vanilla, @@ -867,6 +926,28 @@ fn upgrade_qol_settings(settings: &mut serde_json::Value) -> Result<()> { if !qol_settings.contains_key("persist_blue_suit") { qol_settings.insert("persist_blue_suit".to_string(), false.into()); } + + match qol_settings.get_mut("crash_fixes") { + None => { + qol_settings.insert( + "crash_fixes".to_string(), + serde_json::to_value(CrashFixes::from_preset(CrashFixesPreset::Death))?, + ); + } + Some(crash_fixes) => { + if let Some(preset_str) = crash_fixes["preset"].as_str() { + let preset = match preset_str { + "Crash" => CrashFixesPreset::Crash, + "Death" => CrashFixesPreset::Death, + "Warn" => CrashFixesPreset::Warn, + "Silent" => CrashFixesPreset::Silent, + _ => bail!("Unrecognized preset: {}", preset_str), + }; + *crash_fixes = serde_json::to_value(CrashFixes::from_preset(preset))?; + } + } + } + upgrade_initial_map_reveal_settings(settings)?; Ok(()) } @@ -975,20 +1056,21 @@ fn upgrade_other_settings(settings: &mut serde_json::Value) -> Result<()> { .context("missing other_settings")? .as_object_mut() .context("expected other_settings to be object")?; - - let area_assignment = other_settings - .get_mut("area_assignment") - .context("missing area_assignment")?; - - if area_assignment.is_string() { - let preset_str = area_assignment.as_str().unwrap(); - let preset = match preset_str { - "Standard" => AreaAssignmentPreset::Standard, - "Ordered" => AreaAssignmentPreset::Size, - "Random" => AreaAssignmentPreset::Random, - _ => bail!("Unrecognized area assignment preset: {}", preset_str), - }; - *area_assignment = serde_json::to_value(AreaAssignment::from_preset(preset))?; + { + let area_assignment = other_settings + .get_mut("area_assignment") + .context("missing area_assignment")?; + + if area_assignment.is_string() { + let preset_str = area_assignment.as_str().unwrap(); + let preset = match preset_str { + "Standard" => AreaAssignmentPreset::Standard, + "Ordered" => AreaAssignmentPreset::Size, + "Random" => AreaAssignmentPreset::Random, + _ => bail!("Unrecognized area assignment preset: {}", preset_str), + }; + *area_assignment = serde_json::to_value(AreaAssignment::from_preset(preset))?; + } } if other_settings.get("disable_spikesuit").is_none() diff --git a/rust/maprando/tests/logic_scenarios.rs b/rust/maprando/tests/logic_scenarios.rs index f73b6dac8..f661cf418 100644 --- a/rust/maprando/tests/logic_scenarios.rs +++ b/rust/maprando/tests/logic_scenarios.rs @@ -245,6 +245,9 @@ fn get_settings(scenario: &Scenario) -> Result { all_items_spawn: false, acid_chozo: false, remove_climb_lava: false, + crash_fixes: maprando::settings::CrashFixes::from_preset( + maprando::settings::CrashFixesPreset::Death, + ), etank_refill: maprando::settings::ETankRefill::Vanilla, energy_station_reserves: false, disableable_etanks: match settings