Jump to content

Mod Loader/Stories

From Threadbox

A story pack adds people, conversations, and dress commissions with a mod.json file. You can write short dialogue in JSON or use Yarn Spinner for choices and branching. No DLL is needed.

Start with an example

Rose and Aster starter ZIP has Rose introduce Aster, then adds visits and a pigeon commission. Gerard My Immortal ZIP is a fuller example: Gerard has three portraits and a red-and-black commission with different conversations for each result. Install one example, restart the game, and play past the opening tutorial. Other conversations can appear first, so the new client might not arrive immediately. Back up your save before testing story changes.

What goes in a story pack?

Manifest list What it adds
clients A person based on a game character, with optional portraits, postcard, and body shape.
storyDialogues A scene with a client and lines. Played scenes are remembered in the save.
commissions A job with a client, budget, delivery method, requirements, and outcome dialogue.

Gerard's pack has this layout. Put your own pack in Dressmaker/Mods/YourPackName/ with the same structure:

gerard-my-immortal/
  mod.json
  dialogue.yarn
  portraits/
    verycool_npc_neutral.png
    verycool_npc_smiling.png
    verycool_npc_frowning.png

The pack ID and each client, scene, and commission ID become part of the save. Keep them stable after release. Changing an ID can make the game treat existing progress as new content.

Add a client and portraits

A custom client starts from a native character. baseCharacter supplies the game's existing character setup; your pack can give that client a new name, body shape, and images. Gerard uses Rose as the base:

"clients": [
  {
    "id": "gerard",
    "name": "Gerard",
    "baseCharacter": "Rose",
    "portrait": "portraits/verycool_npc_neutral.png",
    "happyPortrait": "portraits/verycool_npc_smiling.png",
    "sadPortrait": "portraits/verycool_npc_frowning.png"
  }
]

Image paths are relative to mod.json. Use PNG or JPG. The regular portrait is the default face; happyPortrait and sadPortrait are optional. If you leave one out, it uses the regular portrait. You can also provide a postcard image. Replace an image at the same path to update an expression, then restart the game and check it in a fresh conversation. Give every portrait the same canvas size so the character does not jump around when expressions change.

Add other faces under expressions, then switch to them in Yarn with <<set_mod_portrait name>>. See Add an expression.

This changes portraits, not the in-game 3D character model. Optional shape values adjust the copied character's bust, waist, and hips sliders from 0 to 1. For a commission's target body measurements, use measurementsCm on the commission instead.

Add a commission

Gerard's screening_dress commission uses "client": "gerard" and "delivery": "visit". It asks for a Cool score of at least 25, a Goth score of at least 25, only red and black panels, and at least 20% of each colour:

"requirements": [
  { "type": "tag", "tag": "Cool", "value": 25 },
  { "type": "tag", "tag": "Goth", "value": 25 },
  { "type": "color", "colors": ["Red", "Black"] },
  { "type": "colorPercentage", "color": "Red", "value": 20 },
  { "type": "colorPercentage", "color": "Black", "value": 20 }
]

The colour list rules out other colours; the percentage rules make both colours necessary. The pack also sets a 750 coin budget and bust, waist, and hips measurements in centimetres. See requirement types for fabric, garment, accessory, and quality rules.

Allow more than one fabric type

For a fabric requirement, fabricType accepts one type name or an array of allowed types. To allow only lace and silk, add this rule to the commission's requirements list:

{ "type": "fabric", "fabricType": ["Lace", "Silk"] }

Every cut panel must use lace or silk. The dress can use either type on its own or a mixture of both; the list does not require both types. This works like the allowed colors list, but the field is still named fabricType.

To require some of each type, add a separate fabricPercentage rule for each. See fabric type requirements for a complete example. A single name such as "fabricType": "Lace" still works.

Change expressions in JSON dialogue

You don't need a separate Yarn file for a short conversation. Put a portrait command in its own entry in lines, followed by the text that should use it:

"storyDialogues": [{
  "id": "surprise-visit",
  "client": "gerard",
  "lines": [
    "<<set_mod_portrait surprised>>",
    "The screening is tomorrow?",
    "<<set_portrait_neutral>>",
    "All right. Tell me about the dress."
  ]
}]

This uses the surprised expression defined on Gerard below. The same commands work in a commission's dialogue.prompt, complete, failed and excelled lists. The loader turns these lists into Yarn internally. Keep each command and each spoken line in a separate string. If a matching node exists in a .yarn file, that node takes precedence over the JSON lines.

Write dialogue with Yarn Spinner

Yarn Spinner is the dialogue language used by Dressmaker. A node is one conversation with a title and a body. Lines beginning -> are choices; indented lines run after that choice. Commands in <<angle brackets>> can change portraits or control the conversation. The loader compiles the files listed in yarnFiles when it loads your pack, so you do not need a separate Yarn build step.

List the file in mod.json: "yarnFiles": ["dialogue.yarn"]. The node title must match the content it replaces. For Gerard's commission prompt, the pack ID yuuma.gerard-my-immortal becomes yuuma_gerard_my_immortal in the title:

title: Mod_yuuma_gerard_my_immortal_screening_dress_Prompt
---
<<set_portrait_neutral>>
I was told you handle dress emergencies. #line:gerard_intro_02
-> What colours would you like? #line:gerard_choice_colours
    <<set_portrait_happy>>
    Red and black. #line:gerard_reply_colours
