RoA Workshop Guide – Coding Recovery Moves Pt. 1 (The Basics)

Making a custom recovery move can be a steep hurdle for new creators making their first Workshop character. Sandbert and Guadua’s Up-Specials are not very beginner-friendly, and designing one from scratch can seem daunting. However, a simple recovery is not too difficult to make at all – in fact, no real code knowledge is needed at all! The first in this series of guides will teach how to make the simplest recovery move possible from scratch.

This guide will assume you’re familiar with using Grid Indexes to make other basic attacks. If you need a refresher or are unsure, see the following two guides first: Explaining Attack Windows | Explaining Hitboxes

If you’re looking for advice on how to ‘design’ a recovery instead, consider checking my earlier guide: Recovery Guidelines

Clean out your template

If you’re using Sandbert or another mod as a template, there is a good chance it has custom scripting for its Up-Special in attack_update.gml. Since we won’t need it any more, find the correct ‘if’ statement and remove it (or comment it out if you wish to reference it later).

//attack_update.gml
if (attack == AT_USPECIAL){
    //you won't need any of this sandbert code for a simple recovery.
	
    // if (window == 1 && window_timer == 1){
    //     times_through = 0;
    // }
    // etc.
}

Set up a basic attack using Grid Indexes

Make a basic attack. There’s no strict requirements for this, but I would recommend using at least three Windows.

In the example below, we’ll have a ‘startup’ window, a ‘rising’ window with a decent length, and then an endlag window. We’ll then add a hibox to the ‘rising’ window.

Make sure to set AG_CATEGORY to 2 (so that the attack can be used both on the ground and in the air), and AG_OFF_LEDGE to 1 (so that the attack doesn’t get ‘stuck’ on ledges while rising off them). You can find descriptions for all of the Grid Indexes on the official Attack Grid Indexes page.

//attacks/uspecial.gml
//a simple 3-window attack
set_attack_value(AT_USPECIAL, AG_CATEGORY, 2);
set_attack_value(AT_USPECIAL, AG_SPRITE, sprite_get("uspecial"));
set_attack_value(AT_USPECIAL, AG_NUM_WINDOWS, 3);
set_attack_value(AT_USPECIAL, AG_OFF_LEDGE, 1);
set_attack_value(AT_USPECIAL, AG_AIR_SPRITE, sprite_get("uspecial"));
set_attack_value(AT_USPECIAL, AG_HURTBOX_SPRITE, sprite_get("uspecial_hurt"));

set_window_value(AT_USPECIAL, 1, AG_WINDOW_LENGTH, 12);
set_window_value(AT_USPECIAL, 1, AG_WINDOW_ANIM_FRAMES, 2);

set_window_value(AT_USPECIAL, 2, AG_WINDOW_LENGTH, 12);
set_window_value(AT_USPECIAL, 2, AG_WINDOW_ANIM_FRAMES, 3);
set_window_value(AT_USPECIAL, 2, AG_WINDOW_ANIM_FRAME_START, 2);

set_window_value(AT_USPECIAL, 3, AG_WINDOW_LENGTH, 10);
set_window_value(AT_USPECIAL, 3, AG_WINDOW_ANIM_FRAMES, 2);
set_window_value(AT_USPECIAL, 3, AG_WINDOW_ANIM_FRAME_START, 5);

//with a simple hitbox on the 2nd window
set_num_hitboxes(AT_USPECIAL, 1);

set_hitbox_value(AT_USPECIAL, 1, HG_WINDOW, 2);
set_hitbox_value(AT_USPECIAL, 1, HG_LIFETIME, 12);
set_hitbox_value(AT_USPECIAL, 1, HG_HITBOX_X, 30);
set_hitbox_value(AT_USPECIAL, 1, HG_HITBOX_Y, -30);
set_hitbox_value(AT_USPECIAL, 1, HG_PRIORITY, 3);
set_hitbox_value(AT_USPECIAL, 1, HG_PRIORITY, 5);
set_hitbox_value(AT_USPECIAL, 1, HG_HITBOX_GROUP, 1);
set_hitbox_value(AT_USPECIAL, 1, HG_WIDTH, 60);
set_hitbox_value(AT_USPECIAL, 1, HG_HEIGHT, 60);
set_hitbox_value(AT_USPECIAL, 1, HG_DAMAGE, 10);
set_hitbox_value(AT_USPECIAL, 1, HG_ANGLE, 90);
set_hitbox_value(AT_USPECIAL, 1, HG_BASE_KNOCKBACK, 8);
set_hitbox_value(AT_USPECIAL, 1, HG_FINAL_KNOCKBACK, 5);
set_hitbox_value(AT_USPECIAL, 1, HG_KNOCKBACK_SCALING, 0.75);
set_hitbox_value(AT_USPECIAL, 1, HG_BASE_HITPAUSE, 9);
set_hitbox_value(AT_USPECIAL, 1, HG_HITPAUSE_SCALING, 0.7);
set_hitbox_value(AT_USPECIAL, 1, HG_HIT_SFX, asset_get("sfx_blow_medium1"));

Make the Attack Rise

For the ‘rising’ window, we will need to apply a few more Grid Indexes, so that the character begins moving upwards during that window. The grid index AG_WINDOW_VSPEED sets the amount of vertical speed on a window, and AG_WINDOW_VSPEED_TYPE specifies how that vertical speed is applied.

