| |||
Views: 88,275,999 |
Main | FAQ | Uploader | IRC chat | Radio | Memberlist | Active users | Latest posts | Calendar | Stats | Online users | Search | 03-19-24 01:14 PM |
|
Guest: Register | Login |
0 users currently in ROM Hacking | 3 guests | 1 bot |
Main - ROM Hacking - SMB1 Disassembled EU Difference | New thread | New reply |
ShaneM |
| ||
Snifit Compromised account. Please contact an admin to restore access. Level: 36 Posts: 263/285 EXP: 291979 Next: 16131 Since: 02-17-14 Last post: 3099 days Last view: 2615 days |
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 |
© 2005-2023 Acmlm, blackhole89, Xkeeper et al. |
MySQL - queries: 37, rows: 58/58, time: 0.014 seconds. |