FolioTier 2

stats

Typed numeric counters — affection, money, strength. First subsystem; the canonical `$ love += 1` idiom from Tier 2 corpus.

Stats is Folio's typed numeric-counter subsystem — the right home for affection, money, strength, exhaustion, per-character relationship trackers. Two verbs and one expression accessor cover the surface:

The corpus signal is the canonical case for shipping a subsystem rather than a grammar extension: $ love += 1 and $ love -= 1 appear 700+ times in Acting Lessons, 580+ in Eternum, and 270+ in What a Legend. That's the dominant idiom in Tier 2-3 games — large enough that "just use variables" stops scaling for the editor sidebar, the AI importer pass, and the Aldus authoring tool surface.

Canonical example

Folio
set stat love value=0
set stat money value=0

gain stat love amount=1
gain stat money amount=-50

if stats.love >= 3 -> emma-route-good else -> emma-route-neutral
Ren'Py
default love = 0
default money = 0

$ love += 1
$ money -= 50

if love >= 3:
    jump emma_route_good
else:
    jump emma_route_neutral

Where stats live

Stats live in a typed slot inside the canonical project (RuntimeSaveState.subsystems.stats per Phase 6 Track A pre-flight). That means saves carry them, the migrator pattern can evolve them, the editor surface can render them as a typed structure rather than parsing bare variables out of set lines, and the AI importer pass can lower default <name> = N declarations directly into typed subsystems.stats slots.

The shape is flat: Record<string, number>. Per-character relationship trackers (RPmegan, dylan, ellie) and project-wide scalars (money, health, charisma) share the same map. v1 ships one Stats subsystem covering both shapes; a future Relationships subsystem can extract per-character state without re-architecting if post-v1 tier-4 games demand it.

Trenchcoat split — scalar vs. relationship trackers

Per Phase 6 § Per-subsystem checklist § Trenchcoat split check, Acting Lessons forces a design question: are RPmelissa, RPanhw, RPrena Stats or a separate Relationships subsystem? Mechanically they're Stats — per-character scalar counters that gate dialogue. Naming them relationship.<character> instead of stats.<character> opens the door to character-specific machinery (history, decay, bidirectional state) that v1 doesn't need and the corpus doesn't demonstrably want.

The build-time call (Phase 6 § Track A v1 build order, Stats step): ship one Stats subsystem that covers both shapes, with the schema slot pre-named so relationship state can migrate into a dedicated Relationships subsystem post-v1 without re-architecting.

Wallet-shape checkpoint (G6.4)

The Stats verb signature effectively pre-decides Wallet's shape (Tier 4, post-v1). gain stat money amount=-50 and gain stat love amount=1 use the same idiom; if Wallet later wants spend amount=50 instead, that's a fork at Tier-4 build time that compounds.

Per G6.4 (dormant for v1), the Stats verb-naming step included a design checkpoint with the monetization owner. Four agreement points were locked: signed amount covers += -= rather than introducing a lose / spend verb family; stats can go negative by default with optional min: / max: clamps as future slots; transaction logging is post-v1 (the existing aldus_usage audit-log primitive is the analog Wallet would extend); read accessor uniformity means stats.<name> works everywhere.

Importer translation

Ren'Py expresses stats as bare Python variables manipulated through $ lines:

default love = 0          # declaration
$ love += 1               # increment
$ love -= 1               # decrement
if love >= 3:             # condition
    jump emma_route

The importer post-pass lowers default <name> = <num> to set stat <name> value=<num> (declaring the stat in subsystems.stats), rewrites set steps targeting numeric variables to set-stat steps, and rewrites conditions that read those names to stats.<name> expressions. Augmented assignment ($ <name> += N) is not currently lowered by the legacy pipeline — that's a Phase 6 Track B deterministic-pass mapping; until B ships, augmented forms surface as inline-Python issues for manual review.

See also

  • set stat — declaration / hard overwrite
  • gain stat — augmented assignment
  • if — read stats.<name> through the bounded expression grammar
  • variables — non-numeric flags and the broader expression grammar