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:
set stat <name> value=<expr>— declare a counter or overwrite hardgain stat <name> amount=<expr>— augment by a signed amount (the canonical$ love += 1lowering)stats.<name>— read access inside any bounded expression (if stats.love >= 3,option ... if stats.charisma > 5)
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
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-neutraldefault love = 0
default money = 0
$ love += 1
$ money -= 50
if love >= 3:
jump emma_route_good
else:
jump emma_route_neutralWhere 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.