Skip to content

Jobs cookbook

Three recipes covering the most common job shapes. Each one is drawn directly from shipped game data so you can compare against the real files under resources/Jobs/.

For the complete schema (every attribute, every element), see reference/jobs-reference.md.


Recipe 1: Pure-effect job (no gold, no performance)

Section titled “Recipe 1: Pure-effect job (no gold, no performance)”

When to use this shape: rest jobs, household chores, training sessions. The girl does something useful each shift but earns no gold and has no performance score. The shift summary shows a single flat message.

Exemplar: resources/Jobs/houserest/

This job has only three files:

houserest/
job.xml
effects.xml
messages/work.xml
text/en.xml
<?xml version="1.0" encoding="UTF-8"?>
<Job id="houserest" schema="1">
<Title>House Rest</Title>
<Description>She relaxes at home, recovering energy and health.</Description>
<DefaultImage>profile</DefaultImage>
</Job>

No <Eligibility> block means any girl can be assigned. No performance.xml or wage.xml means no gold is paid.

<?xml version="1.0" encoding="UTF-8"?>
<Effects>
<SetStat target="self" stat="Tiredness" delta="-25"/>
<SetStat target="self" stat="Happiness" delta="10"/>
<SetStat target="self" stat="Health" delta="15"/>
<SetStat target="self" stat="Mana" delta="10"/>
</Effects>

Applied unconditionally every shift. Negative delta decreases the stat; positive increases it.

<?xml version="1.0" encoding="UTF-8"?>
<Bank id="work">
<Text id="houserest.work.default" weight="1"/>
</Bank>

One entry, no <When>, always fires. The content is in text/en.xml.

<?xml version="1.0" encoding="UTF-8"?>
<Locale lang="en">
<Text id="houserest.work.default">She relaxed at home and recovered her energy.</Text>
</Locale>

That is the entire job. No performance curve, no wage, no gains file needed.


When to use this shape: any job where the girl earns gold and better stats produce a better result. The engine computes a performance score from her stats and skills, maps it to a gold amount via a curve, then picks a shift message that matches her tier.

Exemplar: resources/Jobs/barmaid/

This job uses all six files:

barmaid/
job.xml
performance.xml
wage.xml
gains.xml
effects.xml (not present for barmaid, but common)
messages/work.xml
text/en.xml
  1. performance.xml computes a score from the girl’s stats and skills.
  2. wage.xml maps that score to gold.
  3. gains.xml distributes skill XP.
  4. messages/work.xml picks a message based on the same score.
<?xml version="1.0" encoding="UTF-8"?>
<Performance>
<Factor skill="Service" weight="3"/>
<Factor stat="Intelligence" weight="3"/>
<Factor stat="Charisma" weight="2"/>
<Factor skill="Performance" weight="2"/>
<TraitMod trait="Psychic" delta="10"/>
<TraitMod trait="Fleet of Foot" delta="10"/>
<TraitMod trait="Optimist" delta="5"/>
<TraitMod trait="Cum Addict" delta="-5"/>
</Performance>

Each <Factor> multiplies the girl’s value by its weight. The engine sums all factors and divides by the total weight to produce a normalised score. <TraitMod> entries add or subtract flat points if the girl has that trait.

<?xml version="1.0" encoding="UTF-8"?>
<Wage currency="gold">
<Curve type="piecewise">
<Point perf="245" wage="155"/>
<Point perf="185" wage="95"/>
<Point perf="145" wage="55"/>
<Point perf="100" wage="15"/>
<Point perf="70" wage="-5"/>
<Point perf="0" wage="-15"/>
</Curve>
</Wage>

Points must be listed in descending performance order. The engine finds the two breakpoints the girl’s score falls between and interpolates linearly. Negative wages cost the player money (the girl caused more damage than she earned).

<?xml version="1.0" encoding="UTF-8"?>
<Gains xp="15" baseSkill="3">
<Skill name="Service" weight="3"/>
<Skill name="Performance" weight="2"/>
<Stat name="Charisma" weight="1"/>
</Gains>

15 XP is distributed across the listed entries proportional to their weights. baseSkill="3" adds a flat 3 points on top for each entry.

