• Welcome to Smashboards, the world's largest Super Smash Brothers community! Over 250,000 Smash Bros. fans from around the world have come to discuss these great games in over 19 million posts!

    You are currently viewing our boards as a visitor. Click here to sign up right now and start on your path in the Smash community!

Completed Luigi's Momentum Affects Fireball Speed

UnclePunch

Smash Ace
Joined
Nov 9, 2014
Messages
673
Updated so now it works with all players, thanks for the advice Achilles!
Code:
$Luigi's Fireball is influenced by his momentum
C22C02A0 00000006
C0240000 39F80128
C10F0000 820F0000
39EFFF64 81EF0000
2C0F0000 4180000C
FC21402A 48000008
FC214028 00000000
Video: https://streamable.com/9zr2

So I started coding this idea and I have a working "prototype".

$Luigi's Fireball is influenced by his momentum
C22C02A0 00000008
C0240000 3DE08045
61EF3130 81EF0000
39EF0128 C10F0000
820F0000 39EFFF64
81EF0000 2C0F0000
4180000C FC21402A
48000008 FC214028
60000000 00000000

It only reads P1's momentum, mainly because I don't know how to check which player used the move mid function. One of my first gecko codes, any advice is appreciated!

Code:
lfs         f1, 0 (r4)              #loads current fireball speed into f1
lis         r15,0x8045           #loads first half of p1 data pointer into r15
ori         r15,r15,0x3130     #loads second half of p1 data pointer into r15
lwz       r15, 0 (r15)           #loads X momentum value address into r15
addi      r15, r15,0x128      #loads momemtum value address into r15
lfs         f8, 0 (r15)            #loads momemtum value into f8
lwz       r16, 0 (r15)           #loads momemtum value into r15
subi      r15, r15,0xb98c    #loads face-direction address into r15
lwz       r15, 0 (r15)           #loads face-direction value into r15
cmpwi   r15, 0                  #checks facing direction
blt         0xE                    #branch if facing left
fadd      f1, f1, f8              #add momemtum to fireball speed
b          0x8                     #branch to end
fsub      f1, f1, f8              #subtract momentum from fireball speed

Video: https://streamable.com/9zr2
 
Last edited:

Achilles1515

Smash Master
Joined
Jun 18, 2007
Messages
3,211
Location
Cincinnati / Columbus OH
Nice work on your first code. I know it feels great to have something finally working.


Couple things:

Your injection point is in the middle of function 802c027c. This function is involved with creating Luigi's fireball or whatever, but it is important to note the function input paramater, which is the character's external data pointer (in r3), also labeled the “player entity struct” in the SSBMO sheet.


Rules of Thumb:

Function input parameters that are not floats start at r3 (e.g. pointers or ints). So if the function takes two parameters of those types, they will come into the function in r3 and r4 (next would be in r5, r6, etc.). Float inputs to functions start at f1.

In the function you injected into, you know it only takes a single input parameter because r4,r5,.. and f1 are being loaded into almost immediately. So it didn't care what was previously in them. r3 however, is loaded from immediately..so it cared what it was input as.

