Expose a character-specific number through a SlugBase feature when a character author should choose it in JSON. Use Remix settings for a player’s preferences and save data for progress earned during play.
Register a typed key
The following members belong inside a plugin that already references SlugBase and declares its dependency. They are integration fragments, not a complete plugin or a compiled example.
private static SlugBase.Features.PlayerFeature<float> pushStrength;
private static void RegisterFeatures()
{
if (pushStrength == null)
pushStrength = SlugBase.Features.FeatureTypes.PlayerFloat(
"example.lanternwren/push_strength");
}Call RegisterFeatures() from the plugin’s OnEnable before SlugBase scans character JSON in its PostModsInit hook. Keep ordinary hook attachment and removal in the same plugin’s lifecycle. A null guard makes this registration repeatable without constructing another feature object.
Feature construction registers the key with FeatureManager. The prefix distinguishes it from keys supplied by other mods. Duplicate keys are reported by the inspected registration code, which retains the first registered feature. Constructing a feature every time Player.Update runs adds repeated registration attempts and can leave lookups using a different feature object.
Put the value in a character
Merge this entry into the features object of a working character definition:
{
"example.lanternwren/push_strength": 3.5
}This fragment is the contents of features, not the complete character file. The key is case-sensitive in the feature dictionary. A float feature expects a JSON number. A quoted string such as "3.5" is a different JSON type.
Read it for the current player
private static float GetPushStrength(Player player)
{
float strength;
if (player == null || pushStrength == null ||
!pushStrength.TryGet(player, out strength))
return 0f;
if (float.IsNaN(strength) || float.IsInfinity(strength))
return 0f;
return UnityEngine.Mathf.Clamp(strength, 0f, 10f);
}Call the helper at the point where the ability needs its parameter. The zero fallback disables this example’s impulse for characters without the feature. The range is a design choice for this recipe, not a SlugBase limit. Receiving a parsed number does not establish that it is suitable for the mechanic.
PlayerFeature<T>.TryGet resolves the character through player.SlugCatClass. A GameFeature<T> resolves the story character through game.StoryCharacter. In a session with different player characters, those can identify different definitions. Use the player feature for an individual’s ability and a game feature for a campaign-wide rule. Check co-op compatibility separately.
Keep mutable state elsewhere
The feature supplies a definition value. A cooldown, remaining charge, or last input state belongs to the individual player’s runtime state. A saved unlock belongs to a persistence layer with a save policy. Changing a local variable returned by TryGet does not update the character JSON or save a new value.
Test the feature with a character that defines it, one that omits it, and an invalid JSON type. Confirm that malformed data produces a useful error and that the ordinary character remains playable. See configurable abilities for combining a user-facing option with a mechanic.
Sources
- Feature construction and character lookup.
- Typed feature factories and registration dictionary.
- SlugBase initialization and JSON scanning. Source checked at commit
dac3433, with no game session performed for these fragments.