<?xml version="1.0" encoding="UTF-8"?>
<Bank id="work">
<Text id="barmaid.work.perfect.1" weight="1">
<When><Performance ge="245"/></When>
</Text>
<Text id="barmaid.work.perfect.charming" weight="2">
<When>
<Performance ge="245"/>
<Trait id="Charming"/>
</When>
</Text>
<Text id="barmaid.work.great.1" weight="2">
<When><Performance ge="185" le="244"/></When>
</Text>
<Text id="barmaid.work.good.1" weight="3">
<When><Performance ge="145" le="184"/></When>
</Text>
<Text id="barmaid.work.worst.1" weight="1">
<When><Performance le="69"/></When>
</Text>
</Bank>

Notice barmaid.work.perfect.charming: it has higher weight (2 vs 1) so it fires more often for charming girls at the top tier. Multiple entries can match the same shift; the engine picks one proportionally by weight.


When to use this shape: several job slots that differ only in one dimension, so you want to avoid duplicating most of the XML. One data directory registers under multiple job IDs, each with a different parameter value. <JobParam> conditions in effects.xml and messages/work.xml gate which blocks fire for which slot.

Exemplar: resources/Jobs/houseso/

Three job slots (Straight Training, Bisexual Training, Lesbian Training) all share one houseso/ directory. They are registered in engine code with orientation=straight, orientation=bi, and orientation=lesbian respectively.

<?xml version="1.0" encoding="UTF-8"?>
<Job id="houseso" schema="1">
<Title>Sexual Orientation Training</Title>
<Description>She undergoes orientation training. The exact effect depends on the orientation parameter set when the job is registered.</Description>
<DefaultImage>profile</DefaultImage>
</Job>
<?xml version="1.0" encoding="UTF-8"?>
<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>
<SetSkill target="self" skill="NormalSex" delta_min="1" delta_max="2"/>
<SetSkill target="self" skill="Lesbian" delta_min="1" delta_max="2"/>
<SetSkill target="self" skill="Group" delta_min="2" delta_max="4"/>
<SetStat target="self" stat="Tiredness" delta="5"/>
<SetStat target="self" stat="Exp" delta="2"/>
</Group>
<Group>
<When>
<JobParam name="orientation" value="lesbian"/>
</When>
<SetSkill target="self" skill="Lesbian" delta_min="2" delta_max="4"/>
<SetSkill target="self" skill="NormalSex" delta_min="-3" delta_max="-1"/>
<SetSkill target="self" skill="Anal" delta_min="-1" delta_max="0"/>
<SetStat target="self" stat="Tiredness" delta="5"/>
<SetStat target="self" stat="Exp" delta="2"/>
</Group>
</Effects>

Only the group whose <JobParam> matches the slot’s registered parameter will fire. The other two groups are skipped entirely.

<?xml version="1.0" encoding="UTF-8"?>
<Bank id="work">
<Text id="houseso.work.straight" weight="1">
<When>
<JobParam name="orientation" value="straight"/>
</When>
</Text>
<Text id="houseso.work.bi" weight="1">
<When>
<JobParam name="orientation" value="bi"/>
</When>
</Text>
<Text id="houseso.work.lesbian" weight="1">
<When>
<JobParam name="orientation" value="lesbian"/>
</When>
</Text>
</Bank>

Each slot sees exactly one eligible message because only one <JobParam> condition matches per run.

<?xml version="1.0" encoding="UTF-8"?>
<Locale lang="en">
<Text id="houseso.work.straight">She underwent straight orientation training.</Text>
<Text id="houseso.work.bi">She underwent bisexual orientation training.</Text>
<Text id="houseso.work.lesbian">She underwent lesbian orientation training.</Text>
</Locale>

The parameter pattern works for any dimension where two or three slots share most of their logic. If the slots differ in more than one dimension, or have very different effect lists, separate directories are cleaner.


  • reference/jobs-reference.md: full attribute and element reference
  • snippets/effects.xml: copy-paste starter for common effect patterns
  • reference/when-conditions.md: full <When> condition language