From 990e7343c7fe0c1c54787cd274c869b558b252e7 Mon Sep 17 00:00:00 2001 From: jeboo Date: Wed, 12 Feb 2025 22:51:31 -0500 Subject: [PATCH 1/3] add objectives screen to pause menu with real-time updating --- patches/rom_map/Bank 82.txt | 19 +- patches/rom_map/Bank 85.txt | 3 +- patches/rom_map/Bank B6.txt | 1 + patches/src/pause_menu_objectives.asm | 882 ++++++++++++++++++ .../tables/pause_menu_objectives_chars.tbl | 42 + 5 files changed, 937 insertions(+), 10 deletions(-) create mode 100644 patches/rom_map/Bank B6.txt create mode 100644 patches/src/pause_menu_objectives.asm create mode 100644 patches/src/tables/pause_menu_objectives_chars.tbl diff --git a/patches/rom_map/Bank 82.txt b/patches/rom_map/Bank 82.txt index 98808b0ce6..30a52ea1e6 100644 --- a/patches/rom_map/Bank 82.txt +++ b/patches/rom_map/Bank 82.txt @@ -1,16 +1,17 @@ F70F - FA00: map_area.asm -FA00 - FA80: alternate_door_colors -FA80 - FA90: fix_water_fx_bug -FA90 - FBB0: seed_hash_display -FBB0 - FBF0: fix_kraid_hud +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 FC30 - FD00: [FREE] -FD00 - FD80: item_dots_disappear -FD80 - FE00: fast_doors -FE00 - FE80: fast_reload -FE80 - FF00: zebes_asleep_music +FD00 - FD80: item_dots_disappear.asm +FD80 - FE00: fast_doors.asm +FE00 - FE80: fast_reload.asm +FE80 - FF00: zebes_asleep_music.asm FF00 - FF10: reserve_hud.asm FF10 - FF30: escape.asm FF30 - FF80: reserve_hud.asm -FF80 - FFFE: [FREE] +FF80 - FFFC: pause_menu_objectives.asm +FFFC - FFFE: fast_pause_menu FFFE - end: custom etank color \ No newline at end of file diff --git a/patches/rom_map/Bank 85.txt b/patches/rom_map/Bank 85.txt index 28060aca96..c63c588912 100644 --- a/patches/rom_map/Bank 85.txt +++ b/patches/rom_map/Bank 85.txt @@ -3,4 +3,5 @@ $96B0 - $9800: walljump_item.asm $9800 - $9880: spin_lock.asm $9880 - $9980: fast_reload.asm $9980 - $9B00: stats.asm -$9B00 - $9B20: fix_hyper_slowlock.asm \ No newline at end of file +$9B00 - $9B20: fix_hyper_slowlock.asm +$9B20 - $A050: pause_menu_objectives.asm \ No newline at end of file diff --git a/patches/rom_map/Bank B6.txt b/patches/rom_map/Bank B6.txt new file mode 100644 index 0000000000..fa4f83a65d --- /dev/null +++ b/patches/rom_map/Bank B6.txt @@ -0,0 +1 @@ +F200-F660: pause_menu_objectives.asm diff --git a/patches/src/pause_menu_objectives.asm b/patches/src/pause_menu_objectives.asm new file mode 100644 index 0000000000..0e030b32a3 --- /dev/null +++ b/patches/src/pause_menu_objectives.asm @@ -0,0 +1,882 @@ +; Objectives pause menu screen based on VARIA Randomizer's patch by theonlydude/flohgh +; +; Simplified the objectives implementation and ported much of the code to reside in bank 85. +; +; Randomizer defines the objectives @ $B6F200 with the following format: +; +; - Each line terminates with a word value of $8000 +; - Max length of line is 30 displayable characters +; - Valid characters defined in tables/pause_menu_objectives_chars.tbl. They must be converted to +; the corresponding word value in the .tbl before writing to ROM +; - Line count is 18 and must all be defined (even if blank) +; +; The first occurrence of !check_char (defined below) will be converted to a check mark +; once the objective is completed. +; +; Stag Shot + +lorom +arch 65816 + +math pri on + +!bank_82_free_space_start = $82FF80 +!bank_82_free_space_end = $82FFFC + +!bank_85_free_space_start = $859B20 +!bank_85_free_space_end = $85A050 + +!bank_B6_free_space_start = $B6F200 +!bank_B6_free_space_end = $B6F660 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; HIJACKS + +;;; new pause index func list +org $82910A + JSR (new_pause_index_func_list,x) + +;;; simplify unpausing +org $82932B + JSR $A56D + +;;; hook map switching to update area-specific tilemap +org $82941E + JSL set_area_tilemap : nop : nop : nop : nop + +;;; update BG2 buttons +org $82A62D + JSL set_bg2_equipment_screen : nop : nop + +org $82A79B + JSL set_bg2_map_screen : nop : nop + +;;; keep 'MAP' left button visible on map screen by keeping palette 2 instead of palette 5 (grey one) +org $82A820 + ORA #$1000 + +org $82A83E + ORA #$1000 + +;;; update glowing sprite around L/R pointer +org $82C1E6 + dw glowing_LR_animation + +;;; new function to check for L/R button pressed +org $82A505 + JML check_l_r_pressed : nop : nop + +;;; Replace pause screen button label palettes functions +org $82A61D + JSR (new_pause_palettes_func_list,x) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; Pause stuff + +!n_lines = #$0012 + +!ObjectiveAddrs = $8FEBC0 +!ObjectiveBitmasks = $8FEBC8 + +;;; character conversion table +table "tables/pause_menu_objectives_chars.tbl",RTL + +;;; pause state +!pause_index = $0727 + +;;; fast_pause_menu +!fast_pause_menu = $82fffc ; must match address in patch.rs + +;;; pause index values +!pause_index_map_screen = #$0000 +!pause_index_equipment_screen = #$0001 +!pause_index_map2equip_fading_out = #$0002 +!pause_index_map2equip_load_equip = #$0003 +!pause_index_map2equip_fading_in = #$0004 +!pause_index_equip2map_fading_out = #$0005 +!pause_index_equip2map_load_map = #$0006 +!pause_index_equip2map_fading_in = #$0007 +;;; new screen (skip 3 indices used by map patch): +!pause_index_objective_screen = #$000B +!pause_index_map2obj_fading_out = #$000C +!pause_index_map2obj_load_obj = #$000D +!pause_index_map2obj_fading_in = #$000E +!pause_index_obj2map_fading_out = #$000F +!pause_index_obj2map_load_map = #$0010 +!pause_index_obj2map_fading_in = #$0011 + +;;; pause screen button label mode +!pause_screen_button_mode = $0753 +!pause_screen_button_map = #$0000 ; Map screen (SAMUS on the right, OBJ on the left) +!pause_screen_button_nothing = #$0001 ; Unpausing (nothing) +!pause_screen_button_equip = #$0002 ; Equipment screen (MAP on the left) +;;; new button mode: +!pause_screen_button_obj = #$0003 ; Objective screen (MAP on the right) + +;;; Pause screen mode +!pause_screen_mode = $0763 + +;;; pause screen mode values +!pause_screen_mode_map = #$0000 +!pause_screen_mode_equip = #$0001 +;;; new mode: +!pause_screen_mode_obj = #$0002 + +;;; button stuff +!held_buttons = $05E1 +!newly_pressed_buttons = $8F +!l_button = #$0020 +!r_button = #$0010 +!light_up_l_button = #$0001 +!light_up_r_button = #$0002 + +;; dynamic objective text: BG1 tilemap in RAM +!BG1_tilemap = $7E3800 +;; rows [5, 23] of screen +!BG1_tilemap_size = $4c0 + +!line_size #= 32*2 +!check_char = #$28DD ; "-" = char to replace with check (1st occurrence) + +;; RAM +;; current first objective displayed +!obj_index = $073d + +;;; simple helper to instant DMA gfx from a static long source address to VRAM +;;; usable during blank screen only +macro gfxDMA(src, dstVRAM, size) + PHP + SEP #$30 + LDA.b #(&$ff) : STA $2116 ;| VRAM Address Registers (Low) + LDA.b #(>>8) : STA $2117 ;| VRAM Address Registers (High) + LDA.b #$80 : STA $2115 ;} Video Port Control Register - Set VRAM transfer mode to word-access, increment by 1. + ; 0x80 == 0b10000000 => i---ffrr => i=1 (increment when $2119 is accessed), + ; ff=0 (full graphic ??), rr=0 (increment by 2 bytes) + JSL $8091A9 ; Set up a DMA transfer + db $01,$01 ; hard-coded channel 1, options = $01 + db $18 ; DMA target = VRAM + dl + dw + LDA.b #$02 : STA $420B ; start transfer + PLP +endmacro + +;;; helper to DMA load from static long address (*not RAM*) to static long address (RAM) +macro loadRamDMA(src, dstRAMl, size) + PHP + SEP #$30 + ;; write RAM address to proper registers + LDA.b #(&$ff) : STA $2181 + LDA.b #((&$ff00)>>8) : STA $2182 + LDA.b #(>>16) : STA $2183 + ;; set up DMA transfer + JSL $8091A9 + db $01,$00 ; hard-coded channel 1, options = $00 + db $80 ; DMA target = WRAM + dl + dw + LDA #$02 : STA $420B ; start transfer + PLP +endmacro + +;;; simple helper to queue DMA gfx from a static long source address to VRAM +;;; usable at any time, uses X +macro queueGfxDMA(src, dstVRAM, size) + LDX $0330 + LDA.w # : STA.b $D0,x + INX : INX + LDA.W #(&$ffff) : STA.b $D0,x + INX : INX + SEP #$20 + LDA.b #(>>16) : STA.b $D0,x + REP #$20 + INX + LDA.w # : STA.b $D0,x + INX : INX + STX $0330 +endmacro + +;;; call bank 82 funcs from bank 85 +macro callBank82Func(func) + PHX + LDX.w # + DEX + JSL hook_85_to_82 + PLX +endmacro + +org !bank_82_free_space_start +;;; glowing sprites around L/R +glowing_LR_animation: + dw $002A, $002A, $002A, $002A + +new_pause_palettes_func_list: + dw $A796, $A6DF, $A628, update_palette_objective_screen + +update_palette_objective_screen: + JSL update_palette_objective_screen_85 + RTS + +hook_85_to_82: ; X = local func + PEA ret_long-1 + PHX ; stack hack + RTS ; to call X +ret_long: + RTL + +new_pause_index_func_list: ; expanded pause index func list +dw $9120, $9142, $9156, $91AB, $9231, $9186, $91D7, $9200 ; stock 0-7 +dw $9156, $F816, $9200 ; map_area.asm 8-0A +dw func_pause_index_objective_screen, func_pause_index_map2obj_fading_out, func_pause_index_map2obj_load_obj ; 0B-0D +dw func_pause_index_map2obj_fading_in, func_pause_index_obj2map_fading_out, $91D7, $9200 ; 0E-11 + +; bank 82 to 85 calls for indexes 0B-0F +func_pause_index_objective_screen: + JSL func_objective_screen + RTS + +func_pause_index_map2obj_fading_out: + JSL func_map2obj_fading_out + RTS + +func_pause_index_map2obj_load_obj: + JSL func_map2obj_load_obj + RTS + +func_pause_index_map2obj_fading_in: + JSL func_map2obj_fading_in + RTS + +func_pause_index_obj2map_fading_out: + JSL func_obj2map_fading_out + RTS + +print "82 end: ", PC +warnpc !bank_82_free_space_end + +;;; continue in bank 85 for obj screen management code +org !bank_85_free_space_start + +;;; load objective screen title from ROM to RAM +load_obj_tilemap: + %loadRamDMA(obj_bg1_tilemap, !BG1_tilemap+20, 24) + RTL + +;;; update RAM tilemap with objectives text, line by line +!tmp_tile_offset = $12 + +update_objs: + PHB + PHK + PLB + LDA.w #!line_size+2 + STA !tmp_tile_offset + STZ !obj_index + LDY #$ffff + +.draw_obj_text + INY + CPY !n_lines + BEQ .check_objs + LDX !tmp_tile_offset + +.draw_obj_loop + PHX + LDX !obj_index + LDA obj_txt_ptrs, x + INX : INX + STX !obj_index + PLX + CMP #$8000 + BEQ .pad_rest_of_line + STA !BG1_tilemap, x + INX : INX + BRA .draw_obj_loop + +.pad_rest_of_line + JSR pad_0 + BRA .draw_obj_text + +.check_objs +;;; check objectives and add check marks + LDY.w #!line_size*2 ; start of 1st line + LDX #$0000 ; objective counter + +.obj_check_lp + PHX + CPX #$0004 ; check for extra iteration, only happens when animals saved + BEQ .skip_check ; if so, check off animals saved! + JSR check_objective + BEQ .nocheck +.skip_check + TYX + +.find_check + LDA !BG1_tilemap, x + CMP !check_char ; tile to switch? + BEQ .checkmark + INX : INX + CPX #!BG1_tilemap_size ; end of grid? + BCS .leave_eog + BRA .find_check + +.checkmark + LDA #$250B ; check mark (green) + STA !BG1_tilemap, x + +.nocheck + PLX + INX + CPX #$0005 ; done with primary obj's and animals? + BEQ .exit + CPX #$0004 ; finished with primary objectives? + BNE .next_obj + JSR check_animals ; if so, check if animals req'd/done + BCC .exit + +.next_obj + TYA + CLC + ADC.w #!line_size*2 + TAY + BRA .obj_check_lp + +.leave_eog + PLX +.exit + PLB + RTL + +check_objective: ; X = index + PHX + TXA + ASL + TAX + LDA.w #$007E + STA.b $02 + LDA.l !ObjectiveAddrs, X + STA.b $00 + LDA.l !ObjectiveBitmasks, X + STA.b $04 + LDA.b [$00] + PLX + BIT.b $04 + RTS + +;;; pad with 0s until end of line +pad_0: + TXA + AND #$003F + BEQ .end + LDA #$280E : STA.l !BG1_tilemap,x + INX : INX + BRA pad_0 + +.end: + INX : INX + STX !tmp_tile_offset + RTS + +check_animals: + LDA $A1F000 ; save_animals_required_opt (defined in escape.asm) + BEQ .no_animals + LDA $7ED821 + AND $0080 ; animals saved? + BEQ .no_animals + SEC + BRA .leave +.no_animals + CLC +.leave + RTS + +;;; direct DMA of BG1 tilemap to VRAM +blit_objs: + %gfxDMA(!BG1_tilemap, $30a0, !BG1_tilemap_size) + RTL + +;;; DMA tilemap each frame +queue_obj_tilemap: + %queueGfxDMA(!BG1_tilemap, $30a0, !BG1_tilemap_size) + RTL + +;;; check if up/down press, and if applicable, scroll (play sfx for scroll ok/ko) +obj_scroll: + RTL + +;;; check for L or R input and update pause_index && pause_screen_button_mode +check_l_r_pressed: + PHP + REP #$30 + LDA !held_buttons + BIT !l_button + BNE .press_L + BIT !r_button + BNE .press_R + BRA .end + +.press_R: + LDA !pause_screen_button_mode + CMP !pause_screen_button_equip ; if already equipment screen => end + BEQ .end + ;; common actions + LDA $C10A ; $82:C10A db 05,00 + STA $0729 ; Frames to flash L/R/start button on pause screen + LDA !light_up_r_button + STA $0751 ; $0751: Which button lights up for $0729 frames when changing screens from pause screen + + LDA !pause_screen_button_mode + CMP !pause_screen_button_obj + BEQ .move_to_map_from_obj + +.move_to_equip_from_map: + LDA !pause_index_map2equip_fading_out + STA !pause_index + LDA !pause_screen_button_equip + STA !pause_screen_button_mode + BRA .play_sound + +.move_to_map_from_obj: + LDA !pause_index_obj2map_fading_out + STA !pause_index + LDA !pause_screen_button_map + STA !pause_screen_button_mode ; pause_screen_button_mode set to pause_screen_button_equip + BRA .play_sound + +.press_L: + LDA !pause_screen_button_mode ; pause_screen_button_mode, 00 == map screen + CMP !pause_screen_button_obj + BEQ .end ; if already on objective screen => end + ;; common actions + LDA $C10A ; $82:C10A db 05,00 + STA $0729 ; frames to flash L/R/start button on pause screen + LDA !light_up_l_button + STA $0751 + + LDA !pause_screen_button_mode + CMP !pause_screen_button_map + BEQ .move_to_obj_from_map ; if on map screen and L pressed => objective screen + +.move_to_map_from_equip: + LDA !pause_index_equip2map_fading_out + STA !pause_index + STZ !pause_screen_button_mode ; pause_screen_button_mode set to pause_screen_button_map + BRA .play_sound + +.move_to_obj_from_map: + LDA !pause_index_map2obj_fading_out + STA !pause_index + LDA !pause_screen_button_obj + STA !pause_screen_button_mode + +.play_sound: + %callBank82Func($A615) ; $A615: Set pause screen buttons label palettes to show/hide them + LDA #$0038 ;\ + JSL $809049 ;} Queue sound 38h, sound library 1, max queued sounds allowed = 6 (menu option selected) + +.end + PLP + JML $82A59A + +;;; buttons addresses in BG2 +!left_button_top = $7E364A +!left_button_bottom = $7E368A +!right_button_top = $7E366C +!right_button_bottom = $7E36AC + +;;; replace 'MAP' with 'OBJ' in left BG2, put back 'SAMUS' in right BG2 +set_bg2_map_screen: + PHB + PHK + PLB + LDY #$000A + LDX #$0000 +.left_loop_top: + LDA obj_top,x + STA !left_button_top,x + INX : INX + DEY : DEY + BNE .left_loop_top + + LDY #$000A + LDX #$0000 +.left_loop_bottom: + LDA obj_bottom,x + STA !left_button_bottom,x + INX : INX + DEY : DEY + BNE .left_loop_bottom + + LDY #$000A + LDX #$0000 +.right_loop_top: + LDA samus_top,x + STA !right_button_top,x + INX : INX + DEY : DEY + BNE .right_loop_top + + LDY #$000A + LDX #$0000 +.right_loop_bottom: + LDA samus_bottom,x + STA !right_button_bottom,x + INX : INX + DEY : DEY + BNE .right_loop_bottom + LDY #$000A ; vanilla code + LDX #$0000 + PLB + RTL + +;;; put back 'MAP' in BG2 left +set_bg2_equipment_screen: + PHB + PHK + PLB + LDY #$000A + LDX #$0000 +.loop_top: + LDA map_top,x + STA !left_button_top,x + INX : INX + DEY : DEY + BNE .loop_top + + LDY #$000A + LDX #$0000 +.loop_bottom: + LDA map_bottom,x + STA !left_button_bottom,x + INX : INX + DEY : DEY + BNE .loop_bottom + LDY #$000A ; vanilla code + LDX #$0000 + PLB + RTL + +set_area_tilemap: + PHA + %queueGfxDMA($E29A00, $0D00, $0020) ; Q + %queueGfxDMA($E29B60, $0DB0, $0040) ; . / + %queueGfxDMA($E29BE0, $0DF0, $0020) ; ! + %queueGfxDMA($E2A160, $10B0, $0080) ; check mark, top of OBJ button + %queueGfxDMA($E2A380, $11C0, $0060) ; bottom of OBJ button + PLA + CMP #$0007 ; vanilla code + BMI .skip_zero2 + LDA #$0000 +.skip_zero2 + RTL + +;;; obj: left: grey (obj), right: MAP +;;; map: left: OBJ, right: samus +;;; equip: left: map, right: grey (samus) + +;;; obj/map/samus buttons tiles +obj_top: + dw $28E4, $290C, $290D, $290E, $28E8 +obj_bottom: + dw $28F4, $291C, $291D, $291E, $28F8 + +map_top: + dw $28E4, $28E5, $28E6, $28E7, $28E8 +map_bottom: + dw $28F4, $28F5, $28F6, $28F7, $28F8 + +samus_top: + dw $28E9, $28EA, $28EB, $28EC, $28ED +samus_bottom: + dw $28F9, $28FA, $28FB, $28FC, $28FD + +update_palette_objective_screen_85: + PHP + REP #$30 + PHB + PHK + PLB + + LDY #$000A + LDX #$0000 +.loop_top + LDA map_top,x + STA !right_button_top,x + INX : INX + DEY : DEY + BNE .loop_top + + LDY #$000A + LDX #$0000 +.loop_bottom + LDA map_bottom,x + STA !right_button_bottom,x + INX : INX + DEY : DEY + BNE .loop_bottom + + LDY #$000A + LDX #$0000 +.loop_top2 + LDA !right_button_top,x + AND #$E3FF + ORA #$1000 + STA !right_button_top,x ; Set tilemap palette indices at $7E:364A..53 to 5 (top of MAP) + INX : INX + DEY : DEY + BNE .loop_top2 + + LDY #$000A + LDX #$0000 +.loop_bottom2 + LDA !right_button_bottom,x + AND #$E3FF + ORA #$1000 + STA !right_button_bottom,x ; Set tilemap palette indices at $7E:368A..93 to 5 (bottom of MAP) + INX : INX + DEY : DEY + BNE .loop_bottom2 + + LDY #$000A + LDX #$0000 +.loop_top3 + LDA !left_button_top,x + AND #$E3FF + ORA #$1400 + STA !left_button_top,x ; Set tilemap palette indices at $7E:364A..53 to 5 (grey) + INX : INX + DEY : DEY + BNE .loop_top3 + + LDY #$000A + LDX #$0000 +.loop_bottom3 + LDA !left_button_bottom,x + AND #$E3FF + ORA #$1400 + STA !left_button_bottom,x ; Set tilemap palette indices at $7E:368A..93 to 5 (grey) + INX : INX + DEY : DEY + BNE .loop_bottom3 + PLB + PLP + RTL + +func_objective_screen: + %callBank82Func($A505) ; Checks for L or R input during pause screens + %callBank82Func($A5B7) ; Checks for start input during pause screen + ; disabled for now since content is static + ;JSL obj_scroll + ;JSL update_objs + ;JSL queue_obj_tilemap + LDA !pause_screen_mode_obj + STA !pause_screen_mode ; Pause screen mode = objective screen + RTL + +func_map2obj_fading_out: + %callBank82Func($A56D) ; Updates the flashing buttons when you change pause screens + LDA !fast_pause_menu + BNE .fast + JSL $808924 ; Handle fading out + BRA .next +.fast + JSL fast_fadeout + +.next + SEP #$20 + LDA $51 ;\ + CMP #$80 ;} If not finished fading out: return + BNE .end ;/ + JSL $80834B ; Enable NMI + REP #$20 + STZ $0723 ; Screen fade delay = 0 + STZ $0725 ; Screen fade counter = 0 + INC !pause_index ; Pause index = 6 (equipment screen to map screen - load map screen) + + ;; save RAM 3800-3fff to 5000-57ff + LDA #$7ff + LDX #$3800 + LDY #$5000 + MVN $7E, $7E + +.end: + RTL + +func_map2obj_load_obj: + REP #$30 + ;; backup map's scroll + LDA $B1 + STA $BD ; BG4 X scroll = [BG1 X scroll] + LDA $B3 + STA $BF ; BG4 Y scroll = [BG1 Y scroll] + ;; no scroll + STZ $B1 ; BG1 X scroll = 0 + STZ $B3 ; BG1 Y scroll = 0 + + STZ !obj_index + JSL load_obj_tilemap + JSL update_objs + JSL blit_objs + LDA !pause_screen_mode_obj + STA !pause_screen_mode ; Pause screen mode = objective screen + %callBank82Func($A615) ; Set pause screen button label palettes + STZ $073F + LDA $C10C + STA $072B ; $072B = Fh + LDA #$0001 + STA $0723 ; Screen fade delay = 1 + STA $0725 ; Screen fade counter = 1 + INC !pause_index ; Pause index = B (map screen to objective screen - fading in) + RTL + +func_map2obj_fading_in: + LDA !pause_screen_mode_obj + STA !pause_screen_mode ; Pause screen mode = objective screen + LDA !fast_pause_menu + BNE .fast + JSL $80894D ; Handle fading in + BRA .next +.fast + JSL fast_fadein + +.next + SEP #$20 + LDA $51 ;\ + CMP #$0F ; If not finished fading in: return + BNE .end ;/ + REP #$20 + STZ $0723 ; Screen fade delay = 0 + STZ $0725 ; Screen fade counter = 0 + LDA !pause_screen_button_obj + STA !pause_screen_button_mode + LDA !pause_index_objective_screen ; index = objective + STA !pause_index +.end: + RTL + +func_obj2map_fading_out: + ;; fade out to map + %callBank82Func($A56D) ; Updates the flashing buttons when you change pause screens + LDA !fast_pause_menu + BNE .fast + JSL $808924 ; Handle fading out + BRA .next +.fast + JSL fast_fadeout + +.next + SEP #$20 + LDA $51 ;\ + CMP #$80 ; If not finished fading out: return + BNE .end ;/ + JSL $80834B ; Enable NMI + REP #$20 + STZ $0723 ; Screen fade delay = 0 + STZ $0725 ; Screen fade counter = 0 + INC !pause_index ; Pause index = D (obj screen to map screen - load map screen) + + ;; restore RAM 3800-3fff from 5000-57ff (needed for equipment screen) + LDA #$7FF + LDX #$5000 + LDY #$3800 + MVN $7E, $7E + +.end: + RTL + +; Variation of $808924 that goes twice as fast: +fast_fadeout: + PHP + SEP #$30 + LDA $51 + AND #$0F ; If (brightness) = 0: return + BEQ .done + DEC A + BEQ .force_blank + DEC A + BNE .store +.force_blank: + LDA #$80 +.store: + STA $51 +.done: + PLP + RTL + +; Variation of $80894D that goes twice as fast: +fast_fadein: + PHP + SEP #$30 + + LDA $51 + INC A + AND #$0F ; If brightness is not max: + BEQ .done + STA $51 ; Increment brightness (disable forced blank) + + INC A + AND #$0F ; If brightness is not max: + BEQ .done + STA $51 ; Increment brightness (disable forced blank) +.done: + PLP + RTL + +obj_bg1_tilemap: + ;; line 0 : objectives screen "window title" + dw $280F, "OBJECTIVES", $280F + +print "85 end: ", pc +warnpc !bank_85_free_space_end + +org !bank_B6_free_space_start +obj_txt_ptrs: +;; max size for single screen: (30 char dw + terminating dw) * 18 lines = 1116 bytes + +warnpc !bank_B6_free_space_end + +; objective screen tiles +; 'Q' +org $e29a00 + db $7C, $00, $C6, $00, $C6, $00, $C6, $00, $DA, $00, $CC, $00, $76, $00, $00, $00 + db $FF, $7C, $FF, $C6, $FF, $C6, $FF, $C6, $FF, $DA, $FF, $CC, $FF, $76, $FF, $00 + +; '.' +org $e29b60 + db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $18, $00, $18, $00, $00, $00 + db $FF, $00, $FF, $00, $FF, $00, $FF, $00, $FF, $00, $FF, $18, $FF, $18, $FF, $00 + +; '/' +org $e29b80 + db $03, $00, $06, $00, $0C, $00, $18, $00, $30, $00, $60, $00, $C0, $00, $00, $00 + db $FF, $03, $FF, $06, $FF, $0C, $FF, $18, $FF, $30, $FF, $60, $FF, $C0, $FF, $00 + +; '!' +org $e29be0 + db $18, $00, $18, $00, $18, $00, $18, $00, $00, $00, $18, $00, $18, $00, $00, $00 + db $FF, $18, $FF, $18, $FF, $18, $FF, $18, $FF, $00, $FF, $18, $FF, $18, $FF, $00 + + +; check mark +org $e2a160 + db $01, $00, $03, $00, $06, $00, $8C, $00, $D8, $00, $70, $00, $20, $00, $00, $00 + db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + +; top of 'OBJ' button +org $e2a180 + db $00, $FF, $00, $00, $FF, $FF, $FF, $FF, $F8, $F8, $F0, $F0, $F2, $F2, $F2, $F2 + db $FF, $FF, $FF, $FF, $00, $FF, $00, $FF, $07, $F8, $0F, $F0, $0D, $F2, $0D, $F2 + db $00, $FF, $00, $00, $FF, $FF, $FF, $FF, $C1, $C1, $4C, $4C, $4C, $4C, $41, $41 + db $FF, $FF, $FF, $FF, $00, $FF, $00, $FF, $3E, $C1, $B3, $4C, $B3, $4C, $BE, $41 + db $00, $FF, $00, $00, $FF, $FF, $FF, $FF, $F3, $F3, $F3, $F3, $F3, $F3, $F3, $F3 + db $FF, $FF, $FF, $FF, $00, $FF, $00, $FF, $0C, $F3, $0C, $F3, $0C, $F3, $0C, $F3 + +; bottom of 'OBJ' button +org $e2a380 + db $F2, $F2, $F2, $F2, $F0, $F0, $F8, $F8, $FF, $FF, $00, $FF, $00, $00, $00, $FF + db $0D, $F2, $0D, $F2, $0F, $F0, $07, $F8, $00, $FF, $FF, $00, $FF, $FF, $FF, $FF + db $41, $41, $4C, $4C, $4C, $4C, $C1, $C1, $FF, $FF, $00, $FF, $00, $00, $00, $FF + db $BE, $41, $B3, $4C, $B3, $4C, $3E, $C1, $00, $FF, $FF, $00, $FF, $FF, $FF, $FF + db $93, $93, $93, $93, $83, $83, $C7, $C7, $FF, $FF, $00, $FF, $00, $00, $00, $FF + db $6C, $93, $6C, $93, $7C, $83, $38, $C7, $00, $FF, $FF, $00, $FF, $FF, $FF, $FF diff --git a/patches/src/tables/pause_menu_objectives_chars.tbl b/patches/src/tables/pause_menu_objectives_chars.tbl new file mode 100644 index 0000000000..4a6568cbd8 --- /dev/null +++ b/patches/src/tables/pause_menu_objectives_chars.tbl @@ -0,0 +1,42 @@ +2800=0 +2801=1 +2802=2 +2803=3 +2804=4 +2805=5 +2806=6 +2807=7 +2808=8 +2809=9 +280E= +28C0=A +28C1=B +28C2=C +28C3=D +28C4=E +28C5=F +28C6=G +28C7=H +28C8=I +28C9=J +28CA=K +28CB=L +28CC=M +28CD=N +28CE=O +28CF=P +28D0=Q +28D1=R +28D2=S +28D3=T +28D4=U +28D5=V +28D6=W +28D7=X +28D8=Y +28D9=Z +28DA=. +28DC=/ +28DD=- +28DE=? +28DF=! From 8a911171c39d0f0c5b7f91d9ad92d17e195834b1 Mon Sep 17 00:00:00 2001 From: Brent Kerby Date: Thu, 13 Feb 2025 11:20:43 -0700 Subject: [PATCH 2/3] integrate pause_menu_objectives with randomizer --- patches/ips/pause_menu_objectives.ips | Bin 0 -> 1698 bytes patches/patch_manifest.json | 2 +- patches/src/pause_menu_objectives.asm | 32 ++------- rust/maprando/src/customize.rs | 1 + rust/maprando/src/patch.rs | 90 ++++++++++++++++++++++++++ scripts/build_ips.py | 8 ++- 6 files changed, 105 insertions(+), 28 deletions(-) create mode 100644 patches/ips/pause_menu_objectives.ips diff --git a/patches/ips/pause_menu_objectives.ips b/patches/ips/pause_menu_objectives.ips new file mode 100644 index 0000000000000000000000000000000000000000..9b16f7c43ed3f248e8e8cedd96da1b3209a253d9 GIT binary patch literal 1698 zcmbVMZD?Cn7=H8B+mE!FT{lDHL}#PJ%X);Ew(bE7-;*#k+o0n3D^Y2A*|cj1r3Q#;*IuX) z_K!1YcZ3d?G#2q-se&yg)LQ=f?|@k(rguen^(iQ~4=(&Ti6)1MfXAi4{qv*{2=d|f z@Ud_*{Pf6~@b_UDITqetgpm}vN^;}|xk(r%9QarY2(Pj3JnMd7og*d%%n{!DR^_4> zH^`^m*eCb7aY*iW3pgyAKs*(boPf=8O*;ynJ(R@lAk{}(xHutm>3UFw$@;>@7A^&= zUn7QLT)DFQh2q5`QZq&c{WV04OY2LACMOh}!sdiqg4TNUc(og|zhxLA-}+ znpsRqS+Ie+mAd9_{90tV>O<$7B77=lOUYnisQNP;0?C|8-+f<#)Ew=t7ofD$8W{4l zfm(y>ke>H5A78}wfIDnGr+bPm$$pISAN`$MR{9!nVU~v&iGnSU$@uIPPWhb6!g@Sef-s{bU>DA>TH*1@dOGcj^e2GlTlh zRCPx7aM%oT7soIQsC8_%Tf>fUq8~f78GuY@&*(n;^*ECe2W(R|<{71Kc5K&zEG(!n zv2F4jVT|;Zun&Y6hedS!qeLG)pLjJZ_T`@O*h|mSBN!OSuj$aK@z~G?Xly*zy#b1i z$3A})O0r|uHBN7qJ-bOZ8N7|`o?1_xM?v?|6P_KOCp|@U8!e$b=oiGWKhU4(FH}Uo zqGfa!t)Sn~->8KCLF?#WM9ca}zKsi{`8F&tMO{4PXZmU$>}I9B-W2uJ86(DEXmvEE z;X$1Yh89U@jB>3cQLr#!OOy*BJ0uSppxCgoE_QMWu3v zhld?)KGm`>Z^ok3h00(fMXRJP_AxWifo`A-%Au<$i*BMh^c@|2iyX++2*CHkHLVrk zI-CRm(o3#stw64mlLTC@034vWZccF05qa30#7V#c6=0`~7XpxGyoFQ{J7v5iK+;TP z0eBW#4%bCks1{!CUG4zTKRc%N9?Zkrz*D77MyNWqVo_CBsj)Fz3|DlhwX3zObgc22 zOb0c;-{4y_GaWM>SNsOQrklar*UHV+Btn1*t7@^RNmD&0>`=KPRo2A)ekPvD82lAK e6K~BJ{3x~l{@GcB?;h Resul "mb_barrier_clear", "mb_left_entrance", "gray_doors", + "pause_menu_objectives", // For the pause menu tileset changes (for green checkmark, etc.) ]; patches.push("hud_expansion_opaque"); for patch_name in patches { @@ -465,6 +466,7 @@ impl<'a> Patcher<'a> { "beam_doors", "horizontal_door_fix", "samus_tiles_optim_animated_tiles_fix", + "pause_menu_objectives", ]; if self.randomization.settings.other_settings.ultra_low_qol { @@ -539,6 +541,9 @@ impl<'a> Patcher<'a> { .fast_pause_menu { patches.push("fast_pause_menu"); + self.rom.write_u16(snes2pc(0x82fffc), 0xFFFF)?; + } else { + self.rom.write_u16(snes2pc(0x82fffc), 0x0000)?; } match self.randomization.settings.other_settings.wall_jump { @@ -2774,6 +2779,90 @@ impl<'a> Patcher<'a> { Ok(()) } + + fn write_objective_data(&mut self) -> Result<()> { + let mut obj_text: Vec = vec![]; + for obj in &self.randomization.objectives { + obj_text.push("".to_string()); // blank line + let text = match obj { + Objective::Kraid => "DEFEAT KRAID", + Objective::Phantoon => "DEFEAT PHANTOON", + Objective::Draygon => "DEFEAT DRAYGON", + Objective::Ridley => "DEFEAT RIDLEY", + _ => panic!("unhandled objective"), + }; + obj_text.push(" - ".to_string() + text); + } + if self.randomization.save_animals == SaveAnimals::Yes { + obj_text.push("".to_string()); + obj_text.push(" - SAVE THE ANIMALS".to_string()); + } + + let char_mapping: HashMap = vec![ + ('0', 0x2800), + ('1', 0x2801), + ('2', 0x2802), + ('3', 0x2803), + ('4', 0x2804), + ('5', 0x2805), + ('6', 0x2806), + ('7', 0x2807), + ('8', 0x2808), + ('9', 0x2809), + (' ', 0x280E), + ('A', 0x28C0), + ('B', 0x28C1), + ('C', 0x28C2), + ('D', 0x28C3), + ('E', 0x28C4), + ('F', 0x28C5), + ('G', 0x28C6), + ('H', 0x28C7), + ('I', 0x28C8), + ('J', 0x28C9), + ('K', 0x28CA), + ('L', 0x28CB), + ('M', 0x28CC), + ('N', 0x28CD), + ('O', 0x28CE), + ('P', 0x28CF), + ('Q', 0x28D0), + ('R', 0x28D1), + ('S', 0x28D2), + ('T', 0x28D3), + ('U', 0x28D4), + ('V', 0x28D5), + ('W', 0x28D6), + ('X', 0x28D7), + ('Y', 0x28D8), + ('Z', 0x28D9), + ('.', 0x28DA), + ('/', 0x28DC), + ('-', 0x28DD), + ('?', 0x28DE), + ('!', 0x28DF), + ].into_iter().collect(); + + let mut addr = snes2pc(0xB6F200); + for line in &obj_text { + for c in line.chars() { + let tile_word = char_mapping[&c]; + self.rom.write_u16(addr, tile_word as isize)?; + addr += 2; + } + self.rom.write_u16(addr, 0x8000)?; // line terminator + addr += 2; + } + + // Add empty lines for unused rows: + for _ in 0..(18 - obj_text.len()) { + self.rom.write_u16(addr, 0x8000)?; // line terminator + addr += 2; + } + + assert!(addr < snes2pc(0xB6F660)); + Ok(()) + } } fn get_other_door_ptr_pair_map(map: &Map) -> HashMap { @@ -2846,6 +2935,7 @@ pub fn make_rom( patcher.apply_miscellaneous_patches()?; patcher.apply_mother_brain_fight_patches()?; patcher.write_walljump_item_graphics()?; + patcher.write_objective_data()?; patcher.apply_seed_identifiers()?; patcher.apply_credits()?; if !randomization.settings.other_settings.ultra_low_qol { diff --git a/scripts/build_ips.py b/scripts/build_ips.py index eef41ba8d2..54d1b195a7 100644 --- a/scripts/build_ips.py +++ b/scripts/build_ips.py @@ -91,9 +91,13 @@ def write_ips_patch(ips_path, changed_byte_dict, chunks): ips_path = f"{OUTPUT_PATH}/{base_filename}.ips" src_modified_ts = os.path.getmtime(asm_path) - ips_modified_ts = os.path.getmtime(ips_path) + ips_modified_ts = None + try: + ips_modified_ts = os.path.getmtime(ips_path) + except FileNotFoundError as e: + pass - if src_modified_ts > ips_modified_ts or args.verify: + if ips_modified_ts is None or src_modified_ts > ips_modified_ts or args.verify: logging.info(f"Assembling {asm_path}") changed_bytes = {} run_asar(asm_path, 0x00, changed_bytes) From 372a19eacc1940a6f1450263278b187c403f1021 Mon Sep 17 00:00:00 2001 From: Brent Kerby Date: Thu, 13 Feb 2025 11:31:23 -0700 Subject: [PATCH 3/3] handle remaining objectives, fix rust formatting --- rust/maprando/src/patch.rs | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/rust/maprando/src/patch.rs b/rust/maprando/src/patch.rs index 7d0ce8aa76..2ede20f32d 100644 --- a/rust/maprando/src/patch.rs +++ b/rust/maprando/src/patch.rs @@ -388,7 +388,7 @@ fn apply_orig_ips_patches(rom: &mut Rom, randomization: &Randomization) -> Resul "mb_barrier_clear", "mb_left_entrance", "gray_doors", - "pause_menu_objectives", // For the pause menu tileset changes (for green checkmark, etc.) + "pause_menu_objectives", // For the pause menu tileset changes (for green checkmark, etc.) ]; patches.push("hud_expansion_opaque"); for patch_name in patches { @@ -2785,17 +2785,34 @@ impl<'a> Patcher<'a> { for obj in &self.randomization.objectives { obj_text.push("".to_string()); // blank line let text = match obj { + Objective::AcidChozoStatue => "ACTIVATE ACID CHOZO STATUE", + Objective::BabyKraidRoom => "CLEAR BABY KRAID ROOM", + Objective::BombTorizo => "DEFEAT BOMB TORIZO", + Objective::Botwoon => "DEFEAT BOTWOON", + Objective::BowlingStatue => "ACTIVATE BOWLING STATUE", + Objective::Crocomire => "DEFEAT CROCOMIRE", + Objective::Draygon => "DEFEAT DRAYGON", + Objective::GoldenTorizo => "DEFEAT GOLDEN TORIZO", Objective::Kraid => "DEFEAT KRAID", + Objective::MetalPiratesRoom => "CLEAR METAL PIRATES ROOM", + Objective::MetroidRoom1 => "CLEAR METROID ROOM 1", + Objective::MetroidRoom2 => "CLEAR METROID ROOM 2", + Objective::MetroidRoom3 => "CLEAR METROID ROOM 3", + Objective::MetroidRoom4 => "CLEAR METROID ROOM 4", Objective::Phantoon => "DEFEAT PHANTOON", - Objective::Draygon => "DEFEAT DRAYGON", + Objective::PitRoom => "CLEAR PIT ROOM", + Objective::PlasmaRoom => "CLEAR PLASMA ROOM", + Objective::SporeSpawn => "DEFEAT SPORE SPAWN", Objective::Ridley => "DEFEAT RIDLEY", - _ => panic!("unhandled objective"), }; obj_text.push(" - ".to_string() + text); } - if self.randomization.save_animals == SaveAnimals::Yes { + if self.randomization.settings.save_animals == SaveAnimals::Yes { obj_text.push("".to_string()); - obj_text.push(" - SAVE THE ANIMALS".to_string()); + obj_text.push(" - SAVE THE ANIMALS!".to_string()); + } else if self.randomization.settings.save_animals == SaveAnimals::Random { + obj_text.push("".to_string()); + obj_text.push(" - SAVE THE ANIMALS?".to_string()); } let char_mapping: HashMap = vec![ @@ -2841,7 +2858,9 @@ impl<'a> Patcher<'a> { ('-', 0x28DD), ('?', 0x28DE), ('!', 0x28DF), - ].into_iter().collect(); + ] + .into_iter() + .collect(); let mut addr = snes2pc(0xB6F200); for line in &obj_text { @@ -2850,16 +2869,16 @@ impl<'a> Patcher<'a> { self.rom.write_u16(addr, tile_word as isize)?; addr += 2; } - self.rom.write_u16(addr, 0x8000)?; // line terminator + self.rom.write_u16(addr, 0x8000)?; // line terminator addr += 2; } // Add empty lines for unused rows: for _ in 0..(18 - obj_text.len()) { - self.rom.write_u16(addr, 0x8000)?; // line terminator + self.rom.write_u16(addr, 0x8000)?; // line terminator addr += 2; } - + assert!(addr < snes2pc(0xB6F660)); Ok(()) }