Skip to content

Effects schema reference

effects.xml is the file inside a job directory that defines which stats, skills, and brothel properties change at the end of each shift. This page covers every element in the schema.

If you are new to job data files, start with docs/jobs-as-data/authoring-checklist.md for the per-port procedure and directory layout. For the <When> condition grammar used inside <Group>, see reference/when-conditions.md.


The root of effects.xml. Contains any mix of <SetStat>, <SetSkill>, <SetBrothel>, <RandomChoice>, <Group>, <Bind>, and <Mod> entries. All top-level entries that are not inside a <Group> fire unconditionally every shift.

<?xml version="1.0" encoding="UTF-8"?>
<Effects>
<!-- entries here -->
</Effects>

Entries are executed in document order. There is no short-circuit evaluation between top-level entries. Every entry runs independently.


Adjusts a girl’s stat by a fixed or random amount.

<SetStat target="self" stat="Tiredness" delta="5"/>
<SetStat target="self" stat="Happiness" delta_min="1" delta_max="3"/>
AttributeRequiredNotes
targetYesWho receives the change. See “Targets” below.
statYesStat name. See “Stat names” below.
deltaOne of delta or delta_min+delta_maxFixed integer delta. May be negative.
delta_minWith delta_maxMinimum of a uniform random range (inclusive).
delta_maxWith delta_minMaximum of a uniform random range (inclusive).

If delta_min > delta_max the engine swaps them silently, so delta_min="-3" delta_max="-1" and delta_min="-1" delta_max="-3" are equivalent. This is intentional for readability when expressing “lose between 1 and 3 points.”

TargetMeaning
selfThe girl performing the job.
brothel.girlsAll girls currently assigned to the same brothel. Useful for morale/training auras.

Charisma, Happiness, Libido, Constitution, Intelligence, Confidence, Mana, Agility, Fame, Level, AskPrice, House, Exp, Age, Obedience, Spirit, Beauty, Tiredness, Health, PCFear, PCLove, PCHate.


Adjusts a girl’s skill by a fixed or random amount.

<SetSkill target="self" skill="Service" delta="1"/>
<SetSkill target="self" skill="NormalSex" delta_min="2" delta_max="4"/>
<SetSkill target="self" skill="Lesbian" delta_min="-3" delta_max="-1"/>
AttributeRequiredNotes
targetYesWho receives the change. Only self is supported in v1.13.
skillYesSkill name. See “Skill names” below.
deltaOne of delta or delta_min+delta_maxFixed integer delta. May be negative.
delta_minWith delta_maxMinimum of a uniform random range (inclusive).
delta_maxWith delta_minMaximum of a uniform random range (inclusive).

Anal, Magic, BDSM, NormalSex, Beastiality, Group, Lesbian, Service, Strip, Combat, Performance.


<SetBrothel>: apply a brothel property delta

Section titled “<SetBrothel>: apply a brothel property delta”

Adjusts a numeric property on one or more brothels.

<SetBrothel target="player.brothels" key="Fame" delta="3" clamp_max="100"/>
<SetBrothel target="brothel" key="Filthiness" delta="2"/>
AttributeRequiredNotes
targetYesWhich brothel(s) to update. See “Targets” below.
keyYesProperty name. See “Key names” below.
deltaYesInteger delta. May be a literal or a bind reference ($bindname). See <Bind> below.
clamp_maxNoIf present, the result is clamped to this maximum after the delta is applied.
TargetMeaning
brothelThe brothel the girl is currently assigned to.
player.brothelsEvery brothel the player owns. Used by jobs that promote the player’s business city-wide (for example, a recruiter who raises Fame everywhere at once).
KeyNotes
FameHow well-known the brothel is (0-100). Affects customer traffic.
FilthinessCleanliness level of the building. High values reduce happiness and income.
HappinessAggregate morale of girls in the brothel (0-100).

Selects exactly one <Option> at random, weighted by the weight attribute. Only the selected option’s children execute.

<RandomChoice>
<Option weight="6"><SetStat target="self" stat="Exp" delta="0"/></Option>
<Option weight="2"><SetSkill target="self" skill="Service" delta="1"/></Option>
<Option weight="2"><SetSkill target="self" skill="Strip" delta="1"/></Option>
</RandomChoice>
AttributeNotes
weightPositive integer. Selection probability is weight / sum_of_all_weights.

An <Option> may contain any mix of <SetStat>, <SetSkill>, <SetBrothel>, and nested <RandomChoice> entries. An empty <Option> is not supported; use a <SetStat delta="0"> as a no-op placeholder when you want a probability sink (a branch that intentionally does nothing).

There is no minimum number of options, but a <RandomChoice> with a single option is equivalent to an unconditional entry and should be simplified.


Groups one or more entries under an optional <When> gate. All entries inside the group execute if the <When> condition passes (or if no <When> is present). Groups do not short-circuit each other: if two groups are present, both are evaluated independently every shift.

<Group>
<When>
<Stat name="Tiredness" lt="50"/>
</When>
<SetStat target="self" stat="Happiness" delta="1"/>
<SetStat target="self" stat="Tiredness" delta="3"/>
</Group>

