Fixing the broken Water Level in TMNT NES based on Displaced Gamers' video

Started by ShadowOne333, August 11, 2022, 12:43:41 PM

Previous topic - Next topic

ShadowOne333

Hey there Romhacking community.

A few days ago, Displaced Gamers released a video focusing on the Water Level from the Teenage Mutant Ninja Turtles game on NES:
https://www.youtube.com/watch?v=PHiFNWJXWgI

This level is infamous for its wonky swimming mechanics and the water currents pulling the turtles when you traverse it.

They offer suggestions to fix the code itself at around 10:57 in the video.
I tried my video to rework the code based on their suggestions, but I am struggling a bit with what seems to be the cap logic and the sinking/swimming up/down routines after the fix.

I'll share the code as I have it right now ready for compilation with Asar.
Any kind of help to close this one up would be greatly appreciated, as I think this would make one of the most obnoxious parts of that game much better just with this fix.

; TMNT (NES) Water Level Fixes
; By Displaced Gamers

;---------------------
; Macro definition for NES banks/addresses
norom

macro org(address,bank)
!headersize = $10
!banksize = $4000

org (<address>%!banksize)+(!banksize*<bank>)+!headersize

base <address>
endmacro

;---------------------
; Variable definitions

!TurtleYVelo_4B0 = $04B0
!TurtleYVelo_SubPix_4C0 = $04C0
!TurtleXVelo_4E0 = $04E0
!TurtleXVelo_SubPix_4F0 = $04F0
!Env_Collision_Check_Driver_Sub = $A896

;-----------------------
%org($A7E4,4) ; 0x127F4, bank 4
; Controller Check
Water_Time_for_Velocities:
lda $32
and #$03 ; Look for Left or Right presses

; Offset: 0 (No Left/Right press), 1 (Right), or 2 (Left)
tay
lda $A9B5,y ; X Velocity Look-up. No L/R: 0, L: FE, R: 2
sta !TurtleXVelo_4E0

; Apply natural sinking velocity to Turtle Y Sub Velo
lda !TurtleYVelo_SubPix_4C0 ; AD C0 04
clc
adc #$10
sta !TurtleYVelo_SubPix_4C0 ; 8D C0 04

; Roll sinking Velo based on sub Velo rollover
lda !TurtleYVelo_4B0
adc #$00
; bmi Water_Save_Starting_YVelo ; Skip ahead if negative (Moving up)
; cmp #$02
; bcc Water_Save_Starting_YVelo
; lda #$02 ; Cap sinking Y velocity at 2

;Water_Save_Starting_YVelo:
; sta !TurtleYVelo_4B0 ; Set YVelo after applying sinking Y Sub Velo

; $30/$31/$32 work in tandem to help check for multiple butto mashing of A for swimming.
; This check is for adding to Y velocity for an A button press
lda $30
and #$80
beq Water_Apply_Current_to_Turtle

; A button was pressed. Turtle is swimming
; Additional upward velocity is valid
lda !TurtleYVelo_SubPix_4C0
clc
adc #$20 ; Add 20 to Turtle Y Subpix Velo
sta !TurtleYVelo_SubPix_4C0
lda !TurtleYVelo_4B0
adc #$FF ; Apply rollover to Y Velo
; bpl Water_Save_YVelo_for_Swim_Button
; cmp #$FD ; Maximum upward velo?
; bcs Water_Save_YVelo_for_Swim_Button
; lda #$FD ; Cap at maximum (2 pixels upward)

;Water_Save_YVelo_for_Swim_Button:
; sta !TurtleYVelo_4B0 ;  Save Turtle Y Velo after swimming

;-------------------------------
; Moved controller check to above Current code

; Controller check for Down on Dpad
lda $32
and #$04
beq Water_Velocity_Complete

; Down is pressed. Add to subpixel Yvelo
lda !TurtleYVelo_SubPix_4C0
clc
adc #$30 ; ( + 48/256) to Sub
sta !TurtleYVelo_SubPix_4C0
lda !TurtleYVelo_4B0
adc #$00 ; (Carry from above addition)
sta !TurtleYVelo_4B0

;-------------------------------

; Apply Current to Turtle
; Four velocity values are looked up for Current:
; X, X Subpixel, Y, Y Subpixel
Water_Apply_Current_to_Turtle:
lda $7E
;beq Water_Velocity_Complete ; If no current present, skip ahead
beq Zero_Out_Cap
lsr
lsr
lsr
lsr
tay ; Current offset 1
lda $A9C0,y
tay ; Current offset 2
lda $A9C5,y ; Load X Current SubPix value

; Apply X Current to Turtle X Velocity
clc
adc !TurtleXVelo_SubPix_4F0
sta !TurtleXVelo_SubPix_4F0
lda $A9C6,y ; Load X Current value
adc !TurtleXVelo_4E0
sta !TurtleXVelo_4E0
lda $A9C7,y ; Load Y Current SubPix value

; Apply Y Current to Turtle Y Velocity
clc
adc !TurtleYVelo_SubPix_4C0
sta !TurtleYVelo_SubPix_4C0
lda $A9C8,y ; Load X Current value
adc !TurtleYVelo_4B0
sta !TurtleYVelo_4B0

;-------------------------------
; Zero-out the fractional X velocity
Zero_Out_Cap:
lda #$00
sta !TurtleXVelo_SubPix_4F0

; Pad with NOPs
padbyte $EA : pad $12881
warnpc $12881 ; 0x12881, bank 4
;-------------------------------

; Velocity application complete
%org($A871,4) ; 0x12881, bank 4
Water_Velocity_Complete:
jsr Cap_Turtle_YVelo_Sub
jmp !Env_Collision_Check_Driver_Sub

Cap_Turtle_YVelo_Sub:
lda !TurtleYVelo_4B0
bmi Cap_Turtle_Upward_YVelo
cmp #$02
bcc Cap_Turtle_YVelo_Sub_Exit
lda #$02
sta !TurtleYVelo_4B0 ; Cap downward Y velocity at 2
bne Cap_Turtle_YVelo_Clear_Subpix

Cap_Turtle_Upward_YVelo:
cmp #$FE
bcs Cap_Turtle_YVelo_Sub_Exit
lda #$FE
sta !TurtleYVelo_4B0 ; Cap upward Y velocity at -2

; Zero subpixel velocity after capping YVelo with min or max
Cap_Turtle_YVelo_Clear_Subpix:
lda #$00
sta !TurtleYVelo_SubPix_4C0
Cap_Turtle_YVelo_Sub_Exit:
rts

;----------------------------------------------------------------

SMB2J-2Q

I have always been amazed by what Displaced Gamers discovers regarding all this seemingly buggy code that could indeed use some fixing up, including what he found for Super Mario Bros. and its infamous "Minus World."

~Ben