Completed Custom Subaction Event - Character Data Modifications

Achilles1515

Moderator
Moderator
Joined
Jun 18, 2007
Messages
3,211
Location
Cincinnati / Columbus OH
#1
Custom Subaction Event - Character Data Modifications (1.02) [Achilles]
Code:
C20716F8 00000033
80A40008 88C50001
2C0600FF 40820180
7CC802A6 90C1FFF4
8063002C A0C50002
7CE62A14 90E40008
38C6FFFC 39200008
7C864BD6 38A50004
88C50000 88E50001
A1050002 81250004
2C060000 4182001C
2C060001 41820024
2C060002 4182002C
2C060003 41820034
7D8340AE 4800009D
7D2341AE 480000FC
7D83422E 4800008D
7D23432E 480000EC
7D83402E 4800007D
7D23412E 480000DC
C0250004 54E9CFFF
41820010 C003002C
FC200072 38E7FF80
7D234214 C0090000
2C070000 41820044
2C070003 418000AC
4182001C 2C070004
4182001C 2C070005
4182001C 2C070006
4182001C FC21002A
48000018 FC200828
48000010 FC210032
48000008 EC200824
D0290000 4800006C
2C070000 4D820020
2C070001 4182002C
2C070002 4182002C
2C070003 4182002C
2C070004 4182002C
2C070005 4182002C
2C070006 4182002C
7D296038 4E800020
7D296378 4E800020
7D296214 4E800020
7D296050 4E800020
7D2C49D6 4E800020
7D2C4BD6 4E800020
38A50008 3884FFFF
2C040000 41A1FEBC
80C1FFF4 7CC803A6
4E800020 80640008
60000000 00000000
This code presents a new, flexible-length subaction event that allows modifications to the character data structure of a player. SSBM Memory Spreadsheet - See the "Char Data Offsets" tab.

Inject this code into your ISO (I suggest using DRGN's Code Manager) and then you can start modifying subaction data within Pl__.dat files to include this new command.

Custom Subaction Format
F8FFAAAA BBCCDDDD EEEEEEEE .... BBCCDDDD EEEEEEEE ... BBCCDDDD EEEEEEEE ....

F8 = subaction command byte (always keep as 0xF8)
FF = flag for custom function (always keep as 0xFF)
AAAA = length of this custom subaction ( = 0x4 + 0x8*[# of char data offset mods])
ex. just one char data offset mod would have a total length of 0xC. Two mods at once would be 0x14. etc..​

The following data is for modifying a character data offset value. Modifications can be repeated in succession, as long as the "length of this custom subaction" is modified accordingly.

BB = modification type
00 = byte [8-bit]
01 = half word [16-bit]
02 = word [32-bit]
03 = floating point number [32-bit]​
CC = modification operation flag
00 = overwrite value at character data offset
01 = AND with value at character data offset
02 = OR with value at character data offset
03 = add to value at character data offset
04 = subtract from value at character data offset
05 = multiply with value at character data offset
06 = divide current value at char data offset with new value
+0x80 = [for floats to x-axis values] multiply new value by character facing direction
1.00 = facing right, -1.00 = facing left (these aren't important to know)​
DDDD = character data offset
EEEEEEEE = new value to be placed @ character data offset
- in reference to x-axis values (e.g. velocity), with the 0x80 flag above enabled, positive float means forward and negative means backward​

NOTE: The BB and CC flags above do not add, except the 0x80 with the CC flag. So, setting the CC flag to 0x85 means to take the new value, multiply it by the facing direction, and then take the value [presumably a float] currently at the char data offset and multiply it by previous result.

@Ampers @Tater Itaru Itaru
omegagmaster omegagmaster use this for your Doc Mario Tornado mod by changing the y-velocity (try self-induced y veloc)

I just whipped this up really quickly tonight and didn't test it much at all. Let me know if there are any issues. Also, if you don't understand how the game uses the character offset data, you may be trying to change something that simply can't be done with a simple overwriting method like this. As an example, plugging in a new action state ID into the action state offset at 0x10 (0x70 minus 0x60) will not cause that player to enter the new action state. The game doesn't work that way (without further modifications). But setting things like velocities should cause instant changes.
 
Last edited:
Joined
Dec 20, 2015
Messages
44
#2
Looks very interesting. I'll most definitely have to experiment with this. Thanks Achilles! :)

Edit: Sorry, I don't exactly know how to insert this code in the Code Manager. I'm a bit new to codes actually, so I don't know much. I went to Tools, Add New Mod to Library, but now I'm unsure what type of mod this is. Is it an injection mod? Static overwrite? ASM? If it's an injection, what is the injection point? If you could explain to me and/or link me to a guide on how this works, I would be very grateful.

My final question is where the injection point? And also how would you test to determine if the code is running (i know you can write a subaction command, but one that would be clearly noticeable in game)
 
Last edited:

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
356
#3
omegagmaster omegagmaster I’ve only just recently started using the code manager, and it’s pretty dang useful once you understand it.

Here’s how to break up a C2 code to get the offset. It’s fairly simple once you know what you’re looking at:

C2 codes are a simple wrapper around a block of ASM to be injected. They look something like this:

C20716F8 00000033
… (CODE) ...
60000000 00000000

The head describes the injection point and the length of the injection. To get the injection point address, change the “C” to an “8” and subtract 2 from the “2” digit (in some codes it will be a 3.)

The body is the code to be injected.

The foot provides space for a return branch for the injection code. My understanding is that it’s 64-bit aligned, so it provides a “nop” instruction (60000000) when the injection is an even number of lines. The trailing “00000000” line is where the important return branch appears in RAM.

When converting a C2 code to an injection mod, you only need to record the address from the C2 line (for the “Offset” field,) and then you can delete everything except for the trailing “00000000” line.

To use this C2 code as an injection mod in the code manager:
  1. Go to the Tools tab,
  2. Click “Add New Mod to Library”
  3. Click the “+” button next to “Injection Mod”
  4. Use 800716F8 as the offset.
  5. Paste the C2 code in the “Injection Code” field
  6. Delete the "C20716F8 00000033" lines from the top, and (optionally) remove the “60000000” from the bottom.
  7. Fill out any information you want to include and hit “Submit Mod”
  8. The save prompt that pops up is for organization. I saved mine to a text file called “Custom Subaction Events” along with Achilles' other custom event; but you can organize it however you like.

Now, when you go to the Injection Mods tab and hit “rescan for mods” it will show the mod you just added.

As for testing it, I believe you can use Crazy Hand’s “raw data” option to modify the contents of each event byte-by-byte. This should let you use the syntax Achilles provided in his op. I've never done this, but I think that's how it goes.

Alternatively, you could play with the events in RAM if you have a means of editing memory (cheat engine, debug dolphin.) I’m actually neck deep in a project right now that’s meant to provide the ability to do this via ASM.

---

Achilles1515 Achilles1515 This is a really powerful one! I’ve only just poked my head in the forums today, so I haven’t played with it yet but I’ll try to think up some fun examples later and post them.

I have a question about your plans for extending the index. I believe the next available event slot is the last parsable 6-bit ID; so will you be making 0xFC a sort of prefunction for calling things from another index? Like a "parent" event? Or do you plan on doing something else?
 

Achilles1515

Moderator
Moderator
Joined
Jun 18, 2007
Messages
3,211
Location
Cincinnati / Columbus OH
#4
omegagmaster omegagmaster I’ve only just recently started using the code manager, and it’s pretty dang useful once you understand it.

Here’s how to break up a C2 code to get the offset. It’s fairly simple once you know what you’re looking at:

C2 codes are a simple wrapper around a block of ASM to be injected. They look something like this:

C20716F8 00000033
… (CODE) ...
60000000 00000000

The head describes the injection point and the length of the injection. To get the injection point address, change the “C” to an “8” and subtract 2 from the “2” digit (in some codes it will be a 3.)

The body is the code to be injected.

The foot provides space for a return branch for the injection code. My understanding is that it’s 64-bit aligned, so it provides a “nop” instruction (60000000) when the injection is an even number of lines. The trailing “00000000” line is where the important return branch appears in RAM.

When converting a C2 code to an injection mod, you only need to record the address from the C2 line (for the “Offset” field,) and then you can delete everything except for the trailing “00000000” line.

To use this C2 code as an injection mod in the code manager:
  1. Go to the Tools tab,
  2. Click “Add New Mod to Library”
  3. Click the “+” button next to “Injection Mod”
  4. Use 800716F8 as the offset.
  5. Paste the C2 code in the “Injection Code” field
  6. Delete the "C20716F8 00000033" lines from the top, and (optionally) remove the “60000000” from the bottom.
  7. Fill out any information you want to include and hit “Submit Mod”
  8. The save prompt that pops up is for organization. I saved mine to a text file called “Custom Subaction Events” along with Achilles' other custom event; but you can organize it however you like.

Now, when you go to the Injection Mods tab and hit “rescan for mods” it will show the mod you just added.

As for testing it, I believe you can use Crazy Hand’s “raw data” option to modify the contents of each event byte-by-byte. This should let you use the syntax Achilles provided in his op. I've never done this, but I think that's how it goes.

Alternatively, you could play with the events in RAM if you have a means of editing memory (cheat engine, debug dolphin.) I’m actually neck deep in a project right now that’s meant to provide the ability to do this via ASM.

---

Achilles1515 Achilles1515 This is a really powerful one! I’ve only just poked my head in the forums today, so I haven’t played with it yet but I’ll try to think up some fun examples later and post them.

I have a question about your plans for extending the index. I believe the next available event slot is the last parsable 6-bit ID; so will you be making 0xFC a sort of prefunction for calling things from another index? Like a "parent" event? Or do you plan on doing something else?
Due to the ID limitations as you mentioned, I think any custom events in the future should have the exact same event byte (e.g. 0xF4 is all custom events). Then the byte at 0x01, which is 0xFF for my two custom events, can just be used as the new custom event ID. In a way, this was kind of the point of keeping that byte as a common "flag". The only purpose the 0xFF has is to tell the game that THIS 0xF4 subaction is a custom one, as opposed to a default one which I'm not sure exists. But the function was vanilla in the game for 0xF4, so I stayed on the conservative side to say that there might be a 0xF4 event, but the likelihood of the second byte being 0xFF was highly unlikely (especially considering that the vanilla 0xF4 event doesn't do anything except increment the subaction reading location). I think we could start custom subaction events with this flag as 0x01 and increment it for each new one.

So:
F401XXXX ..... = custom projectile event subaction
F402XXXX ..... = custom character data mod subaction
etc.

254 custom subactions should be good for now lol. This will actually be easier for anyone trying to use it too, because the entire code containing all the ASM for controlling the different custom subactions can be combined into one single, big code. One code to apply, and then you've got everything.

------------------
Sorry if my lack of response has made you feel like I'm ignoring you. Your posts are great and your content likewise. I've just been sucked into a black hole of CPU AI improvements for 20XX 4.0. It's going extremely well, though.
 
Last edited:

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
356
#5
Achilles1515 Achilles1515 Hey dude thanks for the details! I’m really excited about this prospect you’ve started here with these custom events. I’ve got some suggestions about a few things, but I don’t want to distract from what you’re doing right now because improving the CPU AI sounds like something to really get lost in. Lol, I bet you're having fun with that. I'm glad it's going well!

I’m actually coming from a black hole myself. There’s a lot of discovery involved in this ASM stuff, and I can’t put it down for long without picking it right back up. There’s something extremely gratifying about just exploring the jungle. I've got a little tetris effect going on, though.

---

One thing before you get back to work on your CPU AI project--I've been testing out this F8 event of yours.

You know it’s rad right?

I mean, it can do a million powerful things, but one of the coolest details is that that it does all of these said things from within the data files.

You can for instance move the pointer in player.0x44C, which points to the next event. This is what goto events do--except that they use offsets from within the .dat file.

In other words, you can now "goto" any place in RAM. Like, I think Itaru Itaru could feasibly put God Ganon in the code manager with this, and access it with straight Crazy Hand edits @Ampers @Tater DRGN DRGN

F8FF000C 020003EC <pointer to Ganon's Castle>

I'll provide a simple example and maybe a guide later. Heck, maybe a lot of guides--there's so much you can do with this thing.
 

DRGN

Technowizard
Moderator
Premium
Joined
Aug 20, 2005
Messages
2,036
Location
Sacramento, CA
#6
I haven't gotten the chance to play with this, but it sounds really cool.

Achilles1515 Achilles1515 Hey dude thanks for the details! I’m really excited about this prospect you’ve started here with these custom events. I’ve got some suggestions about a few things, but I don’t want to distract from what you’re doing right now because improving the CPU AI sounds like something to really get lost in. Lol, I bet you're having fun with that. I'm glad it's going well!

I’m actually coming from a black hole myself. There’s a lot of discovery involved in this ASM stuff, and I can’t put it down for long without picking it right back up. There’s something extremely gratifying about just exploring the jungle. I've got a little tetris effect going on, though.

---

One thing before you get back to work on your CPU AI project--I've been testing out this F8 event of yours.

You know it’s rad right?

I mean, it can do a million powerful things, but one of the coolest details is that that it does all of these said things from within the data files.

You can for instance move the pointer in player.0x44C, which points to the next event. This is what goto events do--except that they use offsets from within the .dat file.

In other words, you can now "goto" any place in RAM. Like, I think Itaru Itaru could feasibly put God Ganon in the code manager with this, and access it with straight Crazy Hand edits @Ampers @Tater DRGN DRGN

F8FF000C 020003EC <pointer to Ganon's Castle>

I'll provide a simple example and maybe a guide later. Heck, maybe a lot of guides--there's so much you can do with this thing.

Maybe I'm not sure what you're meaning by the RAM gotos, but there's already a feature in MCM that allows you to branch/goto any RAM address. In a spoiler in the MCM thread's second post, 'Standalone Functions and Special Branch Syntaxes', it talks about special commands that you can use with it. Included is an example where the mod branches to a built-in game function via the function's address simply by using "bl 0x800948a8".
 
Last edited:

Achilles1515

Moderator
Moderator
Joined
Jun 18, 2007
Messages
3,211
Location
Cincinnati / Columbus OH
#7
I haven't gotten the chance to play with this, but it sounds really cool.




Maybe I'm not sure what you're meaning by the RAM gotos, but there's already a feature in MCM that allows you to branch/goto any RAM address. In a spoiler in the MCM thread's second post, 'Standalone Functions and Special Branch Syntaxes', it talks about special commands that you can use with it. Included is an example where the mod branches to a built-in game function via the function's address simply by using "bl 0x800948a8".
The game needs to know where it last left off within a character's subaction data (what Crazy Hand modifies) for the current subaction/action state. So it leaves a pointer in the character data structure that points to the current subaction data location deep in RAM. Next frame it loads that pointer, jumps to the subaction data, and continues.

Punkline is talking about using the "32-bit overwrite" feature of this custom subaction event to change that pointer to, as an example, 0x80005000 where you inserted some brand new subaction data. It's a brilliant idea.
 
Last edited:

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
356
#8
The game needs to know where it last left off within a character's subaction data (what Crazy Hand modifies) for the current subaction/action state. So it leaves a pointer in the character data structure that points to the current subaction data location deep in RAM. Next frame it loads that pointer, jumps to the subaction data, and continues.

Punkline is talking about using the "32-bit overwrite" feature of this custom subaction event to change that pointer to, as an example, 0x80005000 where you inserted some brand new subaction data. It's a brilliant idea.

Exactly what I mean! Sorry, I should have clarified that "goto" is a subaction event. I’ve been playing with it a bit, so here’s a quick example just to be clear:​

Code:
0800000B
44080000 00024A04
00007840 44000000
00024A22 00007F40
B9380000 28DC0000
03F30000 00000000
00000300 03000300
28DC0000 016A0000
00000000 00000300
03000300 28000000
04060000 00000000
00000000 00000000
28000000 00620000
00000000 00000000
00000000 2C000007
0C800000 02580000
2A828792 280420A3
04000002 28000000
00B70000 00000000
00000000 00000000
0C000007 F8FF0014
03030964 41C00000
03040930 3FC00000
04000001 10000000
40000000 04000014
B9380000 28DC0000
03F30000 00000000
00000300 03000300
28DC0000 016A0000
00000000 00000300
03000300 28000000
01230000 00001200
10000000 00000000
2C000009 07D00000
00FA0064 9B0F0792
0F0404A1 0C000008
28DC0000 012C0000
00001400 00000000
00800020 F8FF001C
03030928 40A00000
0303092C 40800000
03040930 3F400000
04000001 10000000
40000000 04000001
2C000003 08C00000
20001000 280F0792
0A0404A1 0C000008
28DC0000 012C0000
00001400 00000000
00800020 F8FF0014
03030930 3F400000
0304092C 40A40000
04000001 10000000
40000000 04000020
5C000000



---

DRGN DRGN I really really want to use standalone functions. I could get them to work wonders on a number of the bloated projects I’m sitting on. For example, here’s how you’d do all of this “goto” business entirely in code:

Event data works in tandem with the following memory addresses (where “player” here refers to the table pointed to by player data pointers listed in the SSBM Data sheet: )

player.0x444 (eventControl.0x00) = Event Timer (like a keyframe countdown)
player.0x448 (eventControl.0x04) = Subaction Frame (used for async timers)
player.0x44C (eventControl.0x08) = Event Pointer (will read subaction data on keyframes)
player.0x450 (eventControl.0x0C) = Subaction Loop Count (also 1 is stored here for “returns”)
player.0x454 (eventControl.0x10) = Event Return Pointer (also used in “execute loops”)

A function at 0x80073240 (I’ve been calling it “SAevent_framein my notes) serves subaction data (that's been loaded from the .dat files into RAM) to individual parsing functions using the event pointer (and BLRLs calculated with these lookup tables: events 00-24 -- 0x803B9840; events 28-?? -- 0x803c06e8)

It's called every frame for every player in a loop that reads off “events” in short bursts between designated control events (like timers, which create keyframes.)

---

Here’s a funky analogy comparing SA control events to ASM control logic (+ keyframes: )
Note that this is just to help explain how the system behaves:

Code:
player.0x444 - Event Timer      == "r4" (keyframe timer)
player.0x448 - Subaction Frame  == "r5" (frame count)
player.0x44C - Event Pointer    == "PC"
player.0x450 - Event Loop Count == "CTR"
player.0x454 - Return Pointer    == "LR"

Event input         == "r3" (parsed data, read by a moving pointer)
Event 04 (Sync Timer)   == mr r4, r3
Event 08 (Async Timer)  == subf r4, r5, r3
Event 0C (set loop)     == mtctr r3; bl 0x4
Event 10 (execute loop) == bdnz
Event 14 (goto)         == mtlr r3; blrl
Event 18 (return)       == blr

---

I believe you could write a standalone function that looks like this:
Code:
#<getBeefData>
blrl        #codes that call this return with a pointer to the data below
.long 0xDEADBEEF    #0x0
.long 0xDEADBEEF    #0x4
.long 0xDEADBEEF    #0x8
.long 0xDEADBEEF    #0xC
.long 0xDEADBEEF    #0x10
#arbitrary length


Then any code could use the data like this:
Code:
bl <getBeefData>
mflr r31    #got my beef data


So for instance, a standalone function that looked like this could be used by a code that organizes groups of injected subactions:
Code:
#<getBowserTauntFlameData>
4E800021 0800000B
44080000 00024A04
00007840 44000000
00024A22 00007F40
B9380000 28DC0000
03F30000 00000000
00000300 03000300
28DC0000 016A0000
00000000 00000300
03000300 28000000
04060000 00000000
00000000 00000000
28000000 00620000
00000000 00000000
00000000 2C000007
0C800000 02580000
2A828792 280420A3
04000002 28000000
00B70000 00000000
00000000 00000000
0C000007 F8FF0014
03030964 41C00000
03040930 3FC00000
04000001 10000000
40000000 04000014
B9380000 28DC0000
03F30000 00000000
00000300 03000300
28DC0000 016A0000
00000000 00000300
03000300 28000000
01230000 00001200
10000000 00000000
2C000009 07D00000
00FA0064 9B0F0792
0F0404A1 0C000008
28DC0000 012C0000
00001400 00000000
00800020 F8FF001C
03030928 40A00000
0303092C 40800000
03040930 3F400000
04000001 10000000
40000000 04000001
2C000003 08C00000
20001000 280F0792
0A0404A1 0C000008
28DC0000 012C0000
00001400 00000000
00800020 F8FF0014
03030930 3F400000
0304092C 40A40000
04000001 10000000
40000000 04000020
5C000000



You could then serve it to the SAevent parsing loop by just setting the pointer in 0x44C. If you set it up correctly, you can also have it return back to the data it was originally parsing. I’m sure you’ll recognize that it’s a very similar process to how code injection works; it just uses data instead of instructions.

I hope to use standalone functions to provide a pipeline for my projects that can be implemented in MCM for other codes (and programs!) to use. I love this magic you’ve started ~

Unfortunately I haven’t been able to get them to work! I can’t get MCM to read any standalone functions I write… I’ve got a pretty tangible example in mind (using a less complicated concept) that I can make a new thread for later, if you wouldn’t mind helping me troubleshoot it.
 
Last edited:

DRGN

Technowizard
Moderator
Premium
Joined
Aug 20, 2005
Messages
2,036
Location
Sacramento, CA
#9


Exactly what I mean! Sorry, I should have clarified that "goto" is a subaction event. I’ve been playing with it a bit, so here’s a quick example just to be clear:​

Code:
0800000B
44080000 00024A04
00007840 44000000
00024A22 00007F40
B9380000 28DC0000
03F30000 00000000
00000300 03000300
28DC0000 016A0000
00000000 00000300
03000300 28000000
04060000 00000000
00000000 00000000
28000000 00620000
00000000 00000000
00000000 2C000007
0C800000 02580000
2A828792 280420A3
04000002 28000000
00B70000 00000000
00000000 00000000
0C000007 F8FF0014
03030964 41C00000
03040930 3FC00000
04000001 10000000
40000000 04000014
B9380000 28DC0000
03F30000 00000000
00000300 03000300
28DC0000 016A0000
00000000 00000300
03000300 28000000
01230000 00001200
10000000 00000000
2C000009 07D00000
00FA0064 9B0F0792
0F0404A1 0C000008
28DC0000 012C0000
00001400 00000000
00800020 F8FF001C
03030928 40A00000
0303092C 40800000
03040930 3F400000
04000001 10000000
40000000 04000001
2C000003 08C00000
20001000 280F0792
0A0404A1 0C000008
28DC0000 012C0000
00001400 00000000
00800020 F8FF0014
03030930 3F400000
0304092C 40A40000
04000001 10000000
40000000 04000020
5C000000






---

DRGN DRGN I really really want to use standalone functions. I could get them to work wonders on a number of the bloated projects I’m sitting on. For example, here’s how you’d do all of this “goto” business entirely in code:

Event data works in tandem with the following memory addresses (where “player” here refers to the table pointed to by player data pointers listed in the SSBM Data sheet: )

player.0x444 (eventControl.0x00) = Event Timer (like a keyframe countdown)
player.0x448 (eventControl.0x04) = Subaction Frame (used for async timers)
player.0x44C (eventControl.0x08) = Event Pointer (will read subaction data on keyframes)
player.0x450 (eventControl.0x0C) = Subaction Loop Count (also 1 is stored here for “returns”)
player.0x454 (eventControl.0x10) = Event Return Pointer (also used in “execute loops”)

A function at 0x80073240 (I’ve been calling it “SAevent_framein my notes) serves subaction data (that's been loaded from the .dat files into RAM) to individual parsing functions using the event pointer (and BLRLs calculated with these lookup tables: events 00-24 -- 0x803B9840; events 28-?? -- 0x803c06e8)

It's called every frame for every player in a loop that reads off “events” in short bursts between designated control events (like timers, which create keyframes.)

---

Here’s a funky analogy comparing SA control events to ASM control logic (+ keyframes: )
Note that this is just to help explain how the system behaves:

Code:
player.0x444 - Event Timer      == "r4" (keyframe timer)
player.0x448 - Subaction Frame  == "r5" (frame count)
player.0x44C - Event Pointer    == "PC"
player.0x450 - Event Loop Count == "CTR"
player.0x454 - Return Pointer    == "LR"

Event input         == "r3" (parsed data, read by a moving pointer)
Event 04 (Sync Timer)   == mr r4, r3
Event 08 (Async Timer)  == subf r4, r5, r3
Event 0C (set loop)     == mtctr r3; bl 0x4
Event 10 (execute loop) == bdnz
Event 14 (goto)         == mtlr r3; blrl
Event 18 (return)       == blr

---

I believe you could write a standalone function that looks like this:
Code:
#<getBeefData>
blrl        #codes that call this return with a pointer to the data below
.long 0xDEADBEEF    #0x0
.long 0xDEADBEEF    #0x4
.long 0xDEADBEEF    #0x8
.long 0xDEADBEEF    #0xC
.long 0xDEADBEEF    #0x10
#arbitrary length


Then any code could use the data like this:
Code:
bl <getBeefData>
mflr r31    #got my beef data


So for instance, a standalone function that looked like this could be used by a code that organizes groups of injected subactions:
Code:
#<getBowserTauntFlameData>
4E800021 0800000B
44080000 00024A04
00007840 44000000
00024A22 00007F40
B9380000 28DC0000
03F30000 00000000
00000300 03000300
28DC0000 016A0000
00000000 00000300
03000300 28000000
04060000 00000000
00000000 00000000
28000000 00620000
00000000 00000000
00000000 2C000007
0C800000 02580000
2A828792 280420A3
04000002 28000000
00B70000 00000000
00000000 00000000
0C000007 F8FF0014
03030964 41C00000
03040930 3FC00000
04000001 10000000
40000000 04000014
B9380000 28DC0000
03F30000 00000000
00000300 03000300
28DC0000 016A0000
00000000 00000300
03000300 28000000
01230000 00001200
10000000 00000000
2C000009 07D00000
00FA0064 9B0F0792
0F0404A1 0C000008
28DC0000 012C0000
00001400 00000000
00800020 F8FF001C
03030928 40A00000
0303092C 40800000
03040930 3F400000
04000001 10000000
40000000 04000001
2C000003 08C00000
20001000 280F0792
0A0404A1 0C000008
28DC0000 012C0000
00001400 00000000
00800020 F8FF0014
03030930 3F400000
0304092C 40A40000
04000001 10000000
40000000 04000020
5C000000



You could then serve it to the SAevent parsing loop by just setting the pointer in 0x44C. If you set it up correctly, you can also have it return back to the data it was originally parsing. I’m sure you’ll recognize that it’s a very similar process to how code injection works; it just uses data instead of instructions.

I hope to use standalone functions to provide a pipeline for my projects that can be implemented in MCM for other codes (and programs!) to use. I love this magic you’ve started ~

Unfortunately I haven’t been able to get them to work! I can’t get MCM to read any standalone functions I write… I’ve got a pretty tangible example in mind (using a less complicated concept) that I can make a new thread for later, if you wouldn’t mind helping me troubleshoot it.
It looks like you're commenting out the header lines, i.e. the pound sign in "#<getBeefData>" and "#<getBowserTauntFlameData>". Is that how it's written in your library files? Those lines aren't just for human readability, but for the program to recognize them as such too. Also, it may be kind of a strange limitation (that should probably be changed in future versions), but the standalone functions need to be added in conjunction with some mod. By mod I mean everything between the "-==-" separators in the library files. So, for example, this wouldn't work:

-==-
<someFunction>
01234567 01234567
01234567 01234567
01234567 01234567
01234567 01234567
01234567 01234567
01234567 01234567
-==-

The parser won't read this and add it to the game. But if you append it to one of the mods that requires it, like this:

-==-
Title of mod
description text, I am
[author]
Version -- DOL Offset ------ Hex to Replace ---------- ASM Code # <- this actually isn't needed and is just meant to help with human readability in the files
1.02 ------ 0x28059C ---- 7C0802A6 -> Branch
01234567 01234567
01234567 01234567
bl <someFunction>
01234567 01234567

<someFunction>
01234567 01234567
01234567 01234567
01234567 01234567
01234567 01234567
01234567 01234567
01234567 01234567
-==-

Then it should be read and added. And if you have multiple mods that include that someFunction function with it, it will still only be added to the game once. I'm pretty sure you don't need to add it to each mod that requires it in the library files though (just needs to be in there for one of them). You could test it both ways though if I'm wrong about that last part. Sorry, I it's just that I coded that several months ago and can't remember if I added that aspect towards the end or not, to basically not fill up the library files with redundant data. Let me know if you find/think of information I should add to my descriptions in my second post in the MCM thread regarding standalone functions, or if there's anything confusing. I should probably add some of what I just said here. So then hopefully it will be clearer for those that come after you, and more people can more easily figure out how to use it. Hope this helps.
 
Last edited:

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
356
#10
It looks like you're commenting out the header lines, i.e. the pound sign in "#<getBeefData>" and "#<getBowserTauntFlameData>". Is that how it's written in your library files? Those lines aren't just for human readability, but for the program to recognize them as such too. Also, it may be kind of a strange limitation (that should probably be changed in future versions), but the standalone functions need to be added in conjunction with some mod. By mod I mean everything between the "-==-" separators in the library files. So, for example, this wouldn't work:

-==-
<someFunction>
01234567 01234567
01234567 01234567
01234567 01234567
01234567 01234567
01234567 01234567
01234567 01234567
-==-

The parser won't read this and add it to the game. But if you append it to one of the mods that requires it, like this:

-==-
Title of mod
description text, I am
[author]
Version -- DOL Offset ------ Hex to Replace ---------- ASM Code # <- this actually isn't needed and is just meant to help with human readability in the files
1.02 ------ 0x28059C ---- 7C0802A6 -> Branch
01234567 01234567
01234567 01234567
bl <someFunction
01234567 01234567

<someFunction>
01234567 01234567
01234567 01234567
01234567 01234567
01234567 01234567
01234567 01234567
01234567 01234567
-==-

Then it should be read and added. And if you have multiple mods that include that someFunction function with it, it will still only be added to the game once. I'm pretty sure you don't need to add it to each mod that requires it in the library files though (just needs to be in there for one of them). You could test it both ways though if I'm wrong about that last part. Sorry, I it's just that I coded that several months ago and can't remember if I added that aspect towards the end or not, to basically not fill up the library files with redundant data. Let me know if you find/think of information I should add to my descriptions in my second post in the MCM thread regarding standalone functions, or if there's anything confusing. I should probably add some of what I just said here. So then hopefully it will be clearer for those that come after you, and more people can more easily figure out how to use it. Hope this helps.

Oh, the example was meant to just fit the subaction I made into the "beefData" concept I was speaking of (which I provided from the top of my head, and so is not actually in my mods library at all.)

It's supposed to be raw ASM, and the comment was there simply to denote what it would become after being included in the GUI. Sorry about all of the confusion!

---

I should have been more clear that I was referring to my woes over the last few days in trying to get standalone functions to work on a little side project I started just to try them out; of which I've simply used ASM inserted in the text-fields of the GUI your program provides for creating standalone functions as a part of a packaged injection mod.

I think I’ve identified 3 individual problems regarding this process that may be potential bugs. I'll send you a PM later with the details~

Bugs fixed as of MCM v2.1 release. Thanks DRGN!
 
Last edited:

SinsOfApathy

Smash Journeyman
Joined
Feb 24, 2015
Messages
444
NNID
Psion312
#11
Punkline Punkline I think Player.0x450 is related to the Sync timer event, since Asynchronous doesn't count down, while Synchronous does.
 
Last edited:

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
356
#12
Punkline Punkline I think Player.0x450 is related to the Sync timer event, since Asynchronous doesn't count down, while Synchronous does.
What exactly do you mean about the async timers not counting down?

It wouldn’t surprise me if I were mistaken; but from what I’ve observed, each of the timer events only write to 0x444 (async timers just use the difference of their input and the current frame (0x448) to calculate the value that goes in 0x444.)

You’re right about 0x450 though. I looked into it and something’s fishy...

It seems to increment 0x450 by one when setting a return pointer. It does this because it then left shifts the value twice; creating word alignment out of it to then use as an offset from 0x444 (to clarify, 0x444 + offset + (0x8, 0xC, or 0x10))

It appears to do this in order to store return pointers (and loop counts) at variable locations. If I had to guess, this is some kind of way of storing nested returns? Just a guess.

Check the function dumps below; perhaps you’ll see something I missed (the functions are all quite small and will give you a pretty concrete understanding of how the 0x444 table behaves.)


---


There is some easily observable organization among the different indices used to call these events. These control index events seem to do all of the manipulation to the subaction table starting at 0x444 in the player data. (other player events only move the pointer in 0x44C)

I could be wrong about that, though. I’ve got a lot left to explore.

For example, I haven’t looked into Event D0, which is apparently “continuation control.” (Deep in the player event index. Sort of an ugly duck.)

I’m in the process right now of gathering a lot of information about these events just by looking at the functions (from their lookup tables.)

Lemme share the few notes I have~

Event function dumps, with some comments:

Event 00 - “stop pointing it’s rude”
Event 04 - “Sync Timer”
Event 08 - “Async Timer”
Event 0C - “Set Loop”
Event 10 - “Execute Loop”
Event 14 - “GoTo”
Event 18 - “Return”
Event 1C - “Subroutine” - “hard branch”
Event 20 - “Skip Timer Decrement” - (still need to check this one)


No comments on these, but you might find the dumps helpful:

Event 24 - “???”
Event D0 - “Continuation Control”


---


BTW, thank you for taking the time to add this to the data sheet! I’ve got a few recent discoveries related to the hitbox event that I think would make excellent additions as well--but could use a second set of critical eyes to go over just to confirm things. Specifically, things about the bone lookup table in player.0x648...

Achilles1515 Achilles1515 you can do some pretty cool things with these hitbox addresses using your F8 event. It's possible to "animate" existing hitbox positions and variables; allowing you to some fun things with the "Set Loop" and "Execute Loop" events:
  • Set/increment/decrement positions to force interpolations
  • Increment/decrement origins to animate positions
  • Increment/decrement size to scale hitboxes over time
And some stuff you can do without loops:
  • Change elements mid-attack
  • Modify SFX
  • Revalidate terminated hitboxes
  • Erase collision records (stored as player/article pointers)
  • etc

Note that there is a whole section for hurtboxes that comes after this area of memory that I haven't looked into yet.

Player data Hitbox tables:

player.0x974 - Hitbox ID 0
player.0xAAC - Hitbox ID 1
player.0xBE4 - Hitbox ID 2
player.0xD1C - Hitbox ID 3

Each hitbox table appears to be 0x138 bytes long. The first part of the data looks like this:

hitbox.0x00 - Hitbox Status (0 is disabled, 1 and 2 initialize 3, which is enabled)
hitbox.0x04 - ?
hitbox.0x08 - DMG (int, unstaled, scaled to model scale) (modifying does nothing?)
hitbox.0x0C - DMG (float, staled, scaled to model scale)
hitbox.0x10 - Z offset from bone (float)
hitbox.0x14 - Y offset from bone (float)
hitbox.0x18 - X offset from bone (float)
hitbox.0x1C - Size of hitbox (float)
hitbox.0x20 - Knockback Angle (int)
hitbox.0x24 - Knockback Growth (int)
hitbox.0x28 - Knockback Weight-Dependant Scale (int)
hitbox.0x2C - Knockback Base (int)
hitbox.0x30 - Element (int)
hitbox.0x34 - Shield Damage
hitbox.0x38 - Hit SFX (int)
hitbox.0x3C - Hit SFX type (just increments previous index by 3…)
hitbox.0x40 - Flags 1 (?)
hitbox.0x44 - Flags 2 (?) << I think this is where hit/hurt interaction is
hitbox.0x48 - Bone Attachment Data Pointer
hitbox.0x4C - Z Position (all coords are floats)
hitbox.0x50 - Y Position
hitbox.0x54 - X Position
hitbox.0x58 - Z Position (last frame)
hitbox.0x5C - Y Position (last frame)
hitbox.0x60 - X Position (last frame)
hitbox.0x64 - Z (last collision)
hitbox.0x68 - Y (last collision)
hitbox.0x6C - X (last collision)

The later portion of the table (not above) is a bunch of placeholders for collision records. Each appears as a pointer to either a projectile or a player (regardless of whether they collide with hitboxes or hurtboxes,) and is 64-bit aligned for some reason--padded with 0’s. I would guess that something else goes there, but I don’t know for certain.

Strangely, the records stop after 13 lines and begin clobbering previous records; leaving 0x5C bytes unaccounted for. It looks to be allocated in the same manner as the collision records, but I haven’t seen anything write to it.

(Hitbox records can be invalidated in order to renew collision logic.)


You can find the lookup table for bones at player.0x648.


Each entry is 0x10 large. The data looks like this, from what little I can tell:

boneID.0x0 - Bone table 1 pointer (read-only?)
boneID.0x4 - Bone table 2 pointer (used for frame updates?)
boneID.0x8 - unk Float
boneID.0xC - unk 8-bit?
boneID.0xD - unk 8-bit?
boneID.0xE - unk 2 bytes of padding?

I really only know anything about the two pointers. You can get some interesting effects by swapping them around in the table.

Hitbox bone attachments appear to reference bone table 1. I’m calling it that just because I’m not sure what else to at the moment...

Hitbox.0x48 contains a pointer that is constructed like this inside of the hitbox parsing function:

Player.0x648 -> boneLookup.0x0 + (boneID * 16) -> boneIDTable1 pointer

Table 1 appears to contain a read-only record of things like current coordinates and scale for a (single) bone. Editing these values appears to do nothing; so it’s a little difficult to make out what’s what. I briefly took note of the values, and need to double check all of it:

boneIDTable1.0x00 - pointer (common between bones?) (80406708 in marth example)
boneIDTable1.0x04 - int (maybe an ID?)
boneIDTable1.0x08 - pointer to other bone (sometimes blank)
boneIDTable1.0x0C - pointer to other bone (sometimes blank)
boneIDTable1.0x10 - pointer to other bone (sometimes blank)
boneIDTable1.0x14 - flags? mostly 0
boneIDTable1.0x18 - padding 0’s? Below are a lot of coordinate/dimension descriptions...
boneIDTable1.0x1C - ZXY 1
boneIDTable1.0x28 - Float (?)
boneIDTable1.0x2C - ZXY Bone Scale (this is the only one I’ve confirmed)
boneIDTable1.0x38 - ZXY 3
boneIDTable1.0x44 - ZXY 4 (these next 4 commonly update every frame, like positions)
boneIDTable1.0x50 - ZXY 5
boneIDTable1.0x5C - ZXY 6
boneIDTable1.0x68 - ZXY 7

The other table is modifiable, but is updated through some kind of iterative blending process that blends with some parent data of loaded bone dimensions.

I didn’t take note of the offsets for this second table one because I think they may resemble the first… still need to confirm.

I’m certain about one thing though; the blending process I just mentioned corrects itself based on a parent table that can be referenced by either of these at offset 0x84 as a pointer. Modifying THAT table will create permanent modifications to the bone dimensions.

To summarize:

player.0x648 -> (boneID * 16) -> 0x84 == table of permanent dimensions/coordinates for single bone

Edit: example --

 
Last edited:

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
356
#14
I'm not quite sure what you're getting at. Each timer event writes its result to 0x444--the async timer is just calculated from the frame count in 0x448 (which counts up, not down.) The notes on that page are simply speaking of the manner in which the timer that's used gets calculated.

See Events 04 and 08 in the dumps there for clarification--you can step through the process and watch it happen in dolphin debug.
 
Last edited:

SinsOfApathy

Smash Journeyman
Joined
Feb 24, 2015
Messages
444
NNID
Psion312
#15
I'm not quite sure what you're getting at. Each timer event writes its result to 0x444--the async timer is just calculated from the frame count in 0x448 (which counts up, not down.) The notes on that page are simply speaking of the manner in which the timer that's used gets calculated.

See Events 04 and 08 in the dumps there for clarification--you can step through the process and watch it happen in dolphin debug.
Just ignore what I said. I was thinking 0x450 was something else besides the loop count, like how Async Timer counts up there would need to be an address that has the limit so that if Timer > Limit, end Event.
 

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
356
#16
Just ignore what I said. I was thinking 0x450 was something else besides the loop count, like how Async Timer counts up there would need to be an address that has the limit so that if Timer > Limit, end Event.
Ah, makes sense. Yeah for async timers, the limit you're speaking of is the actual input parsed from the event data. It gets calculated like this, I believe:

new timer (0x444) = input - current subaction frame (0x448)

Edit: Also, the parsing loop function (the function I keep calling SAEvent_frame) is responsible for updating this address before actually starting the parsing loop that serves these events.

I believe it's adopted from 0x8F4, which has a whole other set of functions that update it.
 
Last edited:
Joined
Dec 20, 2015
Messages
44
#17
Achilles1515 Achilles1515 you can do some pretty cool things with these hitbox addresses using your F8 event. It's possible to "animate" existing hitbox positions and variables; allowing you to some fun things with the "Set Loop" and "Execute Loop" events:
  • Set/increment/decrement positions to force interpolations
  • Increment/decrement origins to animate positions
  • Increment/decrement size to scale hitboxes over time
And some stuff you can do without loops:
  • Change elements mid-attack
  • Modify SFX
  • Revalidate terminated hitboxes
  • Erase collision records (stored as player/article pointers)
  • etc


Better than the Buster Sword. I'm really excited for a tutorial though because I haven't really gotten into ASM/functions and I can't understand how to use this to execute ASM functions in the game.

Just curious, is it possible to use this code to force a shield break upon your opponent or special fall? I want to do something like this (not necessarily those tripped up things that happened to the opponent, but that would be cool too):

 
Last edited:

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
356
#18
Better than the Buster Sword. I'm really excited for a tutorial though because I haven't really gotten into ASM/functions and I can't understand how to use this to execute ASM functions in the game.

Just curious, is it possible to use this code to force a shield break upon your opponent or special fall? I want to do something like this (not necessarily those tripped up things that happened to the opponent, but that would be cool too):


Those are really good questions. First of all, this event only has an effect on the player calling it (as in, the player triggering the event data you’re altering.)

If you could reference an opponent though, you would then have to find a means of defining “opponent.” This would be sort-of simple because there are addresses in the player data that record pointers of other players for things like “player currently being comboed” or “player holding me/player I am holding” (as well as many other useful things, like the bone table I mentioned above--possibly allowing you to do most of the spookier stuff that Luigi was doing in the video)

The reason it’s only “sort-of” simple though is because you would run into potential problems without another kind of means of creating logical comparisons via some kind of “conditional goto.” The addresses containing pointers don’t always have valid data for you to use, so results would sometimes be unpredictable without some way of “checking” and diverting the event to some other set of instructions to handle a variable outcome.

Achilles1515 Achilles1515 if you’re planning on repackaging these codes into one big mod you might be able to implement anything like the following suggestions in order to make this sort of thing possible. It would make the subaction event system ridiculously powerful :

For F8 FF - Character Data event
--Extra modification type option:
  • 04 - load a pointer from the designated offset in DDDD. An extra input line following this one uses the pointer pointed to by the 04 mod line as a BA instead of player.0x60.
    • Examples of how this simple addition might be used:
      • --Add 10 frames of hitstun to player currently being comboed by you
        • F8FF0010 04002094 030323A0 41200000
      • --Fake Beamsword? (set “length” dimension of Marth’s sword until end of current animation (corrected by parent data))
        • F8FF0014 040005E8 040004C4 03050030 3F828F5C
      • --Increment length of Marth’s sword by 20% (permanent)
        • F8FF0018 040005E8 040004C0 04000084 03050030 3F828F5C

--Extra operation options:
  • +0x40 - look up value - instead of using EEEEEEEE as a literal value, interpret the input as a pointer to an existing value and use that instead. A special “offset syntax” for this input could check for the “8” bit at the beginning of a pointer, allowing for the following functionality when it was missing:
    • 8xxxxxxx == absolute pointer
    • xxIIOOOO == offset syntax
      • II - 8-bit target function ID
        • 00 - use target as value
        • 01 - use target as pointer (may conflict with previous syntax... so maybe not)
      • OOOO -- 16-bit signed immediate -- offset from player.0x60 or BA specified in II
  • 07 - XOR (same as OR operation type, but with XOR instruction; allowing for relative bit “toggles”)

---

Additional event - Conditional GoTo
This would be an entirely new event I guess, but it would make heavy character modifications possible entirely from the event system. I’m sure you have your own ideas about something like this in the future, so here are just some suggestions:

The goto address could use the same special “offset syntax” suggested above for the “look up value” operation option.
  • 8xxxxxxx == absolute pointer
  • xxIIOOOO == offset syntax
    • II - 8-bit BA ID
      • 00 - use current event pointer
      • 01 - use a return value? (assuming we learn more about 0x450)
    • OOOO -- 16-bit signed immediate -- offset from BA specified by II


If using a variable length syntax that’s similar to this F8 event, It’d be possible to use this as a non-conditional goto as well in order to take advantage of the same “hook” capability that F8 can provide; but with only 2 words--creating a 1:1 size comparison to existing control events, and with the option for “relative” gotos.

I also think it would be possible to just use a literal CR0 nibble to summarize all of the comparison logic?


omegagmaster omegagmaster , with that said, it’s probably possible to do things like forcing a shield break or special falling via some flags or something hidden away somewhere in memory. I took a long look last night and didn’t come up with any answers directly related to what you’re asking... but I've been curious since you started that request thread, so I'll keep looking.

I’ll be releasing a code soon that lets you see this sort of stuff pretty easily using Cheat Engine, if you’re interested in exploring the RAM without any knowledge of ASM or functions.

The following F8 event examples might be able to serve similar purposes? Unfortunately, they will not be applicable to an opponent:

So here's a fun idea. This here is an interesting detail from a larger post I made earlier this month:

This table at player.0x21FC matches the pointers available in Crazy Hand’s move logic tables according to the current player/move:

0x00 - IASA function pointer
0x04 - Animation Interrupt pointer
0x08 - Action Physics pointer
0x0C - Collision Interrupt pointer
0x10 - Camera Behaviour pointer
These pointers can be changed, and will be used for various progressions in move logic. I don’t know much about this, but it’s possible to use falling/specialfalling interrupt function pointers in these addresses to simulate the same kind of interrupt limitations those states cause.

So, to simulate special falling IASA logic you would use this event:
F8FF000C 0200219C 80096AF4

To simulate normal falling IASA logic, I think you would use this event:
F8FF000C 0200219C 800CCD34

Note that these will be overwritten as soon as the action state changes

---

Here’s a flag you can use that seems to initialize the “Stamina KO” shieldbreak animation. It softlocks the player without being unflagged, and uses a different flag after initialized:
F8FF000C 00022224 00000010

The initialization appears to just be responsible for a screen flash, a (non-disappearing) body aura, and a sound effect before toggling a different flag. You can toggle just that one in order to create the no-control damage fall/tumble that accompanies the effect:
F8FF000C 00022224 00000020

Both are toggled off with this event:
F8FF000C 00012224 000000EF

---

It’s possible to directly change the number of jumps a player is recorded as having used since last hit the ground in order to regain control after entering special falling. It’s not possible to use this address to ENTER special falling, however.

This event subtracts 1 from the currently recorded number of jumps used; exiting special falling and providing a jump:
F8FF000C 02040168 00000001

---

Fastfall effect uses a 1-bit flag at player.0x227A(bit 0x80)

This toggles ON a fastfall:
F8FF000C 0002221A 00000080

Toggle OFF a fastfall:
F8FF000C 0001221A 0000007F

---

This one will set the corresponding hitbox ID element to “sleep.”
hitbox0:
F8FF000C 02000944 00000007

hitbox1:
F8FF000C 02000A9C 00000007

hitbox2:
F8FF000C 02000BD4 00000007

hitbox3:
F8FF000C 02000D0C 00000007

---

I didn't test all of these anywhere but in a memory editor, so let me know if you notice a typo or something. I'll be looking again tonight for a shieldbreak/specialfalling flag, so I'll add those to this list if I find them.


---


@Ampers @Tater -- After experimenting with event 14 “goto” and event 18 “return (from goto)” I thought I’d bring up a point about the simplicity of event 1C in comparison. Note that these events use calculated RAM addresses after being loaded into the game instead of offsets from the .dat files:



Event 1C is very small, and I’ve used comments to describe every step in the far-right column. It simply sets the event pointer to an absolute address in RAM.

This can’t be exploited from the .dat files like Achilles’ F8 event can, but this is very clearly all the event does. I don’t know if event D0 “continuation control” factors into this somehow in order to turn it into a subroutine; but by itself this event is more like a “goto” than a subroutine. I’ll look into event D0 soon, because it’s weird.

I’m not sure of anything besides the fact that 1C is pretty simple. Maybe it should have its name swapped with 14? From what I DO know about 14--it would actually be more suitably named as “subroutine” since it saves a return address that can be used by event 18 in order to “return (from subroutine)”

SinsOfApathy SinsOfApathy brought up an interesting point about player.0x450 that slightly confuses my understanding of how these “returns” are organized, but I’m sure we can figure it out. I’ll try to look into it further later on.
 
Last edited:

tatatat0

Smash Journeyman
Joined
Jan 28, 2015
Messages
412
#20
Hey there is a small problem with the custom subactions, or a glitch. I've had two separate examples of me editing my velocity in the magnifying glass and it causes exceptions. What gives?
 

Ponkapa

Smash Cadet
Joined
Mar 22, 2014
Messages
72
Location
Ruston, LA
#23
I seem to be unable to install any other mod in conjunction with this one, when using it as an injection in MCM.

I need to be able to get to debug with this mod enabled, and I'm able to get debug replaced for tournament mode fine, and also get this code to work fine, but never in conjunction.

Edit: I've gotten it to work. I wrote over Aux and Debug sectors in MCM, and that (logically) must have been causing the crash.
 
Last edited:

The Cape

Smash Master
Joined
May 16, 2004
Messages
4,478
Location
Carlisle, PA
#25
So with this I would try to make a change for Bowser's D tilt. Essentially when it hits a certain frame (23 I think) it speeds up to complete the move, or you press A and it continues on to complete the move. This essentially gives Bowser a two button press D tilt with only one action. Thoughts?
 

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
356
#26
Changing animation speed requires a function call, I believe. Also, I'm not sure how to implement the A button logic with vanilla events. I'd really like to add GoTo logic to the event system though. Edit - it would allow for stuff like this by using comparisons of the button data to determine a GoTo destination. I think stuff like what you're suggesting would be great in Melee.

I have a set of codes that I'd like to write that would do a few things like this to the subaction event system. I'd like to make it so that new event syntaxes could be used to manipulate registers in order to do math and comparison logic for relative GoTos. I also think it'd be pretty simple to eliminate the need for using the relocation table to create new GoTos.

I'm also planning to create a sort of "macro" syntax, for plugging special code into. (It would let you make function calls like this for animation speedup.)

I made a proof of concept a few days ago for recreating frame speed modifiers as a 1-line event macro:
Code:
Math/Logic Macros [Proof of Concept]
Testing syntax extension for Math/Logic Macros index
Test adds [73]A0 xxxx syntax to crazy hand
73A0 = denotes "Animation Speed Change" macro
xxxx = a percentage; examples:
0001 = ~0.4% speed
0100 = 100% speed
FFFF = ~25600% speed
[Punkline]
1.02 ----- 0x80071ab8 --- 80a50000 -> Branch
# 80071ab8 : lwz r5, 0(r5)
# r3 = external player
# r4 = Opcode Hword
# r5 = Full Data
lwz r5, 0(r5)
andi. r6, r4, 0x0300 # check for extension opcode
beq return
bl <testMathLogic> # run test code
skip:
b 0x80071ac8

return:
.long 0x00000000


<slawSINTtoMeleeMeters> ALL
# util_slawSINTtoMeleeMeters
# r3 = signed int; convert to float; * melee meter coef
# r4 = signed shift r3 left amount (0 = 0xNNNNNN.NN, 4 = 0xNNNNNNN.N, 8 = 0xNNNNNNNN, 12 = 0xNNNNNNN0, etc)
# -- r4 is returned without being modified
# f1 = returned float value
andis. r0, r3, 0x8000 # save signed bit
slw r3, r3, r4
or r3, r3, r0 # as far as I know, there isn't a slaw instruction...

lis r0, 0x4330
xoris r3, r3, 0x8000
stw r0, -0xC(sp)
stw r3, -0x8(sp) # store double in redzone
lfd f0, -0xC(sp) # immediately load from redzone (now it's garbage)
lfd f2, -0x7730(rtoc) # 43300000 80000000 (for casting)
lfs f3, -0x7740(rtoc) # Melee Meters coef (useful for more than melee meters!)
fsubs f0, f0, f2 # cast
fmuls f1, f3, f0 # conversion returned in f1
blr


<testMathLogic> ALL
mflr r0
stw r0, 0x4(sp)
stwu sp, -0x80(sp)
addi r7, sp, 0x10
stswi r3, r7, 0x10 # store registers r3 through r6 in 0x10(sp)

# r3 = external player
# r4 = Opcode Hword
# r5 = Full Data
# r6 = 2nd opcode
rlwinm r7, r5, 0, 24, 31
# r7 = 3rd opcode
cmpwi r6, 0x80
ble standard_links # these should be considered standardized in their index

macro_links:
# put macro links here

cmpwi r4, 0x73A0 # frame_speed_modify macro key
beq frame_speed_modify

#end of macros portal
b return

# macros
frame_speed_modify:
rlwinm r3, r5, 0, 16, 31
li r4, 0
bl <slawSINTtoMeleeMeters> # convert int to scaled float
lwz r3, 0x10(sp) # load r3 from our stswi
bl 0x8006f190 # change animation frame speed

#standard_links
standard_links:
standard:

return:
addi r7, sp, 0x10
lswi r3, r7, 0x10 # restore registers r3 through r6 in 0x10(sp)
addi sp, sp, 0x80
lwz r0, 0x4(sp)
mtlr r0
blr

That's just an ASM experiment, but it should run in MCM 3.2.

I have some ideas about making another 3-line syntax for action state morphing. I'd like to combine these action state change argument variables with the subaction transition concept I tried in this old experiment. No code, just a syntax:
Code:
Action Animator Event Macro
Allows the following syntax to mix action states together, and provide unique event data for the animations:
oo oo FFFF ASID FMSP STRT BLND
"DF" - 8-bit (haven't checked if this is safe yet)
"A0" - 8-bit Opcode secondary
FLAG -- 16-bit flags
ASID -- 16-bit Action State ID
FMSP -- 16-bit Frame Speed modifier
STRT -- 16-bit Starting Frame
BLND -- 16-bit Blending Frames (transition)

For the FLAG hword:
+0x80 - intact animation interrupt
+0x40 - intact IASA behavior
+0x20 - intact action physics behavior
+0x10 - intact collision interrupt
+0x08 - intact event pointer
+0x04 - transition changes air/ground flag
+0x02 - air/ground parameter for above flag (0 = ground)

The purpose of the codes are to make it possible to do the things you're asking by using the subaction event system. I don't know how it'll turn out, but if I could make it able to access the player entity in a way similar to Achilles1515 Achilles1515 's code, then it would be able to compare button data when deciding whether to make a GoTo or not.

I haven't written any of this though because I'm also interested in allowing these special comparison GoTos to reach arbitrary file data, so we could make costume-specific or stage-specific movesets--or just simple character moveset data extensions.
 
Last edited:

The Cape

Smash Master
Joined
May 16, 2004
Messages
4,478
Location
Carlisle, PA
#27
That would be some cool changes. Trying to think of a way to get this for Bowser and other similar type moves like Sheik's F smash, Link's U smash, and the like.

It would be cool to have Link's U smash be three strong hits and each one comes out when you touch the A button. Great for shield poking from below.
 

Achilles1515

Moderator
Moderator
Joined
Jun 18, 2007
Messages
3,211
Location
Cincinnati / Columbus OH
#29
I am just curious... how did you get a koopa to spawn? everytime I use the code to spawn a projectile and try to spawn a koopa, it crashes. I eventually gave up. Is there a certain velocity needed for them to function normally?
Koopa's data is in the Mushroom Kingdom Adventure stage file. So if you're not playing on that stage and try to spawn a Koopa, it will freeze.
 

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
356
#31
It's always bugged me that this code couldn't be installed as a DOL mod, so I converted it.

This can be installed via MCM without requiring the gecko code handler:
Code:
    -==-


Custom Subaction Event - Character Data Modifications
F8FFAAAA BBCCDDDD EEEEEEEE ....

AAAA = length of this custom subaction
BB = modification type
00 = byte [8-bit]
01 = half word [16-bit]
02 = word [32-bit]
03 = floating point number [32-bit]?
CC = modification operation flag
00 = overwrite
01 = AND
02 = OR
03 = add
04 = subtract
05 = multiply
06 = divide
+0x80 = multiply character facing direction
DDDD = character data offset
EEEEEEEE = new value to be placed @ character data offset
[Achilles1515]
Revision ---- DOL Offset ---- Hex to Replace ---------- ASM Code -
NTSC 1.02 --- 0x800716F8 ---- 80640008 -> Branch
80A40008 88C50001 2C0600FF 40820180 7CC802A6 90C1FFF4 8063002C A0C50002 7CE62A14 90E40008 38C6FFFC 39200008 7C864BD6 38A50004 88C50000 88E50001 A1050002 81250004 2C060000 4182001C 2C060001 41820024 2C060002 4182002C 2C060003 41820034 7D8340AE 4800009D 7D2341AE 480000FC 7D83422E 4800008D 7D23432E 480000EC 7D83402E 4800007D 7D23412E 480000DC C0250004 54E9CFFF 41820010 C003002C FC200072 38E7FF80 7D234214 C0090000 2C070000 41820044 2C070003 418000AC 4182001C 2C070004 4182001C 2C070005 4182001C 2C070006 4182001C FC21002A 48000018 FC200828 48000010 FC210032 48000008 EC200824 D0290000 4800006C 2C070000 4D820020 2C070001 4182002C 2C070002 4182002C 2C070003 4182002C 2C070004 4182002C 2C070005 4182002C 2C070006 4182002C 7D296038 4E800020 7D296378 4E800020 7D296214 4E800020 7D296050 4E800020 7D2C49D6 4E800020 7D2C4BD6 4E800020 38A50008 3884FFFF 2C040000 41A1FEBC 80C1FFF4 7CC803A6 4E800020 80640008 00000000
#
 
Last edited:
Top