A <Group> may also contain <Bind> declarations (see below). Binds declared inside a group are scoped to that group and visible to the group’s <When> check and all entries inside it.

Effects that depend on a threshold condition (such as “fire penalty if perf < 20, fire bonus if perf >= 20”) must use two separate groups with complementary <When> clauses. Do not assume the absence of one group’s <When> implies the other ran; the two groups are independent:

<Group>
<When><Bind name="perf" lt="20"/></When>
<SetStat target="self" stat="Tiredness" delta="2"/>
</Group>
<Group>
<When><Bind name="perf" ge="20"/></When>
<SetStat target="self" stat="Happiness" delta="1"/>
<SetStat target="self" stat="Tiredness" delta="4"/>
</Group>

Both groups are always evaluated. The first fires when the condition is true; the second fires when its own condition is true. Together they cover the full range.


Computes an integer value from a formula and assigns it a name. The name can then be referenced in <When><Bind name="..." ge="N"/></When> and in delta="$name" on <SetBrothel>.

<Bind name="perf" expr="(Charisma + Intelligence + Service) / 3"/>
AttributeNotes
nameIdentifier used to reference the value. Lowercase, no spaces.
exprArithmetic expression over girl stats and skills. Integer arithmetic; division truncates toward zero.

All stat names and skill names listed under <SetStat> and <SetSkill> above are valid. For example: Charisma, Intelligence, Service, Happiness, Tiredness.

A <Bind> may reference any bind declared earlier in the same <Group>, by name, anywhere its identifier could appear in expr. Binds are evaluated in declaration order, so a later bind sees the value of every bind above it.

<!-- Two-step bind: tier reads perf -->
<Bind name="perf" expr="(Charisma + Intelligence + Service) / 3"/>
<Bind name="tier" expr="perf / 20"/>

The inline-formula workaround still works for packs that target older engine versions:

<!-- Inline form, equivalent and back-compatible -->
<Bind name="perf" expr="(Charisma + Intelligence + Service) / 3"/>
<Bind name="delta" expr="(Charisma + Intelligence + Service) / 60"/>

A bind cannot reference a sibling declared later, and binds in one group are not visible from a different group.

A <Bind> may be declared at the top level of <Effects> (visible everywhere) or inside a <Group> (scoped to that group). The group-scoped form is preferred when the value is only used inside that group.


<Mod>: C++ pipeline bridge (legacy <RunHelper> accepted)

Section titled “<Mod>: C++ pipeline bridge (legacy <RunHelper> accepted)”

Triggers a named C++ sub-pipeline from inside the data-driven executor. The element is registry-based: any name parses successfully, and the engine looks the name up at apply time. The legacy spelling <RunHelper name="..."/> is still accepted as a synonym.

<Mod name="whore_act"/>

<Mod> may appear inside <Effects> or, since 1.13.1, inside <Performance>. The latter is the right surface when the hook is producing a synergy multiplier the performance score should consume.

AttributeNotes
nameName of the registered helper to invoke. Looked up in the engine’s mod registry at apply time.
NameWhat it doesDirection
whore_actRuns the full per-customer loop for whore-archetype jobs: customer selection, refusal check, act performance, side effects.(none; pipeline)
work.barcookBar food synergy producer; publishes a per-Brothel food-quality scalar BarMaid + BarWaitress + BarWhore consume.publisher (headcount/scalar)
work.barmaidReads bar-staff headcount (Cook + Waitress) to scale tip income.consumer (headcount)
work.waitressReads BarMaid + BarCook presence to scale service multiplier.consumer (headcount)
work.barsingerReads piano_present to apply doubled-performance bonus when a Pianist is on shift.consumer (presence flag)
work.pianoPublishes piano_present binary flag the Singer reads.publisher (presence flag)
work.barwhoreReads bar-staff headcount to scale income.consumer (headcount)
work.barstripperReads sleazybarmaid_present.consumer (presence flag)
work.sleazybarmaidReads barstripper_present (mutual cross-consumer pair).consumer (presence flag)
work.sleazywaitressReads barstripper_present.consumer (presence flag)
work.advertisingHook stub; the actual m_AdvertisingLevel mutation lives in C++ until Wave 3 promotes double-typed brothel scalars into <Effects>.bifurcated
work.securityHook stub; the actual m_SecurityLevel decay/recompute + threat-deterrence-from-headcount lives in C++ until Wave 3.bifurcated
work.masseuseHook stub; happy-ending-probability scaling against per-customer satisfaction lands in Wave 3+ via <JobState scope=customer>.bifurcated (future)

Most of the per-job hooks above are placeholders today: they fire mod_fired=true so the registry round-trip is observable, but the actual synergy transport (per-brothel scratch slots like piano_present / bar_staff_count / advertising_budget) ships in a future engine version. Today the synergy effect is whatever the engine’s job code computes; the data-side hook is a placeholder for the eventual transport hand-off.

For details on writing synergy-aware message variants that consume these hooks (and the four partner-direction shapes they come in) see visible-synergies.md.

