diff --git a/patches/ips/escape.ips b/patches/ips/escape.ips index 14c924830..f80451da6 100644 Binary files a/patches/ips/escape.ips and b/patches/ips/escape.ips differ diff --git a/patches/ips/escape_items.ips b/patches/ips/escape_items.ips index 0c1a28af8..0c832380f 100644 Binary files a/patches/ips/escape_items.ips and b/patches/ips/escape_items.ips differ diff --git a/patches/ips/item_count.ips b/patches/ips/item_count.ips new file mode 100644 index 000000000..77ea3c437 Binary files /dev/null and b/patches/ips/item_count.ips differ diff --git a/patches/ips/new_game.ips b/patches/ips/new_game.ips index 5328994b3..6662e8f98 100644 Binary files a/patches/ips/new_game.ips and b/patches/ips/new_game.ips differ diff --git a/patches/rom_map/Bank DF.txt b/patches/rom_map/Bank DF.txt index e4411ab9c..22dceb1a9 100644 --- a/patches/rom_map/Bank DF.txt +++ b/patches/rom_map/Bank DF.txt @@ -2,13 +2,16 @@ D4DF - D761: ; credits.asm D91B - DF0A: ; credits.asm E000 - E127: ; credits.asm E200 - E212: ; alternate_door_colors.asm -E212 - FEEF: [free] +E212 - E2B4: ; item_count.asm +E2B4 - FEEF: [free] FEF0 - FEFF: seed name (null-terminated ASCII string) FF00 - FF03: display seed / internal seed hash -FF04 - FF04: [FREE] +FF04 - FF04: [free] FF05 - FF06: randomizer settings: - bitmask $0001: walljump-boots item enabled - bitmask $0002: split speedbooster item enabled FF07 - FF08: number of frames of artificial lag to add to the unpause black screen FF09 - FF0D: randomizer settings: skill/progression/qol/objectives/maplayout (autotracker use) - stored as single byte ordinal numbers, if a setting has a custom option then byte is set to 0 +FF0E - FF0F: sum of unplaced / nothing items (used for item credits fractional display with stop item placement early) +FF10 - FF11: sum of starting items ; written by patch.rs. (added to collection % during new game) \ No newline at end of file diff --git a/patches/rom_map/RAM.txt b/patches/rom_map/RAM.txt index 97a1939d0..202a1a9d7 100644 --- a/patches/rom_map/RAM.txt +++ b/patches/rom_map/RAM.txt @@ -2,10 +2,10 @@ $95: temporary variable (unused debug location) - used by extended messageboxes, $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) +$09EE: collected item_count (saved to SRAM) $0A1A: previous reserve health (reserve_hud.asm) $1F5B: map area (like $079F) -$1F5D: items collected before getting them all for free at hyper beam (to use in "rate for collecting items" - after credits). This is a snapshot of $09A4, captured in `escape.asm`, at the time of getting hyper beam. +$1F5D: [free] - previously used as escape items snapshot. $1F60: fast_reload.asm $1F62: map area backup in pause menu (like $079F) $1F64: escape refill complete diff --git a/patches/src/escape.asm b/patches/src/escape.asm index e195f1974..4e5a0d152 100644 --- a/patches/src/escape.asm +++ b/patches/src/escape.asm @@ -650,17 +650,17 @@ assert pc() <= !bank_a7_free_space_end ; Include walljump boots and split-speed in item collection count post-credits -org $8BE65B - LDX #$001A +;org $8BE65B +; LDX #$001A -org $8BE661 - BIT item_bits,x +;org $8BE661 + ; BIT item_bits,x -org !bank_8b_free_space_start -item_bits: - dw $0001, $0020, $0004, $1000, $0002, $0008, $0100, $0200, $2000, $4000, $8000, $0400, $0040, $0080 +;org !bank_8b_free_space_start +;item_bits: +; dw $0001, $0020, $0004, $1000, $0002, $0008, $0100, $0200, $2000, $4000, $8000, $0400, $0040, $0080 -assert pc() <= !bank_8b_free_space_end +;assert pc() <= !bank_8b_free_space_end org $82E026 jsr enemy_gfx_load_hook diff --git a/patches/src/escape_items.asm b/patches/src/escape_items.asm index 75ea62033..2f82244a0 100644 --- a/patches/src/escape_items.asm +++ b/patches/src/escape_items.asm @@ -6,8 +6,8 @@ org $A9CD12 org $A9FB70 get_hyper_beam: jsl $91E4AD ; run the hi-jacked instruction - lda $09A4 - sta $1F5D ; take a snapshot of items collected, to use in "rate for collecting items" percentage after credits. + ; lda $09A4 + ; sta $1F5D ; take a snapshot of items collected, to use in "rate for collecting items" percentage after credits. lda #$F72F sta $09A2 ; all items equipped (including WallJump = $0400) sta $09A4 ; all items collected @@ -20,43 +20,43 @@ get_hyper_beam: jsl $809A3E ; Add x-ray to HUD tilemap rtl -warnpc $A9FC00 +assert pc() <= $A9FC00 ; hi-jack item percentage count -org $8BE62F - jsr fix_item_percent +;org $8BE62F +; jsr fix_item_percent ; free space in bank $8B -org $8BF760 -fix_item_percent: +;org $8BF760 +;fix_item_percent: ; restore snapshot of items collected to their state before getting them all with hyper beam (except that ; any items collected during the escape also count). - lda $1F5D - sta $09A4 + ;lda $1F5D + ;sta $09A4 - ldx #$0008 ; run hi-jacked instruction - rts -warnpc $8BF770 + ;ldx #$0008 ; run hi-jacked instruction + ;rts +;warnpc $8BF770 -org $848902 - jsr escape_collect_item +;org $848902 +; jsr escape_collect_item -org $848929 - jsr escape_collect_item +;org $848929 +; jsr escape_collect_item -org $848950 - jsr escape_collect_item +;org $848950 +; jsr escape_collect_item ; free space in bank $84 -org $84F300 -escape_collect_item: - sta $09A4 ; run hi-jacked instruction +;org $84F300 +;escape_collect_item: +; sta $09A4 ; run hi-jacked instruction ; add collected item to snapshot, to count toward item collection percentage ; (this only matters for items collected during escape) - lda $1F5D - ora $0000, y - sta $1F5D +; lda $1F5D +; ora $0000, y +; sta $1F5D - rts -warnpc $84F380 \ No newline at end of file +; rts +;warnpc $84F380 \ No newline at end of file diff --git a/patches/src/item_count.asm b/patches/src/item_count.asm new file mode 100644 index 000000000..9cf9d1b0d --- /dev/null +++ b/patches/src/item_count.asm @@ -0,0 +1,258 @@ +; Modify plms to increase a new collected items variable upon item collection. +; address is automatically saved / loaded to the save file +; +; overwrites unused routine $848821 set the boss bits [[Y]] +; +; $09ee contains number of collected items. +; +; credits % is displayed as a fraction if every item isn't placed. +; nn_357 / StagShot + +; !escape_seed = $dfff04 ; overwritten in patch.rs +!starting_items_count = $dfff10 ; overwritten in patch.rs, also referenced in new_game.asm +!unplaced_total = $dfff0e ; overwritten by patch.rs, contains the sum of 'nothing' +!item_count = $09ee + +org !unplaced_total + dw $0000 + +; org !escape_seed + ; db $00 + +org $848821 ;;; $8821: Unused. Instruction - set the boss bits [[Y]] ;;; New routine - Update sum of items collected. preserves [A] unnecessary? +item_count: + pha + lda !item_count + inc + sta !item_count + iny + iny + pla + rts +assert pc() <= $84882D + +;; $88B0: Instruction - pick up beam [[Y]] and display message box [[Y] + 2] ;;; +org $8488F0 + jmp item_count +assert pc() <= $8488f3 + +;;; $88F3: Instruction - pick up equipment [[Y]] and display message box [[Y] + 2] ;;; +org $848917 + jmp item_count +assert pc() <= $84891a + +;;; $891A: Instruction - pick up equipment [[Y]], add grapple to HUD and display grapple message box ;;; +org $84893e + jmp item_count +assert pc() <= $848941 + +;;; $8941: Instruction - pick up equipment [[Y]], add x-ray to HUD and display x-ray message box ;;; +org $848965 + jmp item_count +assert pc() <= $848968 + +;;; $8968: Instruction - collect [[Y]] health energy tank ;;; +org $848983 + jmp item_count +assert pc() <= $848986 + + +;;; $8986: Instruction - collect [[Y]] health reserve tank ;;; +org $8489a6 + jmp item_count +assert pc() <= $8489a9 + +;;; $89A9: Instruction - collect [[Y]] ammo missile tank ;;; +org $8489cf + jmp item_count +assert pc() <= $8489d2 + +;;; $89D2: Instruction - collect [[Y]] ammo super missile tank ;;; +org $8489f8 + jmp item_count +assert pc() <= $8489fb + +;;; $89FB: Instruction - collect [[Y]] ammo power bomb tank ;;; +org $848a21 + jmp item_count +assert pc() <= $848a24 + +;;; $E627: Instruction - draw item percentage count ;;; + +org $8be627 + php + phb + phk + plb + rep #$30 + phx + phy + lda !unplaced_total ; everything placed? + beq .check_100 + cmp !starting_items_count ; nothing count = same as starting items? (seed has starting items / escape seed) + beq .check_100 + + bra .fractional ; seed has more nothings than starting items (desolate / small map / stop item placement) + +.check_100: + lda !item_count + cmp #$0064 + bne .not_100 +.is_100 + lda $e745 ; 100% completion + sta $7e339c + lda $e747 + sta $7e33dc + lda $e741 + sta $7e339e + lda $e743 + sta $7e33de + lda $e741 + sta $7e33a0 + lda $e743 + sta $7e33e0 + bra .percent_symbol + +.not_100 + ldx #$0002 + jsr .digit2 + bra .percent_symbol + +.fractional + jsl load_fractional_tiles ; transfer custom '/' tile to VRAM + lda #$0064 + sec + sbc !unplaced_total + beq .is_100 ; if you set enough starting items / higher difficulty nothing will be placed, this can result in a xx/00, in which case just display 100%. + sta $14 + + lda !item_count + ldx #$0000 + jsr .digit2 + + lda $14 + ldx #$0006 + jsr .digit2 + + lda #$3888 ; add custom '/' tile to tilemap + sta $7e33a0 + lda #$3898 + sta $7e33e0 + bra .finished + +.percent_symbol + lda #$386a + sta $7e33a2 + lda #$387a + sta $7e33e2 +.finished + ply + plx + plb + plp + rts + +.digit2 + sta $4204 + sep #$20 + lda #$0A + sta $4206 + nop #6 + rep #$20 + lda $4214 + asl a + asl a + tay + lda $e741,y + sta $7e339c,x + lda $e743,y + sta $7e33dc,x + lda $4216 + asl a + asl a + tay + lda $e741,y + sta $7e339e,x + lda $e743,y + sta $7e33de,x + rts + +assert pc() <= $8be741 ; Tilemap values for decimal digits: + +;8BE6ED + +org $dfe212 + +load_fractional_tiles: + + php + sep #$30 +WaitVB: + lda $4212 + and #$80 + beq WaitVB + + lda #$80 ; top_slash → $4880 + sta $2115 + lda #$80 + sta $2116 + lda #$48 + sta $2117 + + lda #$01 + sta $4300 + lda #$18 + sta $4301 + lda.b #top_slash + sta $4302 + lda.b #top_slash>>8 + sta $4303 + lda #$DF + sta $4304 + lda #$20 + sta $4305 + stz $4306 + lda #$01 + sta $420B + + lda #$80 ; bottom_slash → $4980 + sta $2116 + lda #$49 + sta $2117 + + lda.b #bottom_slash + sta $4302 + lda.b #bottom_slash>>8 + sta $4303 + lda #$20 + sta $4305 + stz $4306 + lda #$01 + sta $420B + + plp + rtl + +top_slash: + db $00, $07, $02, $0D, $06, $09, $06, $09 + db $0C, $13, $0C, $32, $18, $26, $18, $24 + db $00, $00, $00, $00, $00, $00, $00, $00 + db $00, $00, $00, $00, $00, $00, $00, $00 + +; db $00,$00,$00,$00,$00,$00,$00,$00 +; db $00,$0F,$06,$19,$0C,$33,$18,$26 +; db $00,$00,$00,$00,$00,$00,$00,$00 +; db $00,$00,$00,$00,$00,$00,$00,$00 + +bottom_slash: + db $18, $64, $30, $4C, $30, $C8, $60, $98 + db $60, $90, $40, $B0, $00, $E0, $00, $00 + db $00, $00, $00, $00, $00, $00, $00, $00 + db $00, $00, $00, $00, $00, $00, $00, $00 + +; db $18,$64,$30,$CC,$60,$98,$00,$F0 +; db $00,$00,$00,$00,$00,$00,$00,$00 +; db $00,$00,$00,$00,$00,$00,$00,$00 +; db $00,$00,$00,$00,$00,$00,$00,$00 + +assert pc() <= $dfe2b4 \ No newline at end of file diff --git a/patches/src/new_game.asm b/patches/src/new_game.asm index 6bc8eefef..762f804ca 100644 --- a/patches/src/new_game.asm +++ b/patches/src/new_game.asm @@ -36,6 +36,10 @@ incsrc "constants.asm" !initial_max_power_bombs = $B5FE66 !initial_save_map_coords = $702602 !spin_lock_enabled = $1F70 +!starting_items_count = $DFFF10 ; overwritten in patch.rs + +org $dfff10 ; starting items count. +dw $0000 ;;; Hijack code that runs during initialization org $82801d @@ -82,6 +86,8 @@ startup: sta $1F5B ; Initialize items/flags collected/equipped: + lda !starting_items_count + sta $09ee lda !initial_items_collected sta $09A4 lda !initial_items_equipped diff --git a/rust/maprando/src/patch.rs b/rust/maprando/src/patch.rs index a05f7ea5f..553bad69a 100644 --- a/rust/maprando/src/patch.rs +++ b/rust/maprando/src/patch.rs @@ -487,6 +487,7 @@ impl Patcher<'_> { "fix_dust_torizo", "fix_choot", "resource_collection_hudcap", + "item_count", ]; patches.push("new_game"); @@ -839,6 +840,7 @@ impl Patcher<'_> { } fn place_items(&mut self) -> Result<()> { + let mut nothing_count: isize = 0; for (&item, &loc) in iter::zip( &self.randomization.item_placement, &self.game_data.item_locations, @@ -848,6 +850,7 @@ impl Patcher<'_> { let new_plm_type = item_to_plm_type(item, orig_plm_type); self.rom.write_u16(item_plm_ptr, new_plm_type)?; if item == Item::Nothing { + nothing_count += 1; let idx = self.rom.read_u16(item_plm_ptr + 4).unwrap() as usize; self.nothing_item_bitmask[idx >> 3] |= 1 << (idx & 7); @@ -859,6 +862,7 @@ impl Patcher<'_> { } } } + self.rom.write_u16(snes2pc(0xdfff0e), nothing_count)?; Ok(()) } @@ -2317,6 +2321,22 @@ impl Patcher<'_> { .into_iter() .collect(); + let mut starting_item_count: isize = 0; + for x in self.starting_items { + if x.count == 0 { + continue; + } + match x.item { + Item::Missile | Item::ETank | Item::ReserveTank | Item::Super | Item::PowerBomb => { + starting_item_count += x.count as isize; + } + _ => { + starting_item_count += 1; + } + } + } + self.rom.write_u16(snes2pc(0xDFFF10), starting_item_count)?; + for x in self.starting_items { if x.count == 0 { continue; @@ -2419,7 +2439,6 @@ impl Patcher<'_> { // Use Tourian load station 2, set up in escape_autosave.asm self.rom.write_u16(initial_area_addr, 5)?; self.rom.write_u16(initial_load_station_addr, 2)?; - // Set all bosses defeated: self.rom.write_n(initial_boss_bits, &[7, 7, 7, 7, 7, 7])?;