Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions docs/characters/character-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,52 @@ interface BaseCharacterDefinition {
}
```

### Companion Definition

Companions extend the base with party mechanics, relationship progression, and optional mount:

```typescript
interface CompanionCharacterDefinition extends BaseCharacterDefinition {
kind: 'companion';

breakthroughInteraction?: TalkCharacterInteraction;
talkInteraction?: TalkCharacterInteraction[];
shopInteraction?: ShopCharacterInteraction[];
tradeInteraction?: TradeCharacterInteraction[];
sparInteraction?: SparCharacterInteraction[];
giftInteraction?: GiftCharacterInteraction[];
craftingInteraction?: CraftingCharacterInteraction[];
challengeInteraction?: ChallengeCharacterInteraction[];
patrolInteraction?: PatrolCharacterInteraction[];
aidBreakthroughInteraction?: AidBreakthroughCharacterInteraction[];

/**
* Mount the companion rides. Use a static MountItem or a DynamicMountDefinition
* to swap mounts based on game flags (realm, quest completion, etc.).
* Resolved at render time by `getCharacterMount(def, variables)`.
*/
mount?: MountItem | DynamicMountDefinition;
}

/**
* A mount that resolves at render time from a list of condition/mount pairs.
* Lets a companion ride different mounts depending on in-game state (e.g.
* breakthrough realm, quest completion) without splitting the companion into
* separate definitions per mount.
*/
interface DynamicMountDefinition {
kind: 'dynamic';
/**
* Evaluated in order against the current flag scope. The first entry whose
* `condition` is truthy (non-zero) supplies the mount. If none match, the
* companion is treated as mountless. Conditions follow the same expression
* syntax as event/character conditions elsewhere in the codebase.
*/
mounts: { condition: string; mount: MountItem }[];
}
```

## Character Stats
## Character Stats

Combat statistics for when the character is fought:
Expand Down
65 changes: 62 additions & 3 deletions docs/characters/companions.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,49 @@ interface CompanionCharacterDefinition extends BaseCharacterDefinition {
patrolInteraction?: PatrolCharacterInteraction[];
aidBreakthroughInteraction?: AidBreakthroughCharacterInteraction[];

mount?: MountItem; // Mount for faster travel
/** Mount the companion rides. Use a static MountItem or a DynamicMountDefinition
* to swap mounts based on game flags (realm, quest completion, etc.). */
mount?: MountItem | DynamicMountDefinition;
}
```

## Dynamic Mounts

Companions can ride different mounts depending on game state using `DynamicMountDefinition`. This lets a single companion definition swap between mounts as the player progresses — for example, upgrading from a transport sword to a faster blade after a realm breakthrough, or receiving a quest reward mount that replaces the default.

```typescript
// Static mount — always used when defined
mount: transportSwordMap.qiCondensation,

// Dynamic mount — selects the first matching condition
mount: {
kind: 'dynamic',
mounts: [
// Most-specific conditions first
{
condition: 'metalAndBloodQuest == 1 && realmProgress >= "Late"',
mount: starflashPlus,
},
{
condition: 'realm >= "pillarCreation"',
mount: starflash,
},
{
condition: 'realm >= "qiCondensation"',
mount: brilliantPalanquin,
},
// Fallback — no condition means always matches when reached
{
condition: '1',
mount: transportSwordMap.earlyGame,
},
],
},
```

The `getCharacterMount(def, variables)` utility evaluates the conditions against the current game flags and returns a single `MountItem | undefined`. Conditions follow the same flag-expression syntax used in event conditions. Conditions are checked in order; the first truthy result wins. If no condition matches, the companion has no mount.

## Relationship System
## Relationship System

Companions have a full relationship progression system defined at the character level:
Expand Down Expand Up @@ -149,8 +188,28 @@ const qiCondensationMidDef: CompanionCharacterDefinition = {
},
],

// Mount for this realm
mount: transportSwordMap.qiCondensation,
// Mount for this realm — static or dynamic (see Dynamic Mounts section)
mount: {
kind: 'dynamic',
mounts: [
{
condition: 'realm >= "pillarCreation"',
mount: soaringDisksU,
},
{
condition: 'realm >= "coreFormation"',
mount: soaringDisks,
},
{
condition: 'realm >= "meridianOpening"',
mount: mountainCleaver,
},
{
condition: '1',
mount: transportSwordEarly,
},
],
},

// Interactions available at this realm
talkInteraction: [mainDialogue, questDialogue],
Expand Down