Announcement

Collapse
No announcement yet.

(Request) River City Ransom NES

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • (Request) River City Ransom NES

    Game genie format invincibility/untouchable code that has NO effect on enemies? (The ones that we already have either make it so you have to throw enemies to finish them (which makes some bosses unbeatable), or makes them invincible as well as also be able to pick items/coins up?

    Thanks!

  • #2
    Invulnerability/Untouchable (Buggy in 2 player mode)
    LPKEOKZO

    When enemies attack you, they hurt themselves. Objects bounce off of you, however you can still die by falling in a pit or water.

    The code seems to work fine in 1 player mode, for both Novice and Advanced settings. I tested it for about an hour, so more play testing is needed to ensure there are no major bugs.

    In 2 player mode, it seems that player 1 can hurt player 2, but player 2 cannot hurt player 1, he only hurts himself. Also, I encountered a bug where player 2 died but still had a health bar and wouldn't respawn on the screen.

    Test it out and let me know what you think! I'll keep testing it as well and let you know if I encounter any more bugs in 1 player mode.



    CODE DEVELOPMENT NOTES (for those interested):
    Spoiler Alert! Click to view...

    Well I gotta say, the developers of this game sure didn't make this one easy. I picked away at it for several hours until I finally had success. I wanted to post my research here in case anyone wants to a) learn or b) improve the code.

    I started off doing some RAM searches and determined that locations $4BF and $4C0 hold player 1 and player 2 health, respectively. Doing a code trace, it appears at first glance that there is a potential target for a code at $8CFB:

    Code:
    Player 1 getting hit:
    
     04:8CEF:A6 04     LDX $0004 = #$00
     04:8CF1:BD BF 04  LDA $04BF,X @ $04BF = #$3F     # Load player 1's health value @ $4BF
     04:8CF4:38        SEC
     04:8CF5:E5 07     SBC $0007 = #$01               # Subtract hit amount from health
     04:8CF7:B0 02     BCS $8CFB                      # If the player has more health, branch to $8CFB
     04:8CF9:A9 00     LDA #$00                       # Otherwise player is dead (health = 00)
    >04:8CFB:9D BF 04  STA $04BF,X @ $04BF = #$3E     # Store new health value
    At first glance, I would simply change $8CFB from STA to LDA, which would prevent the new health value from being written. Setting a breakpoint for $4C0 (P2 health), we can see that the same code is run, just with a different offset:

    Code:
    Player 2 getting hit:
    
     04:8CEF:A6 04     LDX $0004 = #$01
     04:8CF1:BD BF 04  LDA $04BF,X @ $04C0 = #$3F    # Load player 2's health value @ $4C0
     04:8CF4:38        SEC
     04:8CF5:E5 07     SBC $0007 = #$01              # Subtract hit amount from health
     04:8CF7:B0 02     BCS $8CFB                     # If the player has more health, branch to $8CFB
     04:8CF9:A9 00     LDA #$00                      # Otherwise player 2 is dead (health = 00)
    >04:8CFB:9D BF 04  STA $04BF,X @ $04C0 = #$3E    # Store new health value
    Now normally we'd have a good code with something like this:

    Code:
       Invincibility (Enemies also invincible however)
       SUNEUKSO
       8CFB:9D:BD  (change STA into LDA to prevent health value update)
    Oh, if only it were that easy. During testing we find that the enemies are also rendered invincible, which sort of breaks the game. Setting a breakpoint for $8CFB, we can see that the same code is run when an enemy gets hit:

    Code:
    Enemy getting hit:
    
     04:8CEF:A6 04     LDX $0004 = #$03
     04:8CF1:BD BF 04  LDA $04BF,X @ $04C2 = #$0E   # Load enemy slot #2's health @ $4C2
     04:8CF4:38        SEC
     04:8CF5:E5 07     SBC $0007 = #$04             # Subtract hit amount from health
     04:8CF7:B0 02     BCS $8CFB                    # If the enemy has more health, branch to $8CFB
     04:8CF9:A9 00     LDA #$00                     # Otherwise the enemy is dead (health = 00)
    >04:8CFB:9D BF 04  STA $04BF,X @ $04C2 = #$0A   # Store new health value
    So unfortunately, this is a common health altering routine, which applies to both enemies
    and players. This makes it far harder to alter without some serious disassembly. Unfortunately it throws a monkey wrench in our first plans.

    So instead of targeting the instruction at $8CFB, we have to figure out another way to prevent this code from doing its thing.

    I checked the stack at this point, and found $8C52 as the most recent return address. Therefore I was able to determine that the "hit point" code above is part of a routine that starts at $8C88, and we jump to that routine from $8C50.

    Code:
     04:8C48:A5 1A     LDA $001A = #$00     # Load target char index from $1A
     04:8C4A:85 04     STA $0004 = #$00     # Store target char index in $04
     04:8C4C:A5 1B     LDA $001B = #$02     # Load attacking char index from $1B
     04:8C4E:85 05     STA $0005 = #$02     # Store attacking char index in $05
    >04:8C50:20 88 8C  JSR $8C88            # Jump to "hit point" routine and conduct attack
    At this point I made some assumptions, because to me it looked like we are loading some kind of pointers before jumping to the "hit point" routine. I assumed it's a way to load "attacking character" and "target character" values, which the "hit point" routine would then take care of adjusting.

    I tried testing my theory by setting $8C49 to #$1B, so that the code loads the same value for attacking character as it does for target character, hoping this would cause an attacking enemy to essentially attack itself. Low and behold, it worked!

    Code:
       Invulnerability/Untouchable (Buggy in 2 player mode)
       LPKEOKZO
       8C49:1A:1B  (change pointer from $1A to $1B causing enemy to attack itself)
    I tested it in 1 player mode in an emulator for about an hour of play time and didn't encounter any major issues. I had a few guys freeze in the air while jumping, I'm not sure if that's a bug inherent in the game or introduced by the code, as I hadn't played this game much (but definitely will be in the future!).

    I went down a number of seemingly promising dead ends while working on this code. I've included further research here if anyone wants to dig into this further. There appear to be several tables that can probably be manipulated somehow to further enhance this code, or make a true Invincibility code that doesn't cause the enemies to hurt themselves.

    There is also another code listed within that was a half-working attempt at Invincibility, however it breaks Advanced mode.

    MORE DEVELOPMENT NOTES AND OTHER CRAP:
    Spoiler Alert! Click to view...


    Interesting breakpoints:
    $8CF1/$8CFB - read/write char health points
    $0007 - register for number of HP to deduct (used for MANY other things as well)
    $8020 - early on routine related to HP management
    $8C40 - another early on routine related to HP management

    Routine reading from a table in ROM:
    Code:
    04:8AA7:BD 37 8B  LDA $8B37,X @ $8B37 = #$9F    # Reading from a table in ROM
    04:8AAA:85 2A     STA $002A = #$00
    04:8AAC:BD 38 8B  LDA $8B38,X @ $8B38 = #$04
    04:8AAF:85 2B     STA $002B = #$00
    04:8AB1:B1 2A     LDA ($2A),Y @ $0002 = #$03
    04:8AB3:85 2C     STA $002C = #$00
    04:8AB5:A9 00     LDA #$00
    04:8AB7:85 2D     STA $002D = #$00
    04:8AB9:BD 36 8B  LDA $8B36,X @ $8B36 = #$00
    04:8ABC:85 2A     STA $002A = #$00
    04:8ABE:B9 56 00  LDA $0056,Y @ $0058 = #$00
    04:8AC1:29 40     AND #$40
    04:8AC3:F0 08     BEQ $8ACD
    ...
    04:8ACD:A6 2A     LDX $002A = #$00
    04:8ACF:BD 69 8B  LDA $8B69,X @ $8B6F = #$00   # Reading from another table in ROM
    04:8AD2:85 2A     STA $002A = #$00
    04:8AD4:B9 5D 00  LDA $005D,Y @ $005F = #$A0
    Table at $8B36 (groups of 3 bytes?):
    Code:
    00 9F 04     # Regular enemies?
    09 9F 04 
    00 A3 04 
    09 A3 04 
    00 00 00 
    00 00 00 
    1B AB 04     # Bigger enemies?
    1B AB 04 
    12 A7 04 
    12 A7 04 
    00 00 00 
    00 00 00 
    00 00 00 
    24 9F 04     # Bosses?
    25 A3 04 
    26 A7 04 
    2F AB 04
    Table at $8B69 (some kind of HP deduction values?):
    Code:
    00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00
    00 00 01 02 02 03 03 03 01 02 02 03 03 04 05 04 
    05 03 05 04 01 01 02 02 02 02 02 02 02 02 02 08 
    08 08 08 08 08 08 08 08
    This is where $0007 is set and then used by the code at $8CF5:
    Code:
    04:8F42:A9 10     LDA #$10
    04:8F44:20 4D 8F  JSR $8F4D           # Determine number of HP to deduct?
    04:8F47:85 07     STA $0007 = #$02
    04:8F49:68        PLA
    04:8F4A:85 06     STA $0006 = #$00
    04:8F4C:60        RTS -----------------------------------------
    This routine is likely responsible for number of HP to deduct:
    Code:
    04:8F4D:A8        TAY
    04:8F4E:A5 04     LDA $0004 = #$10
    04:8F50:48        PHA
    04:8F51:A5 05     LDA $0005 = #$04
    04:8F53:48        PHA
    04:8F54:84 04     STY $0004 = #$10
    04:8F56:A5 2B     LDA $002B = #$00
    04:8F58:A0 08     LDY #$08
    04:8F5A:C5 04     CMP $0004 = #$10
    04:8F5C:90 02     BCC $8F60
    04:8F5E:E5 04     SBC $0004 = #$10
    04:8F60:2E 05 00  ROL $0005 = #$04
    04:8F63:06 2A     ASL $002A = #$BC
    04:8F65:2A        ROL
    04:8F66:88        DEY
    04:8F67:D0 F1     BNE $8F5A
    04:8F69:A6 05     LDX $0005 = #$04
    04:8F6B:68        PLA
    04:8F6C:85 05     STA $0005 = #$04
    04:8F6E:68        PLA
    04:8F6F:85 04     STA $0004 = #$10
    04:8F71:8A        TXA
    04:8F72:60        RTS -----------------------------------------
    Going back to the "hit point" routine, there is a jump to $8F02 before:
    Code:
    04:8CEC:20 02 8F  JSR $8F02
    04:8CEF:A6 04     LDX $0004 = #$00
    04:8CF1:BD BF 04  LDA $04BF,X @ $04BF = #$2F
    04:8CF4:38        SEC
    04:8CF5:E5 07     SBC $0007 = #$07
    04:8CF7:B0 02     BCS $8CFB
    04:8CF9:A9 00     LDA #$00
    04:8CFB:9D BF 04  STA $04BF,X @ $04BF = #$2F
    Checking jsr 8F02 before, there's more reading from ROM tables at $8F73 and $9043:
    Code:
    04:8F02:A5 06     LDA $0006 = #$04
    04:8F04:48        PHA
    04:8F05:A6 04     LDX $0004 = #$03
    04:8F07:BD B7 04  LDA $04B7,X @ $04BF = #$3F
    04:8F0A:AA        TAX
    04:8F0B:BD 73 8F  LDA $8F73,X @ $8F7B = #$04
    04:8F0E:AA        TAX
    04:8F0F:BD 43 90  LDA $9043,X @ $904B = #$17
    The $8F73 table is also referenced in another routine a bit earlier on, along with $8EE8:
    Code:
    04:8E29:B9 E8 8E  LDA $8EE8,Y @ $8EEB = #$10
    04:8E2C:9D 16 03  STA $0316,X @ $0319 = #$0C
    04:8E2F:A9 80     LDA #$80
    04:8E31:1D 5D 00  ORA $005D,X @ $0060 = #$00
    04:8E34:9D 5D 00  STA $005D,X @ $0060 = #$00
    04:8E37:4C 46 8E  JMP $8E46
    04:8E3A:A6 06     LDX $0006 = #$04
    04:8E3C:BD 73 8F  LDA $8F73,X @ $8F76 = #$03
    04:8E3F:AA        TAX
    04:8E40:BD 33 90  LDA $9033,X @ $9036 = #$00
    04:8E43:9D FA 03  STA $03FA,X @ $03FD = #$00
    04:8E46:60        RTS -----------------------------------------
    Table at 8EE8 - 8F01:
    Code:
    04 08 0C 10 14 18 1C 20 24 28 2C 30 34 38 3C 40 F4 F5 F6 F7 F8 F9 FA FB FD FE
    Table at 8F73 - 9042 (determines how many HP to deduct depending on char ID?)
    Code:
    01 
    02 02 
    03 03 03 
    04 04 04 04 
    05 05 05 05 05 
    06 06 06 06 06 06 
    07 07 07 07 07 07 07 
    08 08 08 08 08 08 08 08 
    09 09 09 09 09 09 09 09 09 
    0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 
    0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 
    0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 
    0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 
    0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 
    0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 
    10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 
    10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 
    10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 
    10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 
    10 10 10 10 10 10 10 10 00 00 00 00 00 00 00 00 
    00 00 00 00 00 00 00 00
    Table at 9043 - 9052:
    Code:
    9043: 2F 2C 29 26 23 20 1D 1A 17 14 11 0E 0B 08 05 02
    I actually had some luck creating a code that works, by setting 9049 to 00, causing this:
    Code:
    04:8F0F:BD 43 90  LDA $9043,X @ $9049 = #$1D
    to turn into this:
    Code:
    04:8F0F:BD 43 90  LDA $9043,X @ $9049 = #$00
    I'm not sure why, but this code seems to function as an Invincibility code. However, it only works in Novice mode. In Advanced mode, enemies cannot be killed. At this point I went back to the code at $8C48 and was able to make a different code as first described above.

    If you want to try the Novice only code, it's below. I didn't playtest it very much once I discovered it doesn't work in Advanced mode.

    Code:
       Invincibility (Use in Novice mode only)
       AAKOPEIP
       9049:1D:00  (change value in table to 00, preventing damage to player)

    "One man's garbage is another man-person's good un-garbage." -- Ricky from Trailer Park Boys

    Comment

    Working...
    X