Skip to content

Status Scripts

FaultyPine edited this page Apr 17, 2021 · 4 revisions

What are they?

Status scripts are typically much more complicated than ACMD scripts, and for good reason. These scripts could be considered the "brains" behind every move -- how and when to cancel the move, when to set/unset flags, etc. It's all managed by status script logic. Some exceptions are that a few of the more "under the hood" mechanics, like the calculations the game performs to determine if you should snap to ledge, aren't handled in status scripts.

Status scripts can fall into 11 different categories that the game uses, with 5 categories that have gone unused:

  • STATUS_PRE: These statuses set up the fighter for execution in the main status script. They usually reset flags and memory that the fighter is going to be needing, and also setting up the specific flags about how the fighter should interact with other agents during the execution of the status.
  • STATUS_MAIN: The main logic of the status. It runs once per frame. Most statuses have some initialization code that they run once, and then shift into different "main loop"-esque subroutine(s).
  • STATUS_END: These statuses will clean up any remaining bits from the main status, usually turning off flags and what not.
  • INIT_STATUS: Similar to STATUS_PRE, except this is more for status specific things. You might notice these statuses turning on some flags or resetting variables that may have lingered across statuses.
  • EXEC_STATUS: The exec status also runs once per frame, and typically performs the calculations that the main status uses to perform it's logic. In both this and the main status is where the majority of the "brains" are stored.
  • EXIT_STATUS: Similar to STATUS_END, except it is for more status specific things (like INIT_STATUS).

The other categories are usually either unused or not important for mod developers.

How do I get them?

Unfortunately, status scripts are both the more powerful of the two kinds of scripts but also the harder to obtain. Currently, the only methods to get status scripts are to either dump them using nrooooooo, which hasn't been updated since Smash version 8.1.0, or to use Ghidra and open the fighter plugin and attempt to read the pseudo-c decompilation.

Setting up Ghidra is pretty simple. After downloading it from the hyperlink above, install adubbz's ghidra loader plugin for switch binaries for the appropriate version of ghidra you are running. Then drag in your dumped copy of any of smash's executables. This could be main (obtained by dumping ExeFS with a homebrew app called nxdumptool, or it could be any of smash's .nro files. Those are obtained by dumping smash's RomFS with nxdumptool, and opening the data.arc file with a data.arc parser program like ARCExplorer. Then dump the files in prebuilt;/release/nro.

While status scripts are definitely more powerful, they aren't necessary to create awesome mods. There is another option, once-per-frame functions, which are covered later in this Wiki. Moving forward, this page assumes that you understand how to get status scripts and that you could get them if you wanted to.

How do I edit them?

Smashline provides the status_script macro to generate code compatible with the game's status system.

use smash::lua2cpp::L2CWeaponCommon;
use smashline::*;

#[status_script(agent = "snake_trenchmortarbullet", status = WEAPON_SNAKE_TRENCHMORTAR_BULLET_STATUS_KIND_FLY, condition = LUA_FUNC_STATUS_FUNC_STATUS_MAIN)]
pub fn snake_tm_bullet_fly(weapon: &mut L2CWeaponCommon) -> L2CValue {
  println!("Snake Trenchmortar Bullet Fly Main");
  original!(weapon)
}

Similar to the acmd_script macro, this macro also accepts an agent argument. While not shown here, the status_script macro also accepts the optional low_priority argument. The status and condition arguments are both LuaConst names, which can be found here in the skyline-smash repository. In the case that you don't know the name of the LuaConst, passing the int will work just fine.

// same effect as above
#[status_script(agent = "snake_trenchmortarbullet", status = 0x1, condition = LUA_FUNC_STATUS_FUNC_STATUS_MAIN)]
pub fn snake_tm_bullet_fly(weapon: &mut L2CWeaponCommon) -> L2CValue {
  println!("Snake Trenchmortar Bullet Fly Main");
  original!(weapon)
}

original! macro for status scripts

The original! macro, unlike the one provided for ACMD scripts, works exactly like skyline hooks macro. Since these functions get called once per frame, they don't call any functions which wait and could cause issues that ACMD scripts can cause.

Using the original! macro for the main status script can be problematic, however, as most main status scripts perform initialization code and then change to a new main function that does not get intercepted by Smashline.

Common status scripts

Lots of status scripts in Ultimate are shared between fighters. For example, basically every character has the same Jump, JumpSquat, Damage, Shield, etc. status scripts. Generally, if the name of the status const doesn't specify a specific fighter's name, you can assume it's a common status. For this reason, Smashline also provides a helper macro to handle these cases

#[common_status_script(status = FIGHTER_STATUS_KIND_JUMP, condition = LUA_SCRIPT_STATUS_FUNC_STATUS_MAIN)]
pub fn common_jump(fighter: &mut L2CFighterCommon) -> L2CValue {
  println!("Common jump");
  call_original!(fighter)
}

Common status scripts take the original! and call_original! syntax from Smashline's symbol hooking code, since there is an optional symbol argument for the macro.

#[common_status_script(status = FIGHTER_STATUS_KIND_JUMP, condition = LUA_SCRIPT_STATUS_FUNC_STATUS_MAIN, symbol = "_ZN7lua2cpp16L2CFighterCommon11status_JumpEv")]
pub fn common_jump(fighter: &mut L2CFighterCommon) -> L2CValue {
  println!("Common jump");
  call_original!(fighter)
}

The symbol argument for the macro is the same thing you would pass to the symbol hook macro. It is here as well since sometimes fighters will change the status script a little bit and then call the original one (similar to what we are doing!). They call the original status through the symbol, so if we replace the symbol it should also impact those derivative statuses!