Points of Required Attention™
Please chime in on a proposed restructuring of the ROM hacking sections.
Views: 88,490,995
Main | FAQ | Uploader | IRC chat | Radio | Memberlist | Active users | Latest posts | Calendar | Stats | Online users | Search 04-27-24 04:04 AM
Guest: Register | Login

0 users currently in ROM Hacking | 6 guests | 4 bots

Main - ROM Hacking - SMB1 Disassembled EU Difference New thread | New reply


ShaneM
Posted on 01-19-15 10:57 PM (rev. 2 of 01-19-15 10:59 PM) Link | Quote | ID: 159366


Snifit
Compromised account.
Please contact an admin to restore access.
Level: 36

Posts: 263/285
EXP: 293504
Next: 14606

Since: 02-17-14

Last post: 3138 days
Last view: 2653 days

ChkForPlayerInjury:
lda Player_Y_Speed ;check player's vertical speed
bmi ChkInj ;perform procedure below if player moving upwards
bne EnemyStomped ;or not at all, and branch elsewhere if moving downwards
ChkInj:
.ifdef Japan
lda Enemy_ID,x ;branch if enemy object < $07
cmp #Bloober ;(Goomba, Buzzy Beetle, Green/Red Koopa and Hammer Bro will branch)
bcc ChkETmrs ;to check stomp/invincible timers and enemy facing direction
lda Player_Y_Position ;otherwise load player's vertical position
clc
adc #$0c ;and add 12 pixels to player's vertical position
.else
lda #FlyCheepCheepFrenzy ;#$14 gets loaded into A
ldy Enemy_ID,x ;branch if enemy object is not FlyCheepCheepFrenzy
cpy #$14
bne + ;branch to Player_Y_Position
lda #Bloober ;load #$07 into A
+ adc Player_Y_Position ;RAM $ce, add carry player's vertical position to A
.endif
cmp Enemy_Y_Position,x ;compare modified player's position to enemy's position
bcc EnemyStomped ;branch if player's position above (less than) enemy's
ChkETmrs: lda StompTimer ;check stomp timer
bne EnemyStomped ;branch if set


My thoughts: This code is only branched to if the player is vertically moving upward or standing still; moving downward vertically causes the BNE to branch in "ChkForPlayerInjury". "Player_Y_Speed" (RAM $9F) will be a signed value if the player is going up and unsigned if going down (so MSB or d7 WILL be set if this branch occurs). Now, if the player is indeed going up, we branch to "ChkInj", the routine in question. What the NTSC seems to do is branch to check various timers if dealing with enemies such as Goombas etc. or anything less than Blooper (#$07) as I labeled above. If the enemy in A plus X offset is #$07 or greater it will thus not branch because the Carry is set. Next, we load A with what's loaded at RAM $CE, or Player_Y_Position. (The player's Y is stored as #$B0 when on the ground at the lowest playable part of the level, decrementing from that value as the player is vertically inclined/stomps on an enemy.) $CF plus X offset is where the Enemy_Y_Position is stored and is #$08 greater than the player's when on flat ground. So, for example, a Goomba (RAM $16,x will load an #$06 in Memory) on flat ground will have a #$B8 loaded into RAM $CF plus X whereas the protagonist will have #$B0 in that occurrence stored at RAM $CE.

So anyway, if the enemy object in Memory plus X is #$07 or greater, Carry will be set so we will clear Carry and add #$0C to A (which is loading the player's current Y position). So A will thus have whatever that value is plus #$0c added to it for the upcoming comparison with RAM $CF plus X register (in layman's terms, the enemy which is loaded for the comparison since 6 enemies can be loaded at once). At the end of the .endif conditional statement above, we now move onto comparing the current value in A with the enemy offset. If the player's Y is at all above (being less than) the enemy's, we thus branch to the enemy being stomped "EnemyStomped" (and the timer flag at RAM $0791 will be set to branch; from there it checks for certain enemies such Spinys or other 'untouchable' enemies and branches accordingly to injure player or else award player certain pts.). If Carry is set, then we move on to check various timers and branch accordingly to injure player/turn enemies around etc. For enemies that are not Lakitu, Cheeps or the likes, we skip all of this. That makes sense, logically.

What the EU does: It appears to use the Y register for the check, but loads A with #$14 by default. Y register is loaded with the value stored at RAM $1E plus X register. The next thing we do is compare register Y with #$14 (or Cheep-Cheep frenzy enemy) and any other enemy which is NOT Cheep-Cheep frenzy will thus branch and we clear the Z flag. If it is the Cheep-Cheep, d6 will be set and thus skip the branch. If it is Cheep, then we now load A with #$07 rather than the previously loaded #$14. (So A will either be loaded with #$14 or #$07 depending on the current enemy plus X register and whether we previously branched or not we will end up with at the "adc Player_Y_Position") Next, we are at "adc Player_Y_Position", instead of A being loaded with this value we ADC with it instead, but Carry will be cleared here so we need not CLC. Neither will Carry be set with what's at RAM $CE. From there we move onto the linear code with the CMP,X. If the player's Y is at all above (being less than) the enemy's, we thus branch to the enemy being stomped "EnemyStomped" (and the timer flag at RAM $0791 will be set to branch; from there it checks for certain enemies such Spinys or other 'untouchable' enemies and branches accordingly to injure player or else award player certain pts.).

Okay, here is my more in-depth explanation. (Lakitu is my guinea pig in this example, since it will set the MSB thereby not cause the code to branch in ChkInj.)

Picture 1: For this to work, you must be no further from Lakitu than this.


Picture 2: This demonstrates when the code is in action in the NTSC version. See how Mario is not exactly hitting Lakitu's head from the top, though collision is counted as Lakitu being stomped from the 800 pts. above? That's the CMP Enemy_Y_Position,x in action there.


Picture 3: This is what happens when I NOP those nine bytes to see what happens. The player is injured instead of Lakitu being damaged. You can see Mario's swim animation, there. When the player takes damage and shrinks, there are actually two swim animations used in the process.



I used savestates to test this to make sure everything is exactly the same to verify. Look at the timer and Spiny's on the ground. Everything is exact between photo 2 and 3.

The EU version changes it to include all enemies because there are instances where, for example, Goombas are going down stairs while the player is jumping up and Mario/Luigi wrongfully takes damage. It singles out Cheep-Cheeps because they can (and will) jump at a diagonal angle, which causes damage to the player if A still contained #$14 at the time. It was specifically changed to load #$07 with flying Cheep-Cheeps to fix that. (I tried changing the #$07 to a #$14 in the EU version to verify that I was correct.)

Alright. I was able to add the code into SMB2J by optimizing the routine "KillPlayer" to make room. Yay.



____________________
At the end, when the day is over, the only one left to face is yourself. Have you been true to yourself and made the most of your day?

Main - ROM Hacking - SMB1 Disassembled EU Difference New thread | New reply

Acmlmboard 2.1+4δ (2023-01-15)
© 2005-2023 Acmlm, blackhole89, Xkeeper et al.

Page rendered in 0.018 seconds. (349KB of memory used)
MySQL - queries: 37, rows: 58/58, time: 0.014 seconds.