TONS of on-screen character functions have the external data pointer as the r3 input. External data pointer is the pointer you loaded at 80453130 (P1's main char). If you want to do something with this character's variable data, you need to then load the internal character offset, as I call it, or "player character data" offset from the SSBMO sheet. From the external data pointer, the internal data pointer is located at +0x2C. You can see the game doing this at 802C0290, and putting the internal offset in r5.

Note how line 802C0298 is loading internal offset +0x2C in f0. Looking at the SSBMO sheet, it is loading the facing direction float of this character, and placing it into f0. In the next assembly line, it is multiplying the facing direction by the fireball x-velocity magnitude.

Long story, you are doing way more work than you need to because the majority of what you want is already available in registers.


Change your injection address to 802C02A8.

r5 = internal offset of character (so you don't even need the player port ID because r5 already contains the offset for the character shooting a fireball)

f0 = current fireball x-velocity, already multiplied by facing direction

- a couple lines down from here, f1 is being loaded into. So you know it is free for use at this point (so load the character x-velocity value into f1)

- load character variable values (SSBMO sheet) from r5, then apply changes to f0, then you're done


CODE_START:

# code to modify f0 (fireball speed)

CODE_END:
stfs f0,0x40(r5) # default code line


-------------


Other thoughts:


ALWAYS branch to labels. It is bad practice to code absolute branches, ex. "b 0x10, or beq- 0x14", because if you ever want to go back to your code and edit/add to parts of it, that branch byte value may need to change to jump where you desire. But if you don't update the branch byte value, ASM-->WiiRD doesn't care. It will branch ahead x amount of bytes and put you in the wrong location. Whereas, if you instructed it to branch to a label, it will automatically account for any edits to your code and bring your branch value to the right location.


cmpwi r5,0
beq- SKIP
SUCCESS:

# can safely/freely write in here and not worry about the above branch byte value

SKIP:
 

UnclePunch

Smash Ace
Joined
Nov 9, 2014
Messages
673
Nice work on your first code. I know it feels great to have something finally working.


Couple things:

Your injection point is in the middle of function 802c027c. This function is involved with creating Luigi's fireball or whatever, but it is important to note the function input paramater, which is the character's external data pointer (in r3), also labeled the “player entity struct” in the SSBMO sheet.


Rules of Thumb:

Function input parameters that are not floats start at r3 (e.g. pointers or ints). So if the function takes two parameters of those types, they will come into the function in r3 and r4 (next would be in r5, r6, etc.). Float inputs to functions start at f1.

In the function you injected into, you know it only takes a single input parameter because r4,r5,.. and f1 are being loaded into almost immediately. So it didn't care what was previously in them. r3 however, is loaded from immediately..so it cared what it was input as.

TONS of on-screen character functions have the external data pointer as the r3 input. External data pointer is the pointer you loaded at 80453130 (P1's main char). If you want to do something with this character's variable data, you need to then load the internal character offset, as I call it, or "player character data" offset from the SSBMO sheet. From the external data pointer, the internal data pointer is located at +0x2C. You can see the game doing this at 802C0290, and putting the internal offset in r5.

Note how line 802C0298 is loading internal offset +0x2C in f0. Looking at the SSBMO sheet, it is loading the facing direction float of this character, and placing it into f0. In the next assembly line, it is multiplying the facing direction by the fireball x-velocity magnitude.

Long story, you are doing way more work than you need to because the majority of what you want is already available in registers.


Change your injection address to 802C02A8.

r5 = internal offset of character (so you don't even need the player port ID because r5 already contains the offset for the character shooting a fireball)

f0 = current fireball x-velocity, already multiplied by facing direction

- a couple lines down from here, f1 is being loaded into. So you know it is free for use at this point (so load the character x-velocity value into f1)

- load character variable values (SSBMO sheet) from r5, then apply changes to f0, then you're done


CODE_START:

# code to modify f0 (fireball speed)

CODE_END:
stfs f0,0x40(r5) # default code line


-------------


Other thoughts:


ALWAYS branch to labels. It is bad practice to code absolute branches, ex. "b 0x10, or beq- 0x14", because if you ever want to go back to your code and edit/add to parts of it, that branch byte value may need to change to jump where you desire. But if you don't update the branch byte value, ASM-->WiiRD doesn't care. It will branch ahead x amount of bytes and put you in the wrong location. Whereas, if you instructed it to branch to a label, it will automatically account for any edits to your code and bring your branch value to the right location.


cmpwi r5,0
beq- SKIP
SUCCESS:

# can safely/freely write in here and not worry about the above branch byte value

SKIP:
This is awesome. I had no idea the code was doing all this already lol, I gotta look into the surrounding code from now on. Also I had no idea about labels. It kept branching to the wrong line whenever I had to modify the code like you said. Thanks for the reply, this definitely helps!
 

Punkline

Dr. Frankenstack
Joined
May 15, 2015
Messages
423
One of my first gecko codes, any advice is appreciated!
I’m really happy to see this! Great idea, especially for a first code~

Achilles had some good advice for you, so I’ll just follow up by saying that writing simple, efficient code is usually a game of context and making the most of your observations. Even after thinking up how to write a code in your head, you’ll usually end up hunting for the most appropriate place to inject it because in many cases you’ll be able to get the vanilla functions to do some (or all) of the heavy lifting for you.

One of the most valuable things I can remember learning about when I was new to writing PPC was how functions use the stack. This, combined with a solid understanding of the difference between “saved” and “volatile” registers will help immensely in determining what the context of any given point in your host function is like (as well as help determine what registers are definitively safe to use.)

In a nutshell, the beginning and ends of functions that need to make room for registers and local variables will contain instructions that use and modify the “sp” register, (which is a special name for r1.) If you ever see this at the beginning and end of a function--know that it is often used to back up and restore specified registers in the r14-r31 range in order to make room for new values without deleting the data already contained inside them.

You don’t really need to know how to use the stack for writing simple codes, but knowing how the game uses it will allow you to learn more about the context of the functions you’re writing injections for.

---

I think the best way to learn more about this stuff is probably to just observe functions in the game that you want to understand. By consulting valuable resources like the Wiibrew wiki, the Gekko User Manual, the Programmer’s Reference Guide, and the Compiler Writer’s Guide--many things can be learned by observing examples and tested through experimentation.
 
Top Bottom