Guide

Stories

Stories are the life events that land in your inbox: a cousin's wedding, a wrist that won't stop aching, a documentary crew, a letter from HMRC. Each one has a title, a body and up to four choices. A choice can change your stats, roll a random outcome, change the world (withdraw you from an event, start an injury, bring in a sponsor offer…) and schedule another story weeks later.

Stories live in data/events/narrative/*.json. Each file is a JSON array of stories. The core game splits them by category (family.json, health.json, travel.json, money.json, …), but any file name works. To change a core story, reuse its id or patch it ("target": "narrative:core.narrative.family-occasion"). The editor schema is the stories reference.

Every field on this page except id, trigger, title, body and choices is optional. A story written before these features existed works exactly as before.

A story#

{
  "id": "acme.story.pub-quiz",
  "trigger": "weekTick",
  "condition": { "chance": 0.02, "tierMin": 4 },
  "title": "Pub Quiz Night",
  "body": "The Feathers need a sixth for the quiz team. The darts round is worth double.",
  "choices": [
    {
      "id": "go",
      "label": "Join the team",
      "effects": { "morale": 4, "fatigue": 0.02 },
      "outcome": "You get every darts question right and none of the geography."
    },
    { "id": "no", "label": "Stay in and practise", "effects": { "form": 0.02 } }
  ],
  "cooldownWeeks": 30
}
FieldMeaning
triggerWhen it's checked (below)
conditionWhat must be true for it to fire, plus chance (below)
title, bodyThe message. Both take text variables
imageOptional picture above the story in the inbox, e.g. "assets/stories/derby.png" (about 2:1; see Images)
choices1–4 choices (below). The last choice is taken for you if the story is left for two weeks, so make it the safe one
weightWhen several stories are ready at once, one is picked by weight. Default 1
onceOnly ever fires once per career
cooldownWeeksWeeks before it can fire again. Default 26. Weekly stories never repeat within 8 weeks, whatever this says

Triggers#

TriggerCheckedExtra facts for the condition
weekTickAt the end of every week
matchEndAfter each of your matcheswon, tv, major, againstRival; {opponent}, {event}
eventEndWhen your event finishespositionMax, prizeMin, won, tv, major; {event}
exhibitionEndAfter an exhibition nightwon, exhibitionKind; {exhibition}, {town}
seasonStart1 January
seasonEndAfter the season review
followUpNever on its own: only another story's followUpSee follow-ups

At most one story fires per check. A follow-up that's due takes that week's weekTick slot. A mod's script can also fire a story at any check, whatever its trigger and condition.

Conditions#

All listed conditions must hold. Stat ranges use the same scales as the game: confidence and morale 0–100, form −1…1, fatigue 0…1, public image −100…100.

ConditionTrue when
chance0–1: the probability per check once everything else holds. No chance = always (milestones)
tierMin, tierMaxYour tier is in range. Careful: tier 1 is the top, so tierMin: 4 means tier 4 or 5 (pub and county level)
tourCardYou do (true) or don't (false) hold a Tour Card
rankMaxOrder of Merit rank at least this good
followersMin, followersMaxFollower count
confidenceMin/Max, moraleMin/MaxConfidence, morale
formMin, formMaxForm
fatigueMin, fatigueMaxFatigue
cashMin, cashMaxBank balance. Use these to keep costs sensible for the player's means
imageMin, imageMaxPublic image
ageMin, ageMaxAge
nationalityYour nationality is one of these, e.g. ["ENG", "WAL"]
livesIn, notLivesInWhere you live (not your nationality) is, or is not, one of these countries, e.g. ["ENG"]. A story that only makes sense in one country says so here
sceneYou live in one of these darts scenes (scene record ids, e.g. ["core.scene.eng"]; a country with no scene of its own is in the fallback record's)
gender"m" or "f"
monthsOnly in these months, e.g. [11, 12]
dateFrom, dateToOnly between these days of the year, "MM-DD", both included: a story that names a day ("10-30" to "11-05" for Fireworks Night) comes in the week up to it, as stories are checked once a week. A dateFrom later in the year than dateTo runs over the new year; either alone means from 1 January or to 31 December
seasonPhase"early" (Jan–Apr), "mid" (May–Aug) or "late" (Sep–Dec)
hasEventWithinDaysYou're entered in (or automatically in) an event starting within this many days. Use it for "the week before a big event" stories, and whenever the text mentions {nextEvent}
nextTrip(With hasEventWithinDays.) The journey to that event is one of these travel steps, by id (the travel settings' steps, for example ["country", "neighbour", "far"]): a long drive in the country, not a flight abroad or a pub round the corner
trip(matchEnd, eventEnd.) The journey to the match or event just played was one of these travel steps, by id: jet lag after a flight, not after the local
seasonMonthsMonths of the weather's year as a northern year has them ([12, 1, 2] for winter): where the player's scene has seasonOffset 6 (the southern hemisphere) the story comes in June to August. months stays the calendar's (Christmas, the tax return)
noEventWithinDaysNothing on your calendar for this many days (a quiet spell)
hasCoach, hasWalkonYou have a darts coach / a walk-on track
dayJobtrue: only while you keep a day job; false: only once you have gone full-time
hasStaffYou employ someone in this staff role: a role id ("core.staff.physio") or its short name ("physio"). A list means any of them
staffQualityMinA staff member rated at least this many stars (1–5): in the hasStaff role if given, otherwise anyone
staffQualityMaxA staff member rated at most this many stars: with "hasStaff": "agent", a bad agent
negotiatedSomething happened in a sponsor negotiation in the last 4 weeks: walkedAway, biddingWar, agentDeal (your agent closed one) or signed (you did)
hasSponsorAny active sponsorship
sponsorCategoryAn active sponsor in this category (darts, apparel, drinks, boards, energy, travel, tech, local)
hasRivalA rivalry of real intensity with someone ({rival} is then filled)
rivalEdgeWho has the edge in your record against the story's rival: "yours" (you lead by rivalry.edgeLead wins, 1 by default), "theirs" or "level". The rival is the opponent just played in a matchEnd story against a rival, else your hottest rival
rivalryMinMeetingsYou and the story's rival have met at least this many times
rivalLastResult"won" or "lost": how your last meeting with the story's rival went (never met: neither holds)
flag, notFlagA story flag (or a list: all of them / none of them) is set / not set. Flags are set by a setFlag action; the name takes text variables, so "flag": "fuel:{rival}" is one flag per rival. See Story flags
injuredYou're carrying an injury
minTitles, maxTitlesCareer titles
lastResultPositionMaxYour most recent event: finishing position at most this (0 = won, 1 = final, 2 = semi…)
winningStreakMin, losingStreakMinConsecutive match wins / losses
won, tv, major, againstRivalmatchEnd / eventEnd only: the result that just happened
positionMaxeventEnd only: finishing position at most this (0 = the winner, 1 = the final). Never holds after a qualifier: coming through one is not a final
prizeMineventEnd only: the prize money the event paid you is at least this
exhibitionKindexhibitionEnd only: the night was one of these kinds, e.g. ["holidayPark", "cruise"] (see exhibitions)
hasBookingYou have (true) or don't have (false) an exhibition booked in the next 4 weeks
loadMin, loadMaxYour workload (0–100): 60 tired, 75 overloaded, 90 burnt out. Use it for travel and burnout stories
squadYour standing in a squads league you play for (below). You must be in one of its squads

Your standing in a squads league (squad)#

For stories about the county side or a youth side. tour picks the league: the squads events of that tour where you live (core.tour.county is the county league, core.tour.junior the Shires youth leagues; a tour that runs one league an age group gives the one that would take you). Every other field is optional and every listed check must hold.

FieldMeaning
capsMin, capsMaxLeague days that picked you (capsMax: 0: never picked yet)
seasonsMinLeague seasons in which you were picked
rankMaxYour place on your squad's own average table this season (1 = its best); last season's until you have played this one
averageMinYour three-dart average on that table, a list with one bar a division, top division first; the last bar holds below
firstSideYour last pick was for your squad's first side (its A team) or not
pickedThe last day your squad played picked you (true) or left you out (false)
droppedThat day left you out after the one before picked you
"condition": {
  "chance": 0.08,
  "months": [9, 10, 11, 1, 2],
  "squad": { "tour": "core.tour.county", "capsMin": 6, "seasonsMin": 2, "rankMax": 4, "picked": true, "averageMin": [80, 75, 70] }
}

In a county league the scene word {scene:county} is the county you play for, so a story can name it.

Choices#

FieldMeaning
idUnique within the story
labelThe button text (text variables allowed)
effectsAlways applied (below)
actionsAlways applied: world effects
outcomeShown after choosing
outcomes1–6 random outcomes; one is rolled on top of effects
followUpA story to fire later (follow-ups)

The inbox shows each choice's guaranteed effects as chips ("Costs £90", "Morale ↑", "Miss next event", "Sponsor interest"). A choice you can't pay for is greyed out, and a story where no choice is affordable never holds up time: it settles on the cheapest choice. Only the choice's own effects.money counts towards affordability, not money in its outcomes.

Effects#

Numbers are changes, applied once.

EffectRangeNotes
money−50,000…50,000Keep it in proportion to the tier the story can reach
confidence, morale−40…40Small is plenty: 2–8
form−0.5…0.50.02–0.06 is noticeable
fatigue−0.5…0.5+ is more tired
followers±100,000
reputation−20…20
image−20…20Public image; sponsors notice it
pressure−0.3…0.3Media pressure carried into your next matches (it fades each week)
load−50…50Workload: + a draining trip, − a proper rest. 3–10 is a lot
any attribute−5…5scoring, finishing, checkoutKnowledge, consistency, tempoControl, bullAccuracy, composure, resilience, crowdHandling, concentration, stamina, fitness, eyesight. Permanent, so use 1–2

Random outcomes#

Give a choice outcomes and one is rolled when it's picked, by weight, among the outcomes whose condition holds (their chance is ignored). The roll is seeded from the career, so the same save always rolls the same way. The choice's own effects, actions and outcome still apply; the rolled outcome's text is shown after them.

{
  "id": "play-through",
  "label": "Strap it and play through",
  "effects": { "confidence": 2 },
  "outcome": "Tape, gritted teeth and a stubborn streak.",
  "outcomes": [
    { "weight": 3, "text": "It settles down by the weekend." },
    {
      "weight": 2,
      "hint": "Could make it worse",
      "text": "By Friday you can't hold a mug. You've made it worse.",
      "actions": [
        { "type": "injury", "label": "Wrist strain", "weeks": [2, 3], "severity": "moderate" }
      ],
      "followUp": { "story": "acme.story.wrist-setback", "afterWeeks": 1 }
    }
  ]
}
Outcome fieldMeaning
weightRelative likelihood. Default 1; 0 never happens
conditionOnly in the running if this holds when the choice is made, e.g. { "formMin": 0 } for a result that needs you in form
effects, actionsAs on a choice
textShown after the choice's outcome
followUpAs on a choice
hintUp to 40 characters, shown as a neutral chip on the choice before you pick it, e.g. "Might meet a sponsor"

A choice with two or more outcomes gets a "Could go either way" chip.

Actions (world effects)#

actions is a list. Each action has a type:

TypeFieldsWhat happens
withdrawrefund (optional, false: a missed registration)You pull out of the next event you're playing (entered or automatic). Entry fee refunded, unless refund is false: then you are out and the fee stays with the promoter (a missed registration; no fine). {event} in the rest of the text becomes that event. Pair it with hasEventWithinDays
sitOutweeks, reason?You miss every event starting in the next weeks (entries refunded)
injuryweeks, label? ("Injury"), severity? (minor), sitOut?, part?, injury?An injury through the medical room: body part, treatment, healing week by week. sitOut defaults to true for serious only
modifierlabel, weeks, accuracy?, training?, fatigueFloor?, confidenceFloor?, composure?A timed boost or setback (below)
sponsorOffercategory?, sponsor?A sponsorship offer arrives now, from that sponsor or category if one suits your tier. {sponsor} becomes its name for the rest of the text and any follow-up
rivalrywith ("rival", "opponent" or a player id), by (−1…1)Heats up (+) or cools (−) a rivalry. 0.1 is a flare-up; 0.3 makes a new rival
newsheadline, bodyA news story about you (text variables allowed)
setFlagflag, value? (true)Remembers a story flag on the career; value: false clears it. Later stories test it with the flag / notFlag conditions. Text variables in the name make it one flag per person ("fuel:{rival}")
socialtext, handle?A post on your feed; without handle it's your own
followerSurgepercent (0–1), min?Followers grow by this share, at least min
staffLeavesrole? (id or short name, e.g. "physio"); older stories may use kind (coach, mentalCoach, fitnessCoach)That member of your team leaves, with no payoff. Without a role, whoever is least settled goes
staffMoraleby (−50…50), role?, relationship? (−50…50)Staff morale (and their relationship with you) changes: one role, or the whole team without role
holidayweeks, label?Time off from today: you miss events for weeks (entries refunded, bookings called off) and your workload recovers as on a holiday
chargeoffence, chance? (1), afterWeeks?The regulator charges you with an offence of its rule book, as a case (see the regulator). With chance it ever comes out; with afterWeeks it comes out that many weeks later, otherwise at once. The choice shows "Could bring a charge from the regulator"
catchOtherchance? (1)An investigation catches a computer player with a Tour Card selling places in events (the regulator's sellingOffence): news, and the card is taken, so a tour place opens

weeks is a number, or [min, max] rolled when it happens.

Mod scripts use the same handlers: dtj.effects.apply(action) takes modifier, injury, sponsorOffer, rivalry, social, followerSurge and staffMorale (clamped to smaller limits than a story's), and a script's inbox message can give its choices effects and actions that show as the same chips.

Modifiers last for their weeks, then expire. While one runs:

Modifier fieldEffect
accuracy−0.1…0.1: + tightens your throwing by that fraction (0.02 = 2%), − loosens it
training−1…1: training gains × (1 + training). −1 stops training altogether
fatigueFloorFatigue can't drop below this
confidenceFloorConfidence can't drop below this
composure−20…20: composure points on the oche while it lasts (− nerves, + calm)

Injuries go through the medical room. part says where it hurts (shoulder, elbow, wrist, back, knee, foot, eyes; left out, it's worked out from the label, else the throwing shoulder), and each part hurts a different part of your game. injury names an injury type from medical.json instead (its name and part). weeks is how long it takes with plain rest; the treatment you choose (and your physio) makes it quicker or slower, and playing on can make it worse. sitOut: true starts you on rest (you miss events), false on playing through it. Keep story injuries short: one to three weeks.

{ "type": "injury", "label": "Wrist strain", "part": "wrist", "weeks": [1, 2] }

League nights are never skipped, because their line-up is fixed for the series.

Follow-ups and arcs#

A followUp on a choice or an outcome schedules another story:

"followUp": { "story": "acme.story.part-two", "afterWeeks": [2, 3], "chance": 0.8 }
FieldMeaning
storyThe story to fire (any trigger; usually followUp)
afterWeeksWeeks from the decision: a number or [min, max]
chance0–1: whether it happens at all. Default 1

The follow-up fires at the first week's end on or after that date, even if its own trigger, cooldown or once wouldn't normally allow it. Its condition is still checked (without chance), so a condition can cancel it: "condition": { "hasRival": true } drops a grudge match if the rivalry has cooled. Trigger facts like won aren't available to a follow-up.

The follow-up keeps the text variables of the story that scheduled it ({opponent}, {event}, {rival}, {sponsor} and the rest), except {money}, {nextEvent}, {venue} and {city}, which are worked out fresh. The inbox marks it "Part 2", "Part 3" and so on. Scheduled follow-ups are kept in the save.

Use "trigger": "followUp" for parts that should only ever come from their arc (or from your script). The loader warns about a followUp story nothing schedules, unless its mod has a script, and removes a follow-up pointing at a story that doesn't exist.

Story flags#

A flag is a name the career remembers. A choice (or a rolled outcome) sets one with a setFlag action, and any later story can ask for it with flag or refuse to fire while it stands with notFlag. Flags never expire on their own: clear one with "value": false when the thread is paid off. Text variables are filled in the name, so a flag can be about a person: "fuel:{rival}" and "called-out:{opponent}" are one flag per rival or opponent, and a story that asks for "flag": "fuel:{rival}" only fires while the flag for that rival stands.

The game sets a few flags itself: staff-clash while two of your staff are at odds and staff-clash-asked once you have answered (the core's "{clashA} or {clashB}?" story in events/narrative/team.json fires on the first and not the second, and its choices set staff-clash-choice:back-one, bang-heads or let-it-ride, which the game reads the next week), and poached-by:<playerId> when a rival takes, or loses, a coach of yours.

The core's rival stories are the worked example (events/narrative/rivals.json): "Beaten by {rival}" sets fuel:{rival}, feud:{rival} or respect:{rival} for the choice you made, and three matchEnd and weekTick stories pay each one off (the screenshot comes down, a columnist drags the feud up while the record is against you, the rival returns the compliment) and clear it; "The Handshake" sets called-out:{opponent} and schedules the opponent's reply as a follow-up; declining the grudge match sets bottled:{rival}, which a rival with the edge brings up until you answer it. The pattern:

{ "id": "fire", "label": "Use it as fuel",
  "actions": [{ "type": "setFlag", "flag": "fuel:{rival}" }] }
{ "id": "acme.story.fuel-paid", "trigger": "matchEnd",
  "condition": { "againstRival": true, "won": true, "flag": "fuel:{rival}" },
  "choices": [{ "id": "done", "label": "Take it down",
                "actions": [{ "type": "setFlag", "flag": "fuel:{rival}", "value": false }] }] }

The flags are listed in the save's story memory (flags), in the order set. Mod scripts do not read them yet.

Text variables#

Titles, bodies, labels, outcome texts, news and social posts can use:

VariableIsWhen missing
{name}Your full name
{first}Your first name
{nickname}Your nickname
{money}Your bank balance, in the player's currency (a fixed sum: {money:500})
{event}The event (matchEnd, eventEnd, or the one a withdraw pulled you out of)"the event"
{opponent}Your opponent (matchEnd)"your opponent"
{rival}Your fiercest rival"your old rival"
{nextEvent}The next event you're playing"your next event"
{venue}, {city}Where that event (or the current one) is"the venue", "town"
{sponsor}Your biggest sponsor, or the one a sponsorOffer brought in"your sponsor"
{coach}Your darts coach"your coach"
{staff:physio}The member in that staff role (short name or full id)"your physio"
{agency}Your agent's agency (or "Name's outfit" for an independent)"your agency"
{clashA}, {clashB}The two members of your team at odds while a clash runs(not filled)
{dealSponsor}The sponsor in the latest negotiation news (see negotiated)"the sponsor"
{otherBidder}The other side of the latest bidding war"their rivals"
{exhibition}The exhibition night just played (exhibitionEnd), or your next booking"the exhibition"
{town}That night's town"town"
{scene:level} …A word of the darts scene you live in (see scenes, words): {scene:level} is "county" in England and "area" in Scotland; {scene:name}, {scene:body}, and {scene:town} / {scene:venue} are a place of the scene's own events, the same one all week; {scene:pub} is one of the scene's venues of the kind "pub" (the fallback record's word where the scene has none) and {scene:league} the name of its local rung's tour; the core also gives every scene road and money words ({scene:motorway}, {scene:services}, {scene:bigRoad}, {scene:smallWin}, {scene:chipShop}, {scene:socialClub} and others: see scenes.json), British in the UK's scenes and neutral on the fallback recordthe story is not told

A story that uses a scene word takes its wording from where the player lives, and a story that uses a word the player's scene has not got does not fire there: a title written as {scene:Level} Call-Up reads "County Call-Up" in England and "Area Call-Up" in Scotland, and the story is never sent to a player living in a country whose scene has no word for the level. Write the words your scene has in its words; a key your scene leaves out is read from the fallback scene's. Nothing in the base game names an English place or an English level word outright any more: use the scene words for places and levels, and livesIn for a story that can only happen in one country.

Example: an arc#

A charity exhibition in three steps: the invite, the night itself (two to three weeks later), and a chance of a viral moment.

[
  {
    "id": "acme.story.charity-invite",
    "trigger": "weekTick",
    "condition": { "chance": 0.02, "months": [10, 11], "followersMin": 400 },
    "title": "The Hospice Exhibition",
    "body": "The hospice in {city} wants you to headline its exhibition night.",
    "choices": [
      {
        "id": "headline",
        "label": "Headline it",
        "effects": { "image": 2 },
        "outcome": "Posters go up with your face on them.",
        "followUp": { "story": "acme.story.charity-night", "afterWeeks": [2, 3] }
      },
      {
        "id": "donate",
        "label": "Send a donation",
        "effects": { "money": -100, "image": 1 },
        "outcome": "They send a lovely card."
      }
    ],
    "cooldownWeeks": 52
  },
  {
    "id": "acme.story.charity-night",
    "trigger": "followUp",
    "title": "Exhibition Night",
    "body": "The function room in {city} is packed. How do you play it?",
    "choices": [
      {
        "id": "show",
        "label": "Put on a show",
        "effects": { "fatigue": 0.05, "morale": 6 },
        "outcome": "Big finishes and bigger cheers.",
        "outcomes": [
          { "weight": 3, "text": "The night raises a fortune.", "effects": { "image": 4 } },
          {
            "weight": 1,
            "hint": "Could go viral",
            "text": "You hit a nine-darter against the local radio DJ. The clip goes everywhere.",
            "actions": [
              { "type": "followerSurge", "percent": 0.1, "min": 500 },
              {
                "type": "news",
                "headline": "Nine-darter at {city} charity night",
                "body": "{name} produced a perfect leg for a good cause."
              }
            ]
          }
        ]
      },
      { "id": "relaxed", "label": "Keep it relaxed", "effects": { "morale": 4 } }
    ]
  }
]

Example: the week before an event#

{
  "id": "acme.story.food-poisoning",
  "trigger": "weekTick",
  "condition": { "chance": 0.01, "hasEventWithinDays": 5, "injured": false },
  "title": "The Dodgy Prawn",
  "body": "A late-night takeaway and a very long night. {nextEvent} is in a few days.",
  "choices": [
    {
      "id": "play",
      "label": "Dry toast and play",
      "effects": { "fatigue": 0.1 },
      "outcomes": [
        { "weight": 2, "text": "You're grey, but upright." },
        {
          "weight": 1,
          "text": "It lingers.",
          "actions": [
            { "type": "modifier", "label": "Under the weather", "weeks": 1, "accuracy": -0.02 }
          ]
        }
      ]
    },
    {
      "id": "withdraw",
      "label": "Pull out of {nextEvent}",
      "actions": [{ "type": "withdraw" }],
      "outcome": "You text the organisers from the bathroom floor. Entry fee refunded."
    }
  ]
}

Checking your stories#

The Mods screen lists problems with each file, line and field. A story with an invalid field (an unknown action type, a severity that doesn't exist, a week range like [4, 2]) disables the mod, like any other schema error. Broken references (a follow-up to a missing story, a sponsorOffer from an unknown sponsor, a rivalry with an unknown player) are removed with a warning and the story still loads.

Written for Darts Player Simulator 0.50.32. Built from the game's own schemas and its base pack.