-> How dramatic should it be? #line:gerard_choice_style
    <<set_portrait_sad>>
    Very dramatic. #line:gerard_reply_style
===

This is a shortened example based on Gerard's pack. Each #line: tag should be unique in your Yarn files. Gerard has four commission nodes ending in _Prompt, _Complete, _Failed, and _Excelled. The game chooses the result node after checking the finished dress. A story scene uses Mod_ followed by its full scene ID, without a result suffix. A matching Yarn node replaces the short lines in mod.json.

Use <<set_portrait_neutral>>, <<set_portrait_happy>>, or <<set_portrait_sad>> to switch between Dressmaker's three usual slots. Use <<set_mod_portrait dramatic>> for a name in the current client's expressions object. Switch back with <<set_portrait_neutral>>. If the name is missing, the portrait stays as it was and the loader writes a warning to the log. The postcard image is separate from dialogue portraits.

Use your own expression

Suppose you draw a surprised face for Gerard and save it as portraits/verycool_npc_surprised.png. Add the expressions field inside Gerard's client entry in mod.json:

{
  "id": "gerard",
  "name": "Gerard",
  "baseCharacter": "Rose",
  "portrait": "portraits/verycool_npc_neutral.png",
  "expressions": {
    "surprised": "portraits/verycool_npc_surprised.png"
  }
}

Then insert these lines into the existing Mod_yuuma_gerard_my_immortal_screening_dress_Prompt node in dialogue.yarn:

<<set_mod_portrait surprised>>
The screening is tomorrow? #line:gerard_surprised_01
<<set_portrait_neutral>>
All right. Tell me about the dress. #line:gerard_surprised_02

The name after set_mod_portrait must match the JSON key. You can define up to 32 names per client; each can use letters, numbers, and underscores and must start with a letter. The image file must exist in the pack, or the client will be skipped with an error in BepInEx/LogOutput.log. Another client can use the same expression name with a different image.

Control when a scene appears

Put availability on a storyDialogues or commissions entry when a scene should wait for progress in the save. This example waits until level 5 and after your pack's introduction scene has played:

"availability": {
  "minimumLevel": 5,
  "afterDialogues": ["introduction"]
}

All listed conditions must be true. You can also use afterStory, minimumRelationship, and afterQuests. Put the next visit's requirements in availability so the loader waits for the earlier story or commission to be completed. Pigeon commissions also need the game's pigeon feature unlocked.

For a choice that changes only the current conversation, Yarn supports <<set>> and <<if>>:

<<set $askedAboutColour = false>>
-> Ask about colour. #line:colour_choice
    <<set $askedAboutColour = true>>
    Red and black. #line:colour_reply
-> Talk about the deadline. #line:deadline_choice
    The screening is tomorrow. #line:deadline_reply
<<if $askedAboutColour>>
Thanks for checking the colours. #line:colour_followup
<<else>>
Please remember: red and black. #line:deadline_followup
<<endif>>

Do not rely on a Yarn variable to survive saving and quitting. Use afterDialogues or afterQuests for progress between visits. The Yarn Spinner flow control guide covers more branching syntax.

Test the story

  1. Back up a save before installing your story pack.
  2. Trigger each scene and commission in its intended order. Try every choice, portrait, and commission result.
  3. Save and restart. Check that completed scenes do not repeat unexpectedly and gated scenes appear when they should.
  4. Read BepInEx/LogOutput.log for a missing image, client, quest, tag, or Yarn node.

Field reference

The story fields page lists the client, commission, requirement, availability, measurement, and Yarn fields. Native IDs and outcomes are in BepInEx/config/DressmakerMods/story-reference.json.

Give patterns as friendship rewards

A client can give the player a garment pattern when they reach a friendship level. Add friendshipRewards to the client, and set unlockByDefault to false on the garment so it isn't available from the start.

This mod.json adds Aster and a bodice pattern. The player receives the pattern at friendship level 2. Replace the copied bodice with your own garment definition when making your pack.

{
  "id": "aster-patterns",
  "name": "Aster's patterns",
  "garmentComponents": [
    {
      "id": "gift-bodice",
      "name": "Aster's bodice",
      "baseComponent": "Split Crew Neck Bodice",
      "unlockByDefault": false
    }
  ],
  "clients": [
    {
      "id": "aster",
      "name": "Aster",
      "baseCharacter": "Rose",
      "friendshipRewards": [
        { "level": 2, "garments": ["gift-bodice"] }
      ]
    }
  ]
}

Use whole-number levels from 1 to 5. Each level can list up to 64 garment IDs. An ID such as gift-bodice refers to this pack; use another-pack.gift-bodice for a garment in another pack. Native garment IDs work too, though this doesn't lock patterns the game already gives the player.

Rewards appear in the game's friendship reward list. Players who have already reached the level receive the pattern when they load their save. Once earned, it stays unlocked, even if friendship drops. Unlocks belong to each save slot.

If a pattern was already unlocked before you added unlockByDefault: false, it stays unlocked. Test a new reward with a fresh save. Removing a story pack stops it granting new rewards; its retained client data is only used to keep saves readable. Missing garments use the loader's usual recovery behaviour.

One garment can be offered by several clients: reaching any of their required levels unlocks it. Garments included in a shared dress become available on import without requiring the recipient to install the story. An already installed copy keeps its own unlock rules.

For plugins, see Register a reward in C#.

Back to the Mod Loader guide