Kuribo64
Views: 19,856,654 Home | Forums | Uploader | Wiki | Object databases | IRC
Rules/FAQ | Memberlist | Calendar | Stats | Online users | Last posts | Search
03-29-24 01:00 PM
Guest:

0 users reading [ASM Resource] Customising behaviour of player/objects/levels | 1 bot

Main - General SM64DS hacking - [ASM Resource] Customising behaviour of player/objects/levels Hide post layouts | New reply


dy
Posted on 04-14-18 02:23 PM (rev. 24 of 05-08-18 01:08 AM) Link | #94061
Introduction

Firstly, huge thanks to the team behind SM64DSe and the ASM Patch Templates, as well as everyone who's contributed to documenting the object parameters, memory addresses and subroutines. What I have found and documented here wouldn't be possible if it wasn't for the amazing quality of research, documentation and tools available to the community. Special thanks also to Fiachra who helped me get started and inspired me to learn ASM hacking.

This thread is intended to collate discoveries on how to customise certain player, object and level properties/behaviours (in ways that can't be changed using SM64DSe alone). It is not a substitute for the existing ASM resources, which are invaluable if you want to create ASM hacks or do your own research.

If you've made any discoveries of your own which fit with this thread, please feel welcome and encouraged to share!


Getting Started with ASM

In order to understand and use the information provided, you will need some basic ARM ASM knowledge. This is not intended to be a general ASM help thread, but don't worry, there are plenty of tutorials available online. I personally have very little programming experience and knew zero ASM just 2 weeks ago, but learnt a lot from this tutorial (30 minute read) and playing around with the no$gba debugger (see below).

Useful programs:
  1. ASM Patch Templates and associated programs
    Allows you to change or create custom code for the game. The most updated templates are included with the latest SM64DSe, but you will still need the associated programs in the above link. The header files and symbols.x included with the templates contain a lot of useful memory addresses and offsets for researching and writing your own code (there are 2 "versions" of the template docs - v2 by mibts has a lot more features, but v1 also has some helpful stuff and is probably more beginner-friendly).

  2. Modified version of NSMBe
    Optional - allows you to directly overwrite existing code rather than jumping to new code (useful for changing data tables and pointers).

  3. no$gba debugger
    Allows you to change and test code live in-game, or to set breakpoints and conduct your own research. Make sure you get the debug version.

Additional tips:
  • When writing hooks with the ASM Patch Template, you need to specify the overlayID in hexadecimal (whereas the overlay numbers documented below are in decimal).
    Eg, to use the 'nsub' hook for something located in overlay 98 (0x62) you would write "nsub_02xxxxxx_ov_62".

  • In ARM ASM, ldrb/strb means load/store byte (1 byte), and ldrh/strh means load/store halfword (2 bytes). Default is 4 bytes, without the b/h suffix.

  • In no$gba, you can change data values by going to the memory location and changing the instruction to "dcd 0xXXXXXXXX" (where the X's are the data you want).
    Don't use "dcd" when compiling code for the ASM Patch Templates though - use ".word", ".hword", or ".byte" instead.


Documented Discoveries

OK, now onto the good stuff! This post will be updated as new discoveries are made.

    Yoshi: can punch and grab

    Hold "Y" (sprint) while attacking to punch/grab/dive: this post


    Player: speed and jump height

    See this post


    Wing Cap: who can get Feather powerup

    There are several parts to this:
    1. When a Feather exists, the game constantly checks whether the player is Mario and immediately despawns the Feather if not (eg, if player loses their Mario disguise)
    2. When the Feather is collected, the game again checks if the player is Mario (if not, the Feather disappears without giving any powerup)
    3. To spawn a Feather, you can either use the "! Block VS" object or change who "? Blocks" give Feathers to (for "? Blocks" that normally only give Feathers to Mario).

    Despawn loop check: here
    Whether to give wings when collected: here
    Whether relevant "? Blocks" spawn Feathers or Power Flowers: here


    Power Flowers: who can see Power Flowers in mirror room

    Unfortunately crashes when collected by Mario/Wario in Castle 2F, and there is no reflection for Yoshi's fire breath. But it seems to work properly in painting levels. Including this in case anyone wants to use static Power Flowers in custom/painting levels only.

    Visibility of static Power Flowers: here


    Brick Blocks / Black Brick Blocks: who can break

    When ground pounded: here
    When kicked or sweep kicked: here
    When punched: here
    On explosion: here


    Ice Blocks: can be shattered

    See this post


    Boos: who can defeat

    On any attack that can normally defeat Boos: here


    Bob-ombs: whether to reappear and what to spawn when defeated

    Whether to reappear: here
    What to spawn: here


    Piranha Plants: whether to reappear and what to spawn

    When defeated: here


    Wooden Crates: whether to reappear and what to spawn

    When broken: here
    When swallowed by Yoshi: here


    Yellow Coins / Blue Coins: whether they disappear

    Countdown timer: here


    Object Draw Distances

    Easy way to change for all or specific objects: here

    Something easy: you could set a fixed draw distance for everything (r2 = 0x600000 seems to give good results without killing the frame rate)
    Something fancy: offset 0xC (2 bytes) of the object's address (r4) is the ActorID of the object - so you could check this to set different draw distances for different objects


    Bowser Star Doors / Endless Stairs: star and character requirements

    Bowser Star Doors: here
    Endless Stairs - music/lighting effects: here
    Endless Stairs - whether can get to top: here

    Change when you get "recovered X stars" messages: here


    Power Stars: whether to continue or exit level after collecting

    100-Coin Stars: here
    Level type: here


    Level and level transition behaviours

    See this post

    Allows you to do things like:
    • create mega levels with more than 8 Power Stars
    • set the StarID for castle/boss maps - so these can have different configurations after an event of your choosing has occured
    • 100-coin stars in secret/boss levels


Conducting Your Own Research

For those who are interested in doing their own research and wondering how I discovered these, the process essentially involves:
  1. Finding a suitable starting breakpoint, eg Actor::Spawn() function (which is called any time something is spawned, such as a coin).

  2. The ASM Hacking Templates (especially symbols.x and SM64DS_2.h from v2) are fantastic resources of currently known data locations, subroutines and their memory addresses. Action Replay codes can also be very helpful, if there is one that does something similar to what you want. Lastly, you can find addresses of object-specific callbacks (eg, OnGroundPounded(), OnKicked(), etc) by following steps 1-8 of Fiachra's tutorial for finding an object's constructor - after the constructor address, you'll see a long list of more addresses (which are the addresses of the various callback functions).

  3. Either tracing backwards through from the breakpoint to figure out where and how the game determines whether to go to that piece of code, or tracing forwards from the breakpoint to find when the game checks for something (eg, type of character).

  4. You'll need some ASM knowledge and a bit of creativity/patience. Tracing backwards involves looking at r14 (LR) to find earlier code that called your breakpointed code (and repeat if necessary). Unfortunately sometimes code uses "b" jumps rather than "bl" jumps (which don't store the return address to r14), so you might end up further back then you wanted and then have to step forwards. For tracing forwards, you can use "F7" in no$gba to step through code line by line (the debugger helpfully tells you the value of registers and whether conditional instructions evaluate to 'true' or 'false' as you step through). You can also use no$gba to change code on the fly to see if it changes what you want to change. Chaining breakpoints and conditional breakpoints can be very helpful (see this post for an example).

See here for how to set different types of conditional breakpoints (thanks to Fiachra for the link). You can also create breakpoints which are triggered whenever a memory address is read or written to (this can be really handy) - the previous documentationis a bit off for these though, so see here instead.

Lastly, once you've found what you are looking for, follow steps 3-5 in this post to find which overlay it appears in.

____________________

- ASM resource for customising behaviour of player/objects/levels
- NSMBe with direct overwrite feature

StarPants
Posted on 04-14-18 04:50 PM (rev. 3 of 04-14-18 09:04 PM) Link | #94063
This is extremely awesome. Keep up the great work!

dy
Posted on 04-21-18 01:41 AM (rev. 15 of 04-26-18 02:59 PM) Link | #94134
Holy crap!!!

Did you know Yoshi has fully functional punch/kick/dive/grab attacks and animations!?

Here's how...

    Yoshi: can punch and grab

    How the game decides whether to perform a 'punch' or 'eat' attack: here

    Here's a quick patch for ASMPatchTemplate which allows Yoshi to use both his new and old attacks (hold "Y" while attacking to punch/dive/grab, etc):

      YoshiCanPunchAndEat.s

      nsub_020DDEAC_ov_02:

      @ check if character is Yoshi
      ldr r0, [r4, #0x8]
      cmp r0, #0x3
      bne 0x020DDEDC

      @ approach used by the game to get "INPUT_PERSISTENT"
      ldr r1, =0x020A0E40
      mov r0, #0x18
      ldrb r2, [r1]
      ldr r1, =0x0209F49C
      mul r0, r2, r0
      ldrh r0, [r1, r0]

      @ choose attack depending on whether "Y" is pressed
      ands r0, r0, #0x800
      beq 0x020DDEB8
      b 0x020DDEDC

    To switch to hold "Y" for eat attack, just change the "beq" to "bne" (but the current setup feels more natural for dive attacks while sprinting).


PS: I've also discovered how to change the following:
  1. Whether a Power Star sends you back to the castle or lets you continue
  2. Whether or not the course star select / load screen is displayed
  3. Which StarID to enter the level as, without the star select screen
  4. Which type of pause screen and congratulations text used for different levels
These should make it easier to create levels without being as constrained by the ActID types that the default game uses (eg, you could create main levels without a star select screen so that the player doesn't know how many stars there are, like with the secret/boss levels). Being able to set the StarID without the level select screen also makes it possible to have castle/boss maps change after an event has occurred (without hacking individual objects).

I'll post these here once I've documented them properly (soon)!

____________________

- ASM resource for customising behaviour of player/objects/levels
- NSMBe with direct overwrite feature

StarPants
Posted on 04-21-18 06:09 AM Link | #94135
Nice! How did you discover Yoshi's secret abilities in the first place?

dy
Posted on 04-21-18 07:49 AM (rev. 4 of 04-21-18 12:54 PM) Link | #94136
Posted by StarPants
Nice! How did you discover Yoshi's secret abilities in the first place?

Similar process to all the others - this one was actually surprisingly straightforward:
  1. I set a read breakpoint on INPUT_1_FRAME to see when the game checks for "A" keypress (note that Yoshi doesn't keep sticking his tongue out if you hold "A" so guessed it was checking INPUT_1_FRAME rather than INPUT_PERSISTENT)
  2. Got the value to look for through DeSmuME RAM watch, then cycled through the breakpoints until I found it check for the "A" key (luckily there weren't many and didn't have to look very hard)
  3. While still breakpointed at the "A" key check, I set a read breakpoint for the acting character type (Player object address + 0x8) to see when ths game next checks who you are playing as
That took me straight to the spot in the image I posted. It branches off if the player is not Yoshi, so I changed it to an unconditional branch to see what would happen. Fully expected it to do nothing or crash due to lack of animations, etc, but can you believe it... IT WORKED!!!


EDIT: Fixed code for YoshiCanPunchAndEat - I accidentally stuffed up the Yoshi check, so it was affecting every character!

____________________

- ASM resource for customising behaviour of player/objects/levels
- NSMBe with direct overwrite feature

StarPants
Posted on 04-21-18 01:22 PM Link | #94138
It is very surprising that it doesn't crash. Maybe Yoshi was intended to be able to punch but this ability was removed in the last minute.

dy
Posted on 04-21-18 04:21 PM (rev. 4 of 05-08-18 01:10 AM) Link | #94139
EDIT: I think you might be right. His punch seems to be set to be much weaker than any other character's (Goombas don't go very far at all), but he can run quite fast while carrying things.

It's a bit of a mixed bag in terms of consistency though. He can throw Big Bob-omb pretty far, but can't seem to throw crates as far as Luigi. Also if you bring lost little penguin to Mama Penguin with Yoshi she won't recognise it and just give the "have you seen" speech... Will have to figure out how to fix all that.

____________________

- ASM resource for customising behaviour of player/objects/levels
- NSMBe with direct overwrite feature

dy
Posted on 04-22-18 02:58 PM (rev. 7 of 04-26-18 02:15 PM) Link | #94149
As promised, here are some discoveries which allow you to customise the behaviour of level transitions and Power Stars.

Might not sound very exciting at first, but it allows you to do things such as:
  • create mega levels (with persistence across several ActIDs) - so you can have more than 8 Power Stars in that "level"
  • set the StarID for castle/boss maps - so you can check if something has happened, then load the map with different objects/warps afterwards
  • choose the behaviour of Power Stars, etc without being constrained by the ActID type
  • have more levels within levels (like Luigi's painting in Boo's Haunt)

    • Power Stars: whether to continue or exit level after collecting

      100-Coin Stars: here
      Level type: here


      Level: whether to display star select screen

      Level type, moving between levels: here
      See below on how to set the star number without the star select screen


      Level: set star number for level

      See here
      Allows you to create multiple setups for the secret/boss/castle levels, or force the star number if you don't want courses to have star select screens


      Level: persistence across levels

      Coin count and object respawns: here
      Note: some objects are not added to the death table, so will respawn when moving between sublevels (eg, Brick Blocks in Cool Cool Mountain after you return from the slide)


      Level: attainability of 100-Coin Stars

      Level type and coin requirements: here
      (Thanks to whoever created the Action Replay code for the offset)


      Whether and when to display "recovered X stars" text

      See here


      Pause Menu: whether can "Exit Course"

      See here


      Pause Menu: make Main Castle use course/level pause screen

      Change the instruction at each of the following addresses from cmp r0, 0x1D" (castle ActID) to "cmp r0, 0xFE" (or another unused ActID):

        0x0202DC74 - loads background for course/level pause screen (when entering castle level)
        0x02029550 - shows text for course/level pause screen
        0x020252B4 - don't show star map on top screen
        0x020280C8 - fix glitch when entering Options screen
        0x02028B74 - fix glitch when leaving Options screen
        0x02027FD4 - fix glitch when leaving Controller Modes screen


      Pause Menu / Star Text: make main courses like secret/boss levels

      To make pause menu only show the course name (like with secret/boss levels), and make the text when you leave with a Power Star like that for secret or boss levels:

        0x0201D860:
        - set r6 to either 0x12 or 0x15 instead (same effect for pause menu, for exit text 0x12 = "[course name] COMPLETE" and 0x15 = "another castle secret star CLEAR")
        - store r0 (the level's real ActID) to a global variable

        0x0201DA6C:
        - get the stored (real) ActID and add to r0 instead (this makes it display the correct course name)
        - leave the value of r6 untouched (ie, still 0x12 or 0x15)

        0x02025680:
        - make the jump unconditional (removes "score" from pause screen)

        0x02023E54:
        - conditional branch - if true, displays "Congratulations" bubble text on exit (like after boss fight)

        0x02024568:
        - jump to 0x0202457C (saves highscore but doesn't display "Highscore" bubble text)


    ____________________

    - ASM resource for customising behaviour of player/objects/levels
    - NSMBe with direct overwrite feature

    dy
    Posted on 04-26-18 02:12 PM (rev. 2 of 04-26-18 02:56 PM) Link | #94190
    Added above some ways to change the type of pause screen and text you get when exiting a level with a star in the above post. I've also realised that Black Brick Blocks share the same vtable and callbacks as orange Brick Blocks - the onAttacked callbacks checks the ActorID to determine what type of brick block it is and therefore whether it should break. The first post has been updated to reflect this.

    Also, I've discovered that you can make Ice Blocks breakable by making the various onAttacked callbacks point to the corresponding callbacks for Brick Blocks instead (they normally point to dummy functions, so aren't breakable). Correct particle effects and everything!

      Ice Blocks: breakability when attacked

      To make them breakable: here
      To change which characters can break them, see "Brick Blocks / Black Brick Blocks: who can break" in the first post

    For replacing the vtable pointers, you can use my modified version of NSMBe (which allows you to directly overwrite code using the same syntax as when creating standard hooks).

    ____________________

    - ASM resource for customising behaviour of player/objects/levels
    - NSMBe with direct overwrite feature

    Hiccup
    Posted on 05-01-18 11:19 AM Link | #94216
    The reason the ice block breaking works with the shards may be because you technically can break them if you hack Giant Mario into the level and walk into one.

    dy
    Posted on 05-01-18 12:49 PM (rev. 2 of 05-01-18 12:49 PM) Link | #94218
    Posted by Hiccup
    The reason the ice block breaking works with the shards may be because you technically can break them if you hack Giant Mario into the level and walk into one.

    Yep, you're probably right. If you look at all the onKicked(), onPunched(), onGroundPounded(), etc functions for the Brick Block, you'll see that basically all they're doing is returning a pointer in r1 if the object should be broken. It gets that pointer by getting the address of the vtable for that object (the first four bytes at the object's address), adding 0x7C to that address, then getting the value from there. Looking at the commented out vtable structure in SM64DS_2.h, offset 0x7C would be the 31st function (InitResources being the 0th), which is the Kill() function.

    So looks like all it does is check whether the object should break, and if so, return the address of the object's Kill() function in r1. So yeah, they must have programmed IceBlock::Kill() to break with shards for if hit by Mega Mario.

    ____________________

    - ASM resource for customising behaviour of player/objects/levels
    - NSMBe with direct overwrite feature

    FZone96
    Posted on 05-05-18 11:34 AM Link | #94243
    Is it possible to make a character run faster than Mario using this?

    StarPants
    Posted on 05-06-18 11:51 AM (rev. 2 of 05-06-18 01:05 PM) Link | #94245
    The check for max speed is somewhere around 020BF330. The instruction in that address is mov r0,r0,lsr 0Ch. If you want to double the max speed, change that instruction to mov r0,r0,lsr 0Bh and if you want to multiply it by four, change the instruction to mov r0,r0,lsr 0Ah and so on. This will affect all characters afaik. Please note that the acceleration will be the same, so it takes more time to reach the max speed.

    Edit: This isn't the ideal way of doing this and it may have side effects. Maybe you should find the actual comparison of the max speed and current horzSpeed.

    dy
    Posted on 05-06-18 02:22 PM (rev. 5 of 05-08-18 12:07 AM) Link | #94246
    Thanks StarPants - good to see others doing ASM research as well!

    There is a 4 halfword data table which sets out the speed multiplier for each character, which affects both walk and run speeds (I found it earlier today but had been planning to look into how it interacts with carry speed and swim speed as well before I posted). Unfortunately not in front of my PC now, but will try post tomorrow (just a heads up so other people don't spend time unnecessarily trying to find it as well - this one was tricky).


    UPDATE

    Heh, StarPants - you actually got really close to finding the data table for movement speed. If you look at 0x020BF30C (just a few lines above the address you found), you'll see it gets the player charID, then loads r2 with a value from 0x020FF170 (plus an offset depending on the charID). The function at 0x020BF30C gets called a lot, with a different multiplier in r1 depending on whether it's determining the speed for running, walking, crawling, etc. This isn't character dependent, so if a character has a faster walking speed, it will have a faster running speed.

    Anyway, without further ado...

      Player: move speed and acceleration

      See here (including ceiling hang and slippery slide)

      Despite what many game guides say, all characters have the exact same acceleration and deceleration (and no, Luigi does not have a faster top speed than Mario).
      Also, thanks to Skelux's "Increase Acceleration" additional patch for the memory address re ground acceleration.


      Player: carry speed

      Regular and heavy objects: here


      Player: swim speed

      See here


      Player: jump power

      See here (including back flip, side flip and long jump)

    I've also figured out how to change the player's attack knockback power and throwing power, but these are all hardcoded differently for different objects - will upload these once I've finished looking into the different objects.

    ____________________

    - ASM resource for customising behaviour of player/objects/levels
    - NSMBe with direct overwrite feature

    StarPants
    Posted on 05-07-18 02:17 PM Link | #94248
    I saw there was something going on in the registers but couldn't analyse it any further. I'm glad you got it.

    unrelatedly: If anyone's doing a kaizo they can try to nop at 0x020BF330. I tried in first person and it felt pretty much like a motorbike without breaks. Never rode one irl though.

    dy
    Posted on 05-08-18 12:06 AM (rev. 2 of 05-08-18 12:09 AM) Link | #94255
    Posted by StarPants
    I saw there was something going on in the registers but couldn't analyse it any further. I'm glad you got it.

    unrelatedly: If anyone's doing a kaizo they can try to nop at 0x020BF330. I tried in first person and it felt pretty much like a motorbike without breaks. Never rode one irl though.

    Oops, I meant a few lines above at 0x20BF30C (I'll fix my above post). Took me a while to work out too - I find that using F7 in no$gba debugger to step through line by line and look at what's happening to the registers after each line, can help make sense of things when I'm struggling.

    Essentially this is what it's doing:
      ldr r2, [r0, 0x8] // r2 = 4 bytes at address (r0 + 0x8) = charID (0:M, 1:L, 2:W, 3:Y)
      ldr r0, =0x020FF170 // r0 = 0x020FF170
      and r2, r2, 0xFF // r2 = r2 bitwise AND 0xFF (seems unnecessary?)
      mov r2, r2, lsl 0x1 // r2 *= r2 (left shift 1 bit is effectively x2) = charID x2
      ldrh r2, [r0, r2] // r2 = 2 bytes at address (0x020FF170 + charID x2)

    After that it gets to some mathsy stuff which I didn't really follow either. But yeah, nop'ing at 0x020BF330 stops it dividing the top speed (by what, something like 2^12?) but without changing acceleration/deceleration. So yeah... (It looks even funnier in 3rd person btw, because it speeds up some of the animations as well.)

    Also FYI - you can change data values in no$gba debugger by changing the instruction to "dcd 0xXXXXXXXX" (took me a while to work out), so you can test things like changing the speed table values on the fly. Don't use "dcd" with ASMPatchTemplate / devkitARM though - use ".word", ".hword", or ".byte" instead.

    ____________________

    - ASM resource for customising behaviour of player/objects/levels
    - NSMBe with direct overwrite feature

    StarPants
    Posted on 05-26-18 07:34 PM Link | #94461
    Collecting a cap changes the real character:

    [image]

    Not very cleanly though


    Main - General SM64DS hacking - [ASM Resource] Customising behaviour of player/objects/levels Hide post layouts | New reply

    Page rendered in 0.129 seconds. (2048KB of memory used)
    MySQL - queries: 29, rows: 229/229, time: 0.018 seconds.
    [powered by Acmlm] Acmlmboard 2.064 (2018-07-20)
    © 2005-2008 Acmlm, Xkeeper, blackhole89 et al.