Skip to content

Perks and enchantments

A perk is an ability definition used by learned player abilities or opponent loadouts. An enchantment is an equipment effect available through a forge recipe. Both can reuse the same behavior with different parameter values.

For native template-backed attribute modifiers, expiry removes the normalized amount originally applied. It does not reevaluate the attribute expression against later combat state. For example, a temporary +12 modifier removes 12 even if its expression would now evaluate to 30; unrelated attribute changes remain intact. This bookkeeping does not provide runtime character-form switching.

The examples below assume sf2 is loaded, the localization keys exist, and opening_charge is the behavior created in Reusable behaviors.

Look up an existing perk definition.

Signature: sf2.perks.get(reference)

Requires: content.register; a dependency on another owner when applicable.

When: Entrypoint.

Returns: A perk handle. Missing or inaccessible definitions raise errors.

local lifesteal = sf2.perks.get("core:perks/PERK_ITEM_SPECIAL_LIFESTEAL_WEAPON")

Use the complete perk definition ID. The returned handle is useful in opponent loadouts, fight rules, and forge candidates.

Register a new perk backed by your own Lua behavior.

Signature: sf2.perks.register(definition)

Requires: content.register, plus the capabilities used by its behavior.

When: Entrypoint, after the behavior it uses is available.

Returns: A perk handle. Registration does not automatically teach it to the player.

Field Type Requirement/default
id String Required local ID.
display_name Localization handle Required.
description Localization handle Required.
icon Sprite handle Optional.
behavior Behavior handle Required.
kind Perk constant Required: sf2.perks.SINGLE or sf2.perks.COMBO.
parameters Typed value table Optional; must satisfy the behavior schema.
upgrades Dense upgrade array Optional; 1–100 contiguous levels starting at 1.
initial_upgrade Integer Optional, default 0. Requires upgrades; must be from 0 through the declared upgrade count.
local focus = sf2.perks.register {
id = "opening_focus",
display_name = sf2.localization.key("perk.opening_charge"),
description = sf2.localization.key("perk.opening_charge.description"),
kind = sf2.perks.SINGLE,
behavior = opening_charge,
parameters = { amount = 0.35 },
}

To make it active, add it to an opponent’s perks, a supported fight rule, or a perk-tree choice. Player callbacks run only for learned, active perks after normal rule filtering.

Perk registration accepts upgrades, a dense array of 1–100 tables: { level, description?, parameters? }. Levels must be ordered and contiguous starting at 1. Level 0 uses the base perk. Omitted descriptions use the base description; supplied descriptions must be localization handles owned by this mod. An upgrade’s parameters override the base independently, not the previous upgrade. Parameters use the behavior schema.

initial_upgrade controls which declared rank is projected as the native baseline for a newly registered learned perk. 0 keeps the base perk plus all declared upgrade variants. A value from 1 through the upgrade count selects that rank as the initial native variant and omits the lower baseline/variants from the native projection. It does not rewrite existing saved learned-perk ranks; saved UpgradeLevel values keep their normal meaning.

-- Fields to include in sf2.perks.register; Drain must be in the behavior schema.
upgrades = {
{ level = 1, parameters = { Drain = 0.02 } },
{ level = 2, parameters = { Drain = 0.04 } },
}

Register unlock/upgrade choices separately with sf2.progression.replace_perk_branch. These entries do not change XP, currency, upgrade costs or the global progression curve. Native progression variants supply the selected description. Learned Lua perks use the saved UpgradeLevel to apply parameter overrides at callback time. Saved parameter records and behavior state are preserved; the effective map is a fresh copy. Unknown levels beyond the registered upgrades and malformed saved levels report an error without modifying the save. Perks without upgrade entries retain their existing parameter behavior.

The complete Mods/example.perk-upgrades mod provides a learned guard with three upgrades, localized descriptions, and level 2–5 branch choices. Its managed tests exercise actual Lua damage callbacks across save/reload and context recreation; full Unity profile selection and encounter validation remains pending.

Equipment and warrior callbacks continue to use base parameters; they do not inherit the player’s learned upgrade level. Disabling a mod removes its native variants and leaves its saved learned perk data intact. Learned behaviors may use the documented style, combo and hit-phase callbacks when their archived behavior depends on those native events.

Show whether a perk’s ability is ready on the fight HUD, for every fighter that carries the perk.

Signature: sf2.perks.recharge_indicator { perk, flags, ready, cooldown }

Requires: content.register.

When: Entrypoint. The indicator applies in every fight while the mod is enabled.

Returns: nil.

Field Type Requirement/default
perk Perk handle Required. A core perk (from sf2.perks.get) or a perk your mod or a dependency registered. One indicator per perk across all enabled mods.
flags String array Required, 1–8 exact flag names. The ability is recharging while any of them is active on the fighter.
ready Sprite handle Required. Shown while no listed flag is active.
cooldown Sprite handle Required. Shown while a listed flag is active.

A flag is a named combat modifier on a fighter, the same thing a move’s not_mod condition checks. Two kinds can be listed:

  • A core perk’s own flag, such as "HermitStormRecharge" from PERK_HERMITSTORM (its <Set FlagName> in the game’s perks.xml).
  • A flag set by a Lua behavior with fighter:set_flag, written with its full name "<mod id>:behaviors/<behavior id>:<key>".

While a fighter carries perk, its status-icon row shows exactly one of the two icons. The ready icon has no timer and stays until the state changes. The cooldown icon shows the native expiration sweep of the listed flag that ends last. A flag set without a duration has no sweep. “Carries” means the perk is active for that fighter after equipment, enchantment-set and rule filtering: an opponent’s warrior perks, or the player’s learned perks, item perks and complete combo enchantment.

local storm = sf2.perks.get("core:perks/PERK_HERMITSTORM")
sf2.perks.recharge_indicator {
perk = storm,
flags = { "HermitStormRecharge" },
ready = sf2.assets.sprite("sprites/abilities/hermit_storm"),
cooldown = sf2.assets.sprite("sprites/abilities/hermit_storm_cooldown"),
}

For a perk with its own Lua recharge, give the flag a duration so the sweep follows it:

local recharge = sf2.behaviors.register {
id = "storm_recharge",
on_animation_start = function(parameters, fighter, event)
if event.target == "self" and event.animation_name == sf2.mod.id .. ":moves/storm" then
fighter:clear_flag("cooldown")
fighter:set_flag("cooldown", 600)
end
end,
}
local storm = sf2.perks.register {
id = "storm", kind = sf2.perks.SINGLE, behavior = recharge,
display_name = sf2.localization.key("perk.storm"),
description = sf2.localization.key("perk.storm.description"),
}
sf2.perks.recharge_indicator {
perk = storm,
flags = { sf2.mod.id .. ":behaviors/storm_recharge:cooldown" },
ready = sf2.assets.sprite("sprites/storm"),
cooldown = sf2.assets.sprite("sprites/storm_cooldown"),
}

The indicator is presentation only. It never blocks a move: gate the move itself with a not_mod condition on the same flag. Icons are drawn at the sprite’s native size, so set pixels_per_unit in its descriptor to size it. Icons update only while a round is running and are removed when the fight ends.

Create a forgeable enchantment for selected equipment categories.

Signature: sf2.enchantments.register(definition)

Requires: content.register, plus the capabilities used by its behavior.

When: Entrypoint, after registering its behavior.

Returns: An enchantment handle. The player still needs to forge and equip it.

Field Type Requirement/default
id String Required local ID.
recipe Recipe constant Required: SIMPLE, MEDIUM, or COMPLEX from sf2.enchantments.
item_types Equipment constant array Required nonempty list; no duplicates.
behavior Behavior handle Required.
display_name, description Localization handles Required.
icon Sprite handle Optional.
parameters Typed value table Optional; must satisfy its schema.
local enchantment = sf2.enchantments.register {
id = "charged_weapon",
display_name = sf2.localization.key("perk.opening_charge"),
description = sf2.localization.key("perk.opening_charge.description"),
behavior = opening_charge,
parameters = { amount = 0.35 },
recipe = sf2.enchantments.SIMPLE,
item_types = { sf2.enchantments.WEAPON },
}

Other equipment constants are ARMOR, HELM, RANGED, and MAGIC in the same namespace.

Recipe selection uses the host’s forge economy. These fields do not customize shared prices, currencies, or upgrade formulas.