Underworld pages
The Underworld is the game’s second map: a column of boss pages the player opens with the round toggle button on the story map. Its pages hold single boss fights and survival gauntlets. A Power Mode checkbox on that map swaps each boss for a harder variant.
Your mod can add its own Underworld pages. They appear alongside the game’s raid
pages and use ordinary battles, fights, warriors and rewards. This page shows how
the pieces connect and documents the sf2.underworld functions. Examples assume
local sf2 = require("sf2").
Build an Underworld page
Section titled “Build an Underworld page”- Register the page with
underworld = trueinsf2.zones.register. Raid map art such asfile = "Raid1.1"fits these pages. - Register each boss as a battle on that page, usually with
type = sf2.battles.FINAL(orSURVIVALfor a gauntlet). - For a normal/Power Mode pair, register two battles at the same position with
power_mode = "normal"andpower_mode = "power". Entries withoutpower_modeare shown in both states. - Optionally give the button your own art with
icons. - Register the boss (often from a shared
sf2.warriors.register_template), its rewards (forge materials usecurrencies), and a fight connecting them.
local depths = sf2.zones.register { id = "depths", file = "Raid1.1", underworld = true }local boss_template = sf2.warriors.register_template { id = "ember_boss", template = sf2.warriors.get_template("core:warrior-templates/default"), voice = "Male", health_bars = 10, visible_health_bars = 10, skeleton = "SkeletonHeavy", items = { sf2.items.get("core:items/weapon/WEAPON_KATANA") },}local function boss(id, mode, level, bars) local battle = sf2.battles.register { id = id, zone = depths, type = sf2.battles.FINAL, x = 360, y = 820, power_mode = mode, } local warrior = sf2.warriors.register { id = id .. "_boss", template = boss_template, level = level, health_bars = bars } local loss = sf2.rewards.register { id = id .. "_loss" } local win = sf2.rewards.register { id = id .. "_win", gems = 5, currencies = { { currency = "ForgeMaterial1", expected = 40 } }, } sf2.fights.register { id = id .. "_fight", battle = battle, rounds = 1, round_time = 300, warriors = { warrior }, rewards = { loss, win }, } return battleendlocal ember = boss("ember", "normal", 40, 10)local ember_power = boss("ember_power", "power", 60, 20)The boss’s raid shield is health_bars normal health bars drawn as
visible_health_bars stacked bars (raid shields).
Both inherit from the template separately. Here the Power Mode boss raises the
shield to 20 and keeps the template’s 10 bars, so each bar holds two normal health
bars, which is how the original game’s Power Mode bosses are built.
For a fight in darkness, register
sf2.rules.light_in_the_darkness
and include its handle in the fight’s rules array. Its spotlight follows the
player unless you select the opponent. The rule changes visibility only.
Registering a battle does not reveal it in an existing save. Reveal entries with
sf2.battles.reveal or a quest, exactly as
on the story map.
Control the toggle and the focus
Section titled “Control the toggle and the focus”The story map shows the Underworld toggle by default. A mod that wants the Underworld to be discovered later can hide the toggle until its story reveals it, and can choose which boss the Underworld map opens on.
sf2.underworld.set_toggle_visible
Section titled “sf2.underworld.set_toggle_visible”Show or hide the map’s Underworld toggle button.
Signature: sf2.underworld.set_toggle_visible(visible) -> boolean
Returns: true when the current map applied the change; false when the
map is not ready: outside the map scene, during a scene or profile change, while
an encounter is being prepared, or while native input is blocked (for example by
a lock screen).
When: In a callback while the map scene is open, such as a scene_enter
event for "map" or a story dialog’s on_complete. Calls from UI cleanup
callbacks raise an error.
Requires: story.progression and a strict boolean.
sf2.story.on("scene_enter", function(event) if event.scene == "map" and not sf2.state.get("depths_found") then sf2.underworld.set_toggle_visible(false) endend)The change lasts until the map is rebuilt: the next time the map scene opens,
the toggle is visible again unless you hide it again. Hide it on every
scene_enter for "map" while it should stay hidden, and store your own
“revealed” flag in mod state. Hiding the toggle does not lock
any battle, and it also hides the way to the game’s own raid pages.
sf2.underworld.set_focus
Section titled “sf2.underworld.set_focus”Choose the boss the Underworld map shows when the player next opens it.
Signature: sf2.underworld.set_focus(battle) -> boolean
Returns: true when the focus was stored; false when no profile is loaded,
a profile change is in progress, or the battle is not on one of your
Underworld pages.
When: A callback after the profile has loaded, typically right before you show the toggle.
Requires: story.progression and a battle handle registered by this mod on a
zone with underworld = true. Strings, core battles and other mods’ battles are
rejected.
-- ember was registered on an Underworld page during loading.if sf2.underworld.set_toggle_visible(true) then sf2.underworld.set_focus(ember)endThis stores the saved Underworld focus; it does not switch maps, reveal or unlock
the battle, or select it on the story map. Use
sf2.battles.focus to select an entry that
is already visible on the current map.
sf2.underworld.set_map_colors
Section titled “sf2.underworld.set_map_colors”Choose the Underworld background tint for normal and Power Mode. Clicking Power Mode blends between these colors without changing battle rules or the story map.
Signature: sf2.underworld.set_map_colors { normal, power, duration? } -> boolean
Returns: true when stored on the current map; false when the map is not
ready, including scene/profile transitions, pending encounters and native input locks.
When: In a callback on the map, usually scene_enter. UI cleanup calls raise
an error. The colors last until the map scene is rebuilt; reapply them on entry.
If several callbacks set them, the last successful call wins.
Requires: story.progression. Required normal and power are hex colors
in #RRGGBB or #RRGGBBAA form. duration is a finite number from 0 to 5 seconds,
default 0.8; zero changes immediately. Colors multiply the map artwork, so
#FFFFFF preserves its original colors. Omitted alpha is opaque.
sf2.story.on("scene_enter", function(event) if event.scene == "map" then sf2.underworld.set_map_colors { normal = "#FFFFFF", power = "#BA8A82", duration = 0.8, } endend)The setting can be made while the story map is visible; it takes effect when the Underworld opens. It is presentation state, is not written to a save, and does not toggle Power Mode or recolor its checkbox. Both colors default to white.
Tell the story
Section titled “Tell the story”Boss introductions and after-fight lines use the regular story tools:
sf2.story.before_fightholds the fight while a sequence ofsf2.ui.story_dialogcards plays, and starts it from the last card’s button.- The
battle_resultevent reports"win"or"loss"(a surrender is reported separately as"surrender"), so a mod can queue a one-time line and show it on the nextscene_enterfor"map". sf2.ui.act_screenshows full-screen chapter text.
Limits
Section titled “Limits”- Only zones registered by your mod can carry
underworld = true; core raid pages keep their own content. - Power Mode filtering applies only on Underworld pages.
- Map buttons need their own sprites for both the normal and selected states;
locked art falls back to the game’s lock icon when
lockedis omitted. - The Underworld functions change map presentation only. They do not grant rewards, reset attempts, or change the game’s online raid services.