Unknown <Mod> names parse successfully but silently no-op at apply time. This is intentional for forward-compat with future engine versions that may register the name; packs authored against a newer engine load cleanly on an older one rather than failing at parse time.


1. Fixed stat penalty + optional skill gain

Section titled “1. Fixed stat penalty + optional skill gain”

A simple job that always costs tiredness and has a 40% chance of gaining one of two skills:

<Effects>
<SetStat target="self" stat="Tiredness" delta="5"/>
<SetStat target="self" stat="Exp" delta="2"/>
<RandomChoice>
<Option weight="6"><SetStat target="self" stat="Exp" delta="0"/></Option>
<Option weight="2"><SetSkill target="self" skill="Service" delta="1"/></Option>
<Option weight="2"><SetSkill target="self" skill="Strip" delta="1"/></Option>
</RandomChoice>
</Effects>

Six out of ten shifts are no-ops on the <RandomChoice>; two out of ten gain Service; two out of ten gain Strip. The total weight is 10, matching the percent(40) then percent(50) pattern from legacy C++ (see resources/Jobs/houserecruiter/effects.xml for the full worked example with a derived performance bind).

2. Orientation-gated training (single file, multiple job slots)

Section titled “2. Orientation-gated training (single file, multiple job slots)”

Three job slots that share one data directory but gate different effects on a <JobParam>:

<Effects>
<Group>
<When><JobParam name="orientation" value="straight"/></When>
<SetSkill target="self" skill="NormalSex" delta_min="2" delta_max="4"/>
<SetSkill target="self" skill="Anal" delta_min="1" delta_max="2"/>
<SetSkill target="self" skill="Lesbian" delta_min="-1" delta_max="-3"/>
<SetStat target="self" stat="Tiredness" delta="5"/>
<SetStat target="self" stat="Exp" delta="2"/>
</Group>
<Group>
<When><JobParam name="orientation" value="bi"/></When>
<!-- ... -->
</Group>
<Group>
<When><JobParam name="orientation" value="lesbian"/></When>
<!-- ... -->
</Group>
</Effects>

The three groups are all evaluated every shift; only the one whose <JobParam> matches the registered slot parameter fires. See resources/Jobs/houseso/effects.xml for the full file.

3. Random skill pick from a pool (two independent draws)

Section titled “3. Random skill pick from a pool (two independent draws)”

Two consecutive <RandomChoice> blocks to pick two skills from a pool of seven, each with equal probability. The same skill may be picked twice; the executor accumulates deltas correctly:

<Effects>
<SetStat target="self" stat="Exp" delta="2"/>
<SetStat target="self" stat="Tiredness" delta="4"/>
<RandomChoice>
<Option weight="1"><SetSkill target="self" skill="NormalSex" delta_min="1" delta_max="1"/></Option>
<Option weight="1"><SetSkill target="self" skill="Anal" delta_min="1" delta_max="1"/></Option>
<Option weight="1"><SetSkill target="self" skill="BDSM" delta_min="1" delta_max="1"/></Option>
<Option weight="1"><SetSkill target="self" skill="Group" delta_min="1" delta_max="1"/></Option>
<Option weight="1"><SetSkill target="self" skill="Lesbian" delta_min="1" delta_max="1"/></Option>
<Option weight="1"><SetSkill target="self" skill="Service" delta_min="1" delta_max="1"/></Option>
<Option weight="1"><SetSkill target="self" skill="Strip" delta_min="1" delta_max="1"/></Option>
</RandomChoice>
<RandomChoice>
<!-- identical block; each draw is independent -->
...
</RandomChoice>
</Effects>

See resources/Jobs/basictraining/effects.xml for the full file.

Compute a derived performance value with <Bind>, gate two complementary groups on it, and use a bind reference in delta="$delta":

<Effects>
<Group>
<Bind name="perf" expr="(Charisma + Intelligence + Service) / 3"/>
<When><Bind name="perf" lt="20"/></When>
<SetStat target="self" stat="Tiredness" delta="2"/>
</Group>
<Group>
<Bind name="perf" expr="(Charisma + Intelligence + Service) / 3"/>
<Bind name="delta" expr="(Charisma + Intelligence + Service) / 60"/>
<When><Bind name="perf" ge="20"/></When>
<SetBrothel target="player.brothels" key="Fame" delta="$delta" clamp_max="100"/>
<SetStat target="self" stat="Happiness" delta="1"/>
<SetStat target="self" stat="Tiredness" delta="4"/>
<SetStat target="self" stat="Exp" delta="2"/>
</Group>
</Effects>

The perf bind is declared in both groups independently because binds in one group are not visible from another. Inside a single group a later bind may reference earlier ones (1.13.0+); the example above keeps both forms inline so it loads on every supported engine version.


  • reference/when-conditions.md: full grammar for <When> conditions used inside <Group>
  • reference/jobs-reference.md: full schema for all other job files (performance, wage, gains, messages)
  • docs/jobs-as-data/authoring-checklist.md: per-port procedure and status flag caveats