Setting AG_WINDOW_VSPEED_TYPE to 1 will set the character’s vertical speed on every frame of the window. VSPEED_TYPE 2 will set the character’s vertical speed only at the start of the window. For a smooth, linear recovery, choose 1; for a ‘jumping’ recovery that is affected by gravity, choose 2.

Note: VSPEED_TYPE 0 is the default, which ‘adds’ onto the character’s current speed instead of setting it to a fixed number. Make sure you set AG_WINDOW_VSPEED_TYPE to 1 or 2 to avoid any unintented results.

set_window_value(AT_USPECIAL, 2, AG_WINDOW_LENGTH, 12);
set_window_value(AT_USPECIAL, 2, AG_WINDOW_ANIM_FRAMES, 3);
set_window_value(AT_USPECIAL, 2, AG_WINDOW_ANIM_FRAME_START, 2);
set_window_value(AT_USPECIAL, 2, AG_WINDOW_VSPEED, -8);
set_window_value(AT_USPECIAL, 2, AG_WINDOW_VSPEED_TYPE, 1);

Add Pratfall

In Rivals, Smash Bros and other platform fighters, a character will fall helplessly after using an Up-Special move, recovering only when they touch the ground. This state is known as ‘Pratfall’ or ‘Special Fall’. Although not every recovery move uses pratfall, it is the most expected way to make a recovery move work.

The Grid Index ‘AG_WINDOW_TYPE’ has a few settings for changing how an attack acts upon reaching the end of a window. Setting AG_WINDOW_TYPE to 7 will cause the move to enter pratfall. If you’re curious, you can learn about the other ‘window types’ on the Attack Grid Indexes webpage.

set_window_value(AT_USPECIAL, 3, AG_WINDOW_TYPE, 7);
set_window_value(AT_USPECIAL, 3, AG_WINDOW_LENGTH, 10);
set_window_value(AT_USPECIAL, 3, AG_WINDOW_ANIM_FRAMES, 2);
set_window_value(AT_USPECIAL, 3, AG_WINDOW_ANIM_FRAME_START, 5);

Final Touches

With that, you should now have a functioning recovery move! Tweak and experiment with it as much as necessary. You can try making your recovery move diagonally by adding the grid indexes AG_WINDOW_HSPEED and AG_WINDOW_HSPEED_TYPE, or add extra windows for more complex motions and timing, or add extra indexes to the first window so that the character stops moving. Subtle differences can make all the difference.

//the complete attacks/uspecial.gml example script
set_attack_value(AT_USPECIAL, AG_CATEGORY, 2);
set_attack_value(AT_USPECIAL, AG_SPRITE, sprite_get("uspecial"));
set_attack_value(AT_USPECIAL, AG_NUM_WINDOWS, 3);
set_attack_value(AT_USPECIAL, AG_OFF_LEDGE, 1);
set_attack_value(AT_USPECIAL, AG_AIR_SPRITE, sprite_get("uspecial"));
set_attack_value(AT_USPECIAL, AG_HURTBOX_SPRITE, sprite_get("uspecial_hurt"));

//startup
set_window_value(AT_USPECIAL, 1, AG_WINDOW_LENGTH, 12);
set_window_value(AT_USPECIAL, 1, AG_WINDOW_ANIM_FRAMES, 2);

//'rising' / active frames
set_window_value(AT_USPECIAL, 2, AG_WINDOW_LENGTH, 12);
set_window_value(AT_USPECIAL, 2, AG_WINDOW_ANIM_FRAMES, 3);
set_window_value(AT_USPECIAL, 2, AG_WINDOW_ANIM_FRAME_START, 2);

//endlag
set_window_value(AT_USPECIAL, 3, AG_WINDOW_TYPE, 7);
set_window_value(AT_USPECIAL, 3, AG_WINDOW_LENGTH, 10);
set_window_value(AT_USPECIAL, 3, AG_WINDOW_ANIM_FRAMES, 2);
set_window_value(AT_USPECIAL, 3, AG_WINDOW_ANIM_FRAME_START, 5);


set_num_hitboxes(AT_USPECIAL, 1);

//'rising' hitbox
set_hitbox_value(AT_USPECIAL, 1, HG_LIFETIME, 12);
set_hitbox_value(AT_USPECIAL, 1, HG_HITBOX_X, 30);
set_hitbox_value(AT_USPECIAL, 1, HG_HITBOX_Y, -30);
set_hitbox_value(AT_USPECIAL, 1, HG_PRIORITY, 5);
set_hitbox_value(AT_USPECIAL, 1, HG_HITBOX_GROUP, 1);
set_hitbox_value(AT_USPECIAL, 1, HG_WIDTH, 60);
set_hitbox_value(AT_USPECIAL, 1, HG_HEIGHT, 60);
set_hitbox_value(AT_USPECIAL, 1, HG_DAMAGE, 10);
set_hitbox_value(AT_USPECIAL, 1, HG_ANGLE, 90);
set_hitbox_value(AT_USPECIAL, 1, HG_BASE_KNOCKBACK, 8);
set_hitbox_value(AT_USPECIAL, 1, HG_FINAL_KNOCKBACK, 5);
set_hitbox_value(AT_USPECIAL, 1, HG_KNOCKBACK_SCALING, 0.75);
set_hitbox_value(AT_USPECIAL, 1, HG_BASE_HITPAUSE, 9);
set_hitbox_value(AT_USPECIAL, 1, HG_HITPAUSE_SCALING, 0.7);
set_hitbox_value(AT_USPECIAL, 1, HG_HIT_SFX, asset_get("sfx_blow_medium1"));

Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *