Skip to content

Unique scenes cookbook

Four recipes for authoring scenes that reflect a girl’s personality, her current situation, and (where one specific girl is involved) her name. All examples use real, shipped syntax — compare against resources/Jobs/ and resources/Packages/Sample_Full/.

For the complete predicate list, see reference/when-conditions.md. For full job structure see reference/jobs-reference.md.

Scope. This cookbook covers what’s pack-authorable today — XML + the <When> engine, plus optional per-girl Lua overrides. Long arcs (“after 30 in-game days…”) aren’t pack-authorable yet; see “Known gap” at the bottom.


When to use this shape: you want a job to read differently depending on the girl’s traits or stats. A Shy girl bartending should sound different from a Charming one. The base text bag still fires for everyone else.

Exemplar: resources/Jobs/barmaid/messages/work.xml

Inside any text bag, attach a <When> block. Variants with a matching <When> are eligible alongside the unconditional ones; the engine picks among the eligible bag entries by weight=.

<Text id="barmaid.work.perfect.psychic" weight="3">
<When>
<Performance ge="245"/>
<Trait id="Psychic"/>
</When>
</Text>
<Text id="barmaid.work.great.beauty" weight="3">
<When>
<Performance ge="185" le="244"/>
<Stat name="Beauty" ge="85"/>
</When>
</Text>

A bare <When> with multiple children means all must match (implicit AND). The first variant fires only when the girl scored a “perfect” shift and has the Psychic trait. The second wants a “great” shift and Beauty ≥ 85.

Authoring rule: keep at least one unconditional variant in each performance bucket, so a girl who matches no gate still has something to say.


When to use this shape: the line should fire when she has any of a related cluster of traits, or when several conditions all need to hold.

Use the combinators from reference/when-conditions.md:

<!-- Either Charming or Seductive triggers this line -->
<Text id="streetwalker.work.flirt" weight="2">
<When>
<Any>
<Trait id="Charming"/>
<Trait id="Seductive"/>
</Any>
</When>
</Text>
<!-- Shy girl, working at night, sober: a specific personality moment -->
<Text id="barmaid.work.shy_night" weight="2">
<When>
<Trait id="Shy"/>
<DayNight value="night"/>
<None>
<Status id="Pregnant"/>
<Status id="Drugged"/>
</None>
</When>
</Text>

<Any> = OR. Implicit AND when children sit directly under <When>. <None> = “none of these” (sugar for not-any).


Recipe 3: A scene for one specific named girl

Section titled “Recipe 3: A scene for one specific named girl”

When to use this shape: you’ve written a unique character and want lines that only she can roll. This is the closest thing to “Sarah’s bespoke scene” — a 1.15 addition.

Exemplar — defining the girl: resources/Packages/Sample_Full/Girls.girlsx

<Girls>
<Girl Name='Sample Girl'
Desc='A cheerful young woman with a quick wit and a quicker smile.'
Charisma='40' Beauty='50' Confidence='50' Spirit='60'
Age='22' Status='Slave'>
<Trait Name='Quick Learner'/>
<Trait Name='Optimistic'/>
</Girl>
</Girls>

The bespoke text in a job’s messages/work.xml:

<Text id="barmaid.work.signature.sample_girl" weight="5">
<When>
<Girl name="Sample Girl"/>
<Performance ge="185"/>
</When>
</Text>

Then in text/en.xml:

<Text id="barmaid.work.signature.sample_girl">
Sample Girl winked at every regular by name, and the tips landed before the drinks did.
</Text>

<Girl name="..."/> matches the girl’s display name exactly. Combine with <Trait>, <Stat>, <Performance> to scope further — “this scene plays only for Sarah, only when she’s Pregnant, only on a great shift” is one nested <When>.

Pair this with the UniqueRegistry (1.15+) which guarantees at most one live Sarah at a time, so the bespoke content never feels duplicated.


Recipe 4: Reacting to a life change (status flags)

Section titled “Recipe 4: Reacting to a life change (status flags)”

When to use this shape: her situation has changed — pregnancy, slave status, drugged, poisoned — and the scene should acknowledge it.

<!-- Pregnant girl tending bar -->
<Text id="barmaid.work.pregnant" weight="2">
<When>
<Status id="Pregnant"/>
</When>
</Text>
<!-- Slave with low Spirit cracking a real smile -->
<Text id="barmaid.work.spirit_breakthrough" weight="3">
<When>
<Status id="Slave"/>
<Stat name="Spirit" le="30"/>
<Performance ge="185"/>
</When>
</Text>

<Status> covers run-time conditions the engine tracks per girl. <Stat> reads any of her current stats with ge/le/gt/lt/eq. Compose freely.


Recipe 5: Per-girl interaction script (Lua override)

Section titled “Recipe 5: Per-girl interaction script (Lua override)”

When to use this shape: the <When>-gated text bag isn’t expressive enough — you want a branching conversation tree, custom buttons, or stat changes triggered by player choices.

Exemplar: resources/Packages/Sample_Full/Characters/MeetGirl.lua and docs/modding-and-packages/interactions.md.

In the girl’s XML, point TRIGGER_TALK at your custom script. From inside that Lua file you can:

  • Branch on wm.girl.traits, wm.girl.stats, wm.girl.lifetime.* (acts, pregnancies, wear tier, broken-threshold flag)
  • Show choices, mutate stats, grant items, push other screens

Use this surface when the XML <When> engine runs out — typically: conversation trees, mini-quests, anything that needs player choice mid-scene. For passive shift narration, prefer Recipes 1–4.


<When> can read her current traits, stats, status, and the current shift’s performance. It cannot natively express “30 in-game days after she started this job” or “after her third miscarriage”. Those need Lua reading wm.girl.lifetime.* counters.

Roadmap signal: the 2.x track aims to expose declarative time-and-history triggers to packs so long-arc storytelling lands without writing Lua.

Until then, the practical pattern is:

  1. Use XML for everything that depends on who she is right now.
  2. Drop into Lua only for arc progression and player-choice scenes.