(still in the works)
(sorry for posting when it's not ready yet but the "preview" button seems not to work when creating a topic)


Random Notes

When using the Axe method, you actually compile as a program then convert it into an appvar. That's why I'll say "program" and "appvar" everywhere without distinction.

If you are fluent with Asm, I'd advise you to use the Asm tutorial instead.


Notes to make your life easier

You can include the SMASHH program into your program. It only includes definitions, not code nor data so you can include it wherever you want in your program.
This will not only be a lot more readable for you to write "°AirNull" than to write "pi00000011" but this will also allow some specification changes (I hope that will never happen but we never know) because if you wrote "°AirNull" everywhere, just change the SMASHH and the °AirNull constant is changed everywhere while if you wrote "pi00000011", you'll have to change them all "by hand".

I'd also advise you to use your own macros, like "38→°FoxGravity" so that you can quickly change a constant everywhere in your program.


How do characters work, without details (you'll have details below)

Basically, they work with states. When your character is standing, doing nothing, it's in a state that we can call the "Standing state". It will leave this state when you press Jump for example, to go to the "Jumping state". Another example is the "Dashing State", that repeats itself (not entirely true) until you stop pressing the arrow key you pressed to start this state, or when you press Jump, etc.


The Header

This is necessary to have the game list your character and not list random appvars that don't have the right header.

The header starts with a one byte number, which for now is 1. This is actually not used for now, but in case specification changes happen (which I still hope will never happen), you'll have to put 2 here then 3, etc so that the program knows which specification your character is following.

You then need to put "SSBOCHAR"[00]. This is what the program uses to check if the appvar is a SSBO character or not.

The following two bytes are the size of the program.

Then, you put a 16x14 icon for the character selection menu (28 bytes).

You finish the header with the name of your character, obviously null-terminated.

For example if you are making Fox, here's what it should look like for now. I didn't say that your Axe source needs to start with a dot followed by the name of the compiled program but you must do that obviously.

Code:

.FOXP
prgmSMASHH

[]→°FoxBEGIN
[01]
"SSBOCHAR"[00]
Data(°FoxEND-°FoxBEGIN^r)
[00000000000000000000000000000000000000000000000000000000]
"Fox"[00]
[]→°FoxEND



General Information

After the header, you need to put information about your character that SSBO will needs to get quick access to. In that order:
1 byte: gravity (not used anymore, can be recycled for something else)
1 byte: air speed
1 byte: traction
1 byte: max number of jumps (2 in general, 5 for Jigglypuff)
2 bytes: max horizontal speed (not used yet)

Then follow the adresses of states that the program will need to have quick access to. A counter example is the second frame of the Down Aerial. You only access that one after the first frame. That first frame however needs to be quickly accessed to when the player asks for a Down Aerial.
All of those adresses take two bytes.
Since for now you have not written any state and have the adress of none, just put the adress of the standing state everywhere, you'll change that as you go along.

Anyway, in that order:
Standing, Airing, Dashing, GroundJump, AirJump, Landing
AirNeutralSpecial, AirSideSpecial, AirDownSpecial, AirUpSpecial
GroundNeutralSpecial, GroundSideSpecial, GroundDownSpecial, GroundUpSpecial
DashAttack
SideSmash, DownSmash, UpSmash
NeutralAir, ForwardAir, BackAir, DownAir, UpAir
LedgeGrabbed

Just a little precision. Axe compiles program "in" the RAM area it will be executed. For Asm users, I mean there is no way to say .org 0. However, state adresses need to be the ones related to the beginning of the data, not absolute ones assuming the program will be located at a precise location. This is why you'll have to put a "[]→°DB" right after the header and a "-°DB" after all your adresses (and this is why I said at the beginning that if you were fluent with Asm, you'd probably want to use your favorite assembler to build the appvar rather than using Axe).

So this is what you have for now:

Code:

.FOXP
prgmSMASHH
38→°FoxGravity

[]→°FoxBEGIN
[01]
"SSBOCHAR"[00]
Data(°FoxEND-°FoxBEGIN^r)
[00000000000000000000000000000000000000000000000000000000]
"Fox"[00]

[]→°DB
Data(°FoxGravity)
Data(5)
Data(20)
Data(2)
Data(255^r)
Data(°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r)
Data(°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r)
Data(°FoxStand-°DB^r)
Data(°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r)
Data(°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r)
Data(°FoxStand-°DB^r)

[]→°FoxStand
##that state is not done yet but since you wrote its adress above, you need that adress to exist

[]→°FoxEND
## dont forget that the °FoxEND must be the last line of your program, it's not because you wrote it at a previous step that what you add at the next step must be written after it


We now have quick access to the Standing state... which we didn't implement. That's what we are going to do.


States

States have those fields:
2x2 bytes: pointer to sprite facing right, pointer to sprite facing left
2x1 bytes: teleportation (X,Y)
1x1 byte: flags
2x2 bytes: speed
1x1 byte: arrow key influence
1x2 bytes: adress to next state by default
<depends>: commands
1x1 byte: 0, end of commands.

- I guess there's no problem with the sprite pointers, except maybe the sprite format, which is just 1 byte for the width in bytes (width in pixels divided by Cool, one byte for the height in pixels, then usual data to describe pixels, 1 bit per pixel.
- Teleportation should be straightforward too. Basically, if you want your character to have its position changing when entering that state, put non zero numbers here. Notice that those numbers will obviously be added to your current position. And since they are 1 byte numbers, you can only move from -128 to +127. This is actually not implemented yet.
- Flags change the behaviour of your state.
--- °Inv when set will grant your character invincibility during that state (not implemented)
--- °Piv when set allows you to pivot (change the direction you're facing)
--- °AbsX when set will set your X speed to the specified X speed in the speed field, and when not set will add the X speed in that field to your current X speed
--- °AbsY is obviously the same for the Y speed
--- °SetKnockBack is unused
--- °GroundNull states that when in that state you are on the ground and can't use attacks
--- °GroundOK states that when in that state you are on the ground and can use attacks
--- °GroundDash states that when in that state you are on the ground and can use dash attacks
--- °AirNull states that when in that state you are in the air and can't use attacks
--- °AirOK states that when in that state you are in the air and can use attacks
--- °Aerial states that this state is an aerial attack (don't follow the example of the Fox I made, he doesn't use that flag when he should)
--- °Ledge states that you are currently holding a ledge
- Speed is the X speed and Y speed we mentionned when talking about °AbsX and °AbsY
- The arrow key influence is a number that, when non-zero, will allow your character to be moved horizontally during that state. It is for example used in the Air (falling) state but it may be set to zero in most attacks. The higher it is, the more the character can be moved of course.
- The adress to the next state by default is pretty straightforward too
- Commands (will) have their section below in that tutorial but are not used in the standing state so first read and understand the following example

The Standing state is a very simple example (plus, it's the state you gave the adress everywhere so it would rather be defined soon).
I am throwing it without more explanation because I don't know what to say, it you have questions, feel free to ask.


Code:

.FOXP
prgmSMASHH
38→°FoxGravity

[]→°FoxBEGIN
[01]
"SSBOCHAR"[00]
Data(°FoxEND-°FoxBEGIN^r)
[00000000000000000000000000000000000000000000000000000000]
"Fox"[00]

[]→°DB
Data(°FoxGravity)
Data(5)
Data(20)
Data(2)
Data(255^r)
Data(°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r)
Data(°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r)
Data(°FoxStand-°DB^r)
Data(°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r)
Data(°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r)
Data(°FoxStand-°DB^r)

[]->°FoxStand .état debout normal
Data(°FoxStandSprR-°DB^^r,°FoxStandSpr-°DB^^r) .sprite
Data(0,0) .téléportation
Data(°Piv+°GroundOK)
Data(0^^r,0^^r) .V
Data(0) .arrowkeys influence
Data(°FoxStand-°DB^^r) .état suivant par défaut
Data(0)

[021B]->°FoxStandSpr
[004000A001300208052407142834660EF1946FF821F4DCE2A4C794CF95C84E3863F037F80FFC0F3C0B1C079603DE01CE03DE039E010C]
[021B]->°FoxStandSprR
[020005000C80104024A028E02C147066298F1FF62F84473BE325F32913A91C720FC61FEC3FF03CF038D069E07BC073807BC079C03080]

[]→°FoxEND


Notice that flags can be added in the flags field. Writing "or" instead of "+" is advised however, in case you add twice the same flag (since or_ing it twice gives the same result as or_ing it once) but "+" is more compact so I use it.
Quick question on the high-level details, since I don't understand Axe: if gravity is not used, does this mean you are not distinguishing fast-fallers?

Also, it seems that each character spends the same amount of time in each frame of the animation and does the same amount of damage / knockback?

This is understandable given the limitations of the platform, but I want to double check I understand what's going on Smile
Wait, I haven't finished at all Razz
The gravity field in the "general information" is not used, but in fact, all states have a "Y-speed" field so you'll put the gravity here (I said "speed" and not "acceleration" because that speed will either be added to the current speed or replace it). So if your character is a fast faller, put a high number "everywhere" (not exactly everywhere but you see what I mean).

And for the amount of time in each frame, I have a "repeat command" that allows you to repeat the frame for a certain amount of time (see FireFox for example, which charges for more than one frame then moves for more than one frame). That's why I said I wanted to make Fox a bit more readable because when you read, you feel like there's only a bunch of numbers but obviously all numbers have meanings (see comments).

And they don't do the same amount of damage/knockback. That's the "hitbox" command. For now, for testing purposes, all my hitboxes look quite the same (for those I implemented) but you already can see that at least their direction is not the same (the dash attack sends upwards while the upsmash sends upwards first then backwards). And obviously, don't put any hitbox in states that don't hurt.

For now, all you have done in that tutorial is write the adresses of the states, but you haven't written any state yet.
Question: How does every line work? And how do I create those one-hit KO characters Very Happy? This line in particular:


Code:
Data(°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r)


turned off my brain Smile. Also, where do graphics go (especially attacks), and how are they implemented?
Thanks in advance.
Are you fluent with Asm or Axe (one of both is enough) ?
Because it would be easier to explain if you knew at least one of those languages.

"Data(number)" is the equivalent of ".db number".
"Data(number^^r)" is the equivalent of ".dw number".
"[]->°Label" is the equivalent of "Label:"
[XXYY] were X and Y are 0-9 or A-F is the equivalent of .db $XX,$YY

Then, you can put several of those in one line. So what the line you mentionned does is just dw-ing several times the number °FoxStand-°DB. And as you can see, there is a []->°FoxStand right before the data of the standing state, and a []->°DB at the beginning of the "real" data. So °FoxStand-°DB is the label of the standing state relatively to the beginning of the "real" data. And why do we put it a bunch of times ? Because the game needs an adress to a certain number of states. But since we only made the standing state, we can't really put the adresses of all the states it needs, that's why we put the adress of the standing state everywhere, so the game doesn't crash when we try our character so far.

Graphics go... wherever you want Wink
Preferably at the end, it would be more readable for everyone and more convenient if someone ever wants to make an editor. See the last block of code I wrote (I'll paste it here because that "last block" won't apply to the same block anymore when I update the tutorial).

Hayleia wrote:


Code:

.FOXP
prgmSMASHH
38→°FoxGravity

[]→°FoxBEGIN
[01]
"SSBOCHAR"[00]
Data(°FoxEND-°FoxBEGIN^r)
[00000000000000000000000000000000000000000000000000000000]
"Fox"[00]

[]→°DB
Data(°FoxGravity)
Data(5)
Data(20)
Data(2)
Data(255^r)
Data(°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r)
Data(°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r)
Data(°FoxStand-°DB^r)
Data(°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r)
Data(°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r,°FoxStand-°DB^r)
Data(°FoxStand-°DB^r)

[]->°FoxStand .état debout normal
Data(°FoxStandSprR-°DB^^r,°FoxStandSpr-°DB^^r) .sprite
Data(0,0) .téléportation
Data(°Piv+°GroundOK)
Data(0^^r,0^^r) .V
Data(0) .arrowkeys influence
Data(°FoxStand-°DB^^r) .état suivant par défaut
Data(0)

[021B]->°FoxStandSpr
[004000A001300208052407142834660EF1946FF821F4DCE2A4C794CF95C84E3863F037F80FFC0F3C0B1C079603DE01CE03DE039E010C]
[021B]->°FoxStandSprR
[020005000C80104024A028E02C147066298F1FF62F84473BE325F32913A91C720FC61FEC3FF03CF038D069E07BC073807BC079C03080]

[]→°FoxEND


See at the very end, there is a bunch of hex. That's sprites. The format is "width in bytes, height in pixels, pixels in hex".
You can obviously write Data(2,17) instead of [021B].
And you see that a label is assigned to each sprite and used in state declarations Smile

It's probably also worth notice I use both TokenIDE and tok8x to generate code, and when I copy pasted it to the tutorial, I might not have always used the same software or the same set of tokens, hence why there are some weird things. For example, "^r" and "^^r" both represent the superscript r you can find in Angle. And "->" and "→" both represent the arrow you use to store in variables.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement