Skip to content
Open
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
137 changes: 137 additions & 0 deletions docs/dev/BIRTH_AUGUR_AUTOMATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Birth Augur / Lucky Roll Automation

## Context

Issues [#684](https://github.com/foundryvtt-dcc/dcc/issues/684) and [#470](https://github.com/foundryvtt-dcc/dcc/issues/470) request automation of birth augur effects. Currently, the "Lucky Roll" field is a free-text textarea with no mechanical effect — users must manually track and apply their birth augur modifier.

DCC RAW: the birth augur modifier is locked at character creation (level-1 luck mod). Some groups house-rule it to float with current luck. Both modes need support.

The system already applies luck mod to crits, fumbles, and turn unholy for ALL characters (regardless of birth augur). Phase 1 leaves that behavior unchanged.

## Approach: `_getBirthAugurBonusFor()` Helper

A helper method on `DCCActor` that returns the effective birth augur modifier for a given effect type. Each compute function calls this helper for its relevant effect types and adds the result. This avoids mutating persisted fields and handles the double-compute pattern cleanly (same inputs → same outputs).

```javascript
_getBirthAugurBonusFor (...effectTypes) {
if (!this.isPC) return 0
const augurIndex = this.system.details.birthAugurIndex
if (augurIndex == null) return 0
const augur = BIRTH_AUGURS[augurIndex - 1]
if (!augur || !effectTypes.includes(augur.effect)) return 0
return this.system.config.birthAugurMode === 'floating'
? this.system.abilities.lck.mod
: this.system.details.birthAugurLuckMod
}
```

## Phase 1 (MVP): Data Model + UI + 14 "Easy" Augurs

### Files to Create

**`module/birth-augurs.mjs`** — Canonical table of all 30 birth augurs:
```javascript
export const BIRTH_AUGURS = [
{ index: 1, key: 'harshWinter', effect: 'allAttack' },
{ index: 2, key: 'theBull', effect: 'meleeAttack' },
{ index: 3, key: 'fortunateDate', effect: 'missileAttack' },
// ...etc for all 30, with i18n keys for name and description
]
```

Effect types for Phase 1 automation:

| # | Augur | Effect Type | Target |
|---|-------|-------------|--------|
| 1 | Harsh winter | `allAttack` | melee + missile attack adjustment |
| 2 | The bull | `meleeAttack` | melee attack adjustment |
| 3 | Fortunate date | `missileAttack` | missile attack adjustment |
| 6 | Born on the battlefield | `allDamage` | melee + missile damage adjustment |
| 7 | Path of the bear | `meleeDamage` | melee damage adjustment |
| 8 | Hawkeye | `missileDamage` | missile damage adjustment |
| 13 | Seventh son | `spellCheck` | spellCheckOtherMod |
| 17 | Lucky sign | `allSaves` | all three save otherBonus |
| 20 | Struck by lightning | `reflexSave` | ref save otherBonus |
| 21 | Lived through famine | `fortSave` | frt save otherBonus |
| 22 | Resisted temptation | `willSave` | wil save otherBonus |
| 23 | Charmed house | `armorClass` | AC computation |
| 24 | Speed of the cobra | `initiative` | init computation |
| 30 | Wild child | `speed` | speed (×5 per +1/-1) |

Remaining augurs (#4, 5, 9, 10–12, 14–16, 18–19, 25–29) are defined in the table but their effect is not yet automated — selecting them stores the data but won't auto-apply until later phases.

### Files to Modify

**`module/data/actor/base-actor.mjs`**
- Add `birthAugurIndex: new NumberField({ initial: null, nullable: true, integer: true, min: 1, max: 30 })` to `details`
- Add migration in `migrateData()` to auto-populate `birthAugurIndex` from existing `birthAugur` text (best-effort pattern matching)

**`module/data/actor/player-data.mjs`**
- Add `birthAugurMode: new StringField({ initial: 'static' })` to `config` SchemaField

**`module/actor.js`**
- Add `import { BIRTH_AUGURS } from './birth-augurs.mjs'`
- Add `_getBirthAugurBonusFor(...effectTypes)` helper method
- `computeMeleeAndMissileAttackAndDamage()`: add birth augur bonus for `allAttack`/`meleeAttack`/`missileAttack`/`allDamage`/`meleeDamage`/`missileDamage`
- `computeSavingThrows()`: add birth augur bonus for `allSaves`/`reflexSave`/`fortSave`/`willSave`
- `computeSpellCheck()`: add birth augur bonus for `spellCheck`
- `computeInitiative()`: add birth augur bonus for `initiative`
- `prepareDerivedData()` AC section: add birth augur bonus for `armorClass`
- `prepareDerivedData()` speed section: add birth augur bonus for `speed` (×5)
- Store `_computedBirthAugurMod` and `_computedBirthAugurEffect` for UI display

**`templates/actor-partial-pc-common.html`**
- Replace "Lucky Roll" textarea section with: dropdown selector (30 augurs + "Custom") + textarea (kept for display/custom text)
- Show computed birth augur modifier value

**`templates/dialog-actor-config.html`**
- Add Birth Augur Mode selector (Static / Floating) with tooltip explaining RAW vs house rule

**`module/actor-sheet.js`**
- Pass `birthAugurs` array with selection state to template context

**`lang/en.json`** (and other lang files)
- Add i18n keys for all 30 augur names, descriptions, and UI labels

**`module/pc-parser.js`**
- When parsing `luckySign`, also set `birthAugurIndex` by matching against the augur table

### Tests

**`module/__tests__/birth-augur.test.js`** (new)
- `_getBirthAugurBonusFor()` returns correct values per effect type
- Static mode uses `birthAugurLuckMod`, floating mode uses `lck.mod`
- Null index → no bonus
- Each automated augur modifies the correct computed values

**`module/__tests__/actor.test.js`** (extend)
- Compute functions incorporate birth augur bonus correctly
- Birth augur interacts correctly with Active Effects (they stack)

## Phase 2: Skills + HP Augurs

**Skill augurs (#10, #11, #12)** — Apply at roll time in `rollSkillCheck()`:
- Born under the loom (#10): Add bonus to all skill rolls
- Fox's cunning (#11): Add bonus to findTrap/disableTrap rolls
- Four-leafed clover (#12): Add bonus to detectSecretDoors rolls

**HP augur (#25 Bountiful harvest)** — In `prepareDerivedData()`, add `birthAugurMod × level` to `hp.max`

## Phase 3: Context-Dependent + Gated Augurs

**Context-dependent** (#4 unarmed, #5 mounted, #9 pack hunter, #14 spell damage, #16 magical healing, #18 trap saves, #19 poison saves, #27 corruption): Require roll-time context flags. Some may remain informational if context can't be determined.

**Gated luck** (#15 turn unholy, #26 crits, #28 fumbles): Add opt-in world setting to only apply luck to these when birth augur matches. Default OFF to preserve current behavior.

**Informational** (#29 birdsong/languages): No automation planned.

## Verification

1. `npm test` — all existing + new tests pass
2. `npm run format` — code passes lint
3. Manual testing in Foundry:
- Create character, select each of the 14 automated augurs, verify computed values change
- Toggle static/floating mode, verify modifier source changes
- Apply Active Effects to same fields, verify they stack correctly
- Import character via PC parser, verify augur auto-detected
- Existing characters with text-only augurs continue working unchanged
67 changes: 67 additions & 0 deletions docs/user-guide/Birth-Augur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Birth Augur (Lucky Roll)

In DCC RPG, every character is born under a particular birth augur that modifies a specific game mechanic based on their luck modifier. The DCC system can now automate these modifiers for you.

## Selecting a Birth Augur

On the **Character** tab, the **Lucky Roll** section has a dropdown where you can select your character's birth augur from the full list of 30 options.

When you select a birth augur, the system will automatically apply the luck modifier to the appropriate computed value (attack rolls, saving throws, AC, etc.). The current modifier value is displayed below the dropdown.

The free-text field below the dropdown is kept for notes, custom text, or backwards compatibility with existing characters.

## Setting the Luck Modifier

The birth augur modifier is set via the **Birth Augur Luck Mod** field in the details section of the character. This should be the character's luck modifier at the time of character creation (their level-0 luck mod).

If you import a character via the PC parser, the system will attempt to auto-detect the birth augur from the lucky sign text and set the index automatically.

## Static vs Floating Mode

By RAW (Rules As Written), the birth augur modifier is locked at character creation — it uses the luck modifier the character had at level 0, regardless of any later changes to their luck score. This is the default **Static** mode.

Some groups house-rule that the birth augur modifier floats with the character's current luck score. To enable this:

1. Click the **Toggle Controls** button (three vertical dots) in the title bar
2. Click **Config**
3. Find the **Birth Augur Mode** dropdown
4. Change it from **Static (RAW)** to **Floating (House Rule)**

In **Floating** mode, the birth augur bonus will always use the character's current luck modifier instead of the stored birth augur luck mod value.

## Automated Augurs

The following 14 birth augurs are currently automated. Selecting one of these will modify the corresponding computed values on the character sheet:

| # | Birth Augur | Effect |
|---|-------------|--------|
| 1 | Harsh winter | All attack rolls (melee and missile) |
| 2 | The bull | Melee attack rolls |
| 3 | Fortunate date | Missile fire attack rolls |
| 6 | Born on the battlefield | All damage rolls (melee and missile) |
| 7 | Path of the bear | Melee damage rolls |
| 8 | Hawkeye | Missile fire damage rolls |
| 13 | Seventh son | Spell checks |
| 17 | Lucky sign | All saving throws |
| 20 | Struck by lightning | Reflex saving throws |
| 21 | Lived through famine | Fortitude saving throws |
| 22 | Resisted temptation | Willpower saving throws |
| 23 | Charmed house | Armor Class |
| 24 | Speed of the cobra | Initiative |
| 30 | Wild child | Speed (multiplied by 5 feet per modifier point) |

## Non-Automated Augurs

The remaining augurs (#4, 5, 9–12, 14–16, 18–19, 25–29) can be selected from the dropdown to record the character's birth augur, but their effects are not yet automated. You will need to apply these modifiers manually when they are relevant (e.g. during specific skill checks, when unarmed, when mounted, etc.).

## Interaction with Active Effects

Birth augur bonuses stack with Active Effects. For example, if a character has the "Charmed house" augur (+1 AC) and also has an Active Effect that adds +2 to AC, both bonuses will be applied.

## Existing Characters

Characters created before this feature was added will continue to work unchanged. Their free-text birth augur field is preserved. If the system can recognise the augur from the existing text, it will automatically set the birth augur index during migration. Otherwise, you can manually select the correct augur from the dropdown.

## Luck, Crits, Fumbles, and Turn Unholy

The system already applies the luck modifier to critical hits, fumbles, and turn unholy checks for all characters regardless of birth augur. This existing behaviour is unchanged — those bonuses are applied whether or not the character's birth augur matches those effects.
57 changes: 57 additions & 0 deletions lang/cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,63 @@
"DCC.BadValueFormulaWarning": "物品值字段的公式错误!",
"DCC.BaseACAbilityConfig": "基础 AC 属性",
"DCC.BirthAugur": "出生征兆",
"DCC.BirthAugurMode": "出生征兆模式",
"DCC.BirthAugurModeHint": "静态模式使用角色创建时的幸运修正值(规则原文)。浮动模式使用当前幸运修正值(房规)。",
"DCC.BirthAugurModeStatic": "静态(规则原文)",
"DCC.BirthAugurModeFloating": "浮动(房规)",
"DCC.BirthAugurMod": "出生征兆修正值",
"DCC.BirthAugurNone": "— 无 —",
"DCC.BirthAugurNotAutomated": "(尚未自动化)",
"DCC.BirthAugurItem": "出生预兆",
"DCC.BirthAugurDetails": "出生预兆详情",
"DCC.BirthAugurEffect": "效果类型",
"DCC.BirthAugurEffectNone": "无(未自动化)",
"DCC.BirthAugurEffectAllAttack": "所有攻击骰",
"DCC.BirthAugurEffectMeleeAttack": "近战攻击骰",
"DCC.BirthAugurEffectMissileAttack": "远程攻击骰",
"DCC.BirthAugurEffectAllDamage": "所有伤害骰",
"DCC.BirthAugurEffectMeleeDamage": "近战伤害骰",
"DCC.BirthAugurEffectMissileDamage": "远程伤害骰",
"DCC.BirthAugurEffectSpellCheck": "施法检定",
"DCC.BirthAugurEffectAllSaves": "所有豁免投骰",
"DCC.BirthAugurEffectReflexSave": "反射豁免投骰",
"DCC.BirthAugurEffectFortSave": "强韧豁免投骰",
"DCC.BirthAugurEffectWillSave": "意志豁免投骰",
"DCC.BirthAugurEffectArmorClass": "护甲等级",
"DCC.BirthAugurEffectInitiative": "先攻",
"DCC.BirthAugurEffectSpeed": "速度",
"DCC.BirthAugurDragHint": "从合集中拖拽",
"DCC.BirthAugurOpenCompendium": "打开出生预兆合集",
"DCC.BirthAugur.harshWinter": "严冬:所有攻击检定",
"DCC.BirthAugur.theBull": "公牛:近战攻击检定",
"DCC.BirthAugur.fortunateDate": "吉日:远程攻击检定",
"DCC.BirthAugur.raisedByWolves": "狼群养大:徒手攻击检定",
"DCC.BirthAugur.conceivedOnHorseback": "马背受孕:骑乘攻击/伤害检定",
"DCC.BirthAugur.bornOnTheBattlefield": "战场出生:伤害检定",
"DCC.BirthAugur.pathOfTheBear": "熊之道:近战伤害检定",
"DCC.BirthAugur.hawkeye": "鹰眼:远程伤害检定",
"DCC.BirthAugur.packHunter": "群猎者:0级训练武器的攻击/伤害",
"DCC.BirthAugur.bornUnderTheLoom": "织机下出生:技能检定",
"DCC.BirthAugur.foxsCunning": "狐狸的狡猾:寻找/解除陷阱",
"DCC.BirthAugur.fourLeafedClover": "四叶草:寻找暗门",
"DCC.BirthAugur.seventhSon": "第七子:施法检定",
"DCC.BirthAugur.theDwarvenStar": "矮人之星:法术伤害",
"DCC.BirthAugur.unholy": "邪恶:驱散邪恶检定",
"DCC.BirthAugur.scepter": "权杖:治疗法术",
"DCC.BirthAugur.luckySign": "幸运标记:所有豁免检定",
"DCC.BirthAugur.guardianAngel": "守护天使:对抗陷阱的豁免",
"DCC.BirthAugur.survivedThePlague": "瘟疫幸存者:对抗毒素的豁免",
"DCC.BirthAugur.struckByLightning": "被闪电击中:反射豁免检定",
"DCC.BirthAugur.livedThroughFamine": "饥荒幸存者:强韧豁免检定",
"DCC.BirthAugur.resistedTemptation": "抵制诱惑:意志豁免检定",
"DCC.BirthAugur.charmedHouse": "福宅:护甲等级",
"DCC.BirthAugur.speedOfTheCobra": "眼镜蛇之速:先攻",
"DCC.BirthAugur.bountifulHarvest": "丰收:生命值(每级)",
"DCC.BirthAugur.warriorsBattle": "战士之战:重击表",
"DCC.BirthAugur.markOfTheDemon": "恶魔之印:腐化检定",
"DCC.BirthAugur.doomedToFail": "注定失败:失误",
"DCC.BirthAugur.twinned": "鸟鸣:语言",
"DCC.BirthAugur.wildChild": "野孩子:速度(×5英尺)",
"DCC.BlindnessDeafness": "目盲\/耳聋",
"DCC.Bonus": "奖励",
"DCC.BrokenLimbs": "残破肢体",
Expand Down
57 changes: 57 additions & 0 deletions lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,63 @@
"DCC.BadValueFormulaWarning": "Ungültige Formel für Wert des Gegenstands!",
"DCC.BaseACAbilityConfig": "RK-Modifikator",
"DCC.BirthAugur": "Geburtszeichen",
"DCC.BirthAugurMode": "Geburtszeichen-Modus",
"DCC.BirthAugurModeHint": "Statisch verwendet den Glücksmodifikator bei der Charaktererstellung (RAW). Fließend verwendet den aktuellen Glücksmodifikator (Hausregel).",
"DCC.BirthAugurModeStatic": "Statisch (RAW)",
"DCC.BirthAugurModeFloating": "Fließend (Hausregel)",
"DCC.BirthAugurMod": "Geburtszeichen-Modifikator",
"DCC.BirthAugurNone": "— Keines —",
"DCC.BirthAugurNotAutomated": "(noch nicht automatisiert)",
"DCC.BirthAugurItem": "Geburtszeichen",
"DCC.BirthAugurDetails": "Geburtszeichen Details",
"DCC.BirthAugurEffect": "Effektart",
"DCC.BirthAugurEffectNone": "Keiner (nicht automatisiert)",
"DCC.BirthAugurEffectAllAttack": "Alle Angriffswürfe",
"DCC.BirthAugurEffectMeleeAttack": "Nahkampf-Angriffswürfe",
"DCC.BirthAugurEffectMissileAttack": "Fernkampf-Angriffswürfe",
"DCC.BirthAugurEffectAllDamage": "Alle Schadenswürfe",
"DCC.BirthAugurEffectMeleeDamage": "Nahkampf-Schadenswürfe",
"DCC.BirthAugurEffectMissileDamage": "Fernkampf-Schadenswürfe",
"DCC.BirthAugurEffectSpellCheck": "Zauberproben",
"DCC.BirthAugurEffectAllSaves": "Alle Rettungswürfe",
"DCC.BirthAugurEffectReflexSave": "Reflexrettungswürfe",
"DCC.BirthAugurEffectFortSave": "Zähigkeitsrettungswürfe",
"DCC.BirthAugurEffectWillSave": "Willensrettungswürfe",
"DCC.BirthAugurEffectArmorClass": "Rüstungsklasse",
"DCC.BirthAugurEffectInitiative": "Initiative",
"DCC.BirthAugurEffectSpeed": "Geschwindigkeit",
"DCC.BirthAugurDragHint": "Aus Kompendium ziehen",
"DCC.BirthAugurOpenCompendium": "Geburtszeichen-Kompendium öffnen",
"DCC.BirthAugur.harshWinter": "Harter Winter: Alle Angriffswürfe",
"DCC.BirthAugur.theBull": "Der Stier: Nahkampf-Angriffswürfe",
"DCC.BirthAugur.fortunateDate": "Glücksdatum: Fernkampf-Angriffswürfe",
"DCC.BirthAugur.raisedByWolves": "Von Wölfen aufgezogen: Waffenlose Angriffswürfe",
"DCC.BirthAugur.conceivedOnHorseback": "Auf dem Pferderücken gezeugt: Berittene Angriffs-/Schadenswürfe",
"DCC.BirthAugur.bornOnTheBattlefield": "Auf dem Schlachtfeld geboren: Schadenswürfe",
"DCC.BirthAugur.pathOfTheBear": "Pfad des Bären: Nahkampf-Schadenswürfe",
"DCC.BirthAugur.hawkeye": "Falkenauge: Fernkampf-Schadenswürfe",
"DCC.BirthAugur.packHunter": "Rudeljäger: Angriff/Schaden für trainierte Stufe-0-Waffen",
"DCC.BirthAugur.bornUnderTheLoom": "Unter dem Webstuhl geboren: Fertigkeitswürfe",
"DCC.BirthAugur.foxsCunning": "List des Fuchses: Fallen finden/entschärfen",
"DCC.BirthAugur.fourLeafedClover": "Vierblättriges Kleeblatt: Geheimtüren finden",
"DCC.BirthAugur.seventhSon": "Siebter Sohn: Zauberwürfe",
"DCC.BirthAugur.theDwarvenStar": "Der Zwergenstern: Zauberschaden",
"DCC.BirthAugur.unholy": "Unheilig: Unheiliges-vertreiben-Würfe",
"DCC.BirthAugur.scepter": "Zepter: Heilzauber",
"DCC.BirthAugur.luckySign": "Glückszeichen: Alle Rettungswürfe",
"DCC.BirthAugur.guardianAngel": "Schutzengel: Rettungswürfe gegen Fallen",
"DCC.BirthAugur.survivedThePlague": "Die Pest überlebt: Rettungswürfe gegen Gift",
"DCC.BirthAugur.struckByLightning": "Vom Blitz getroffen: Reflex-Rettungswürfe",
"DCC.BirthAugur.livedThroughFamine": "Hungersnot überlebt: Zähigkeits-Rettungswürfe",
"DCC.BirthAugur.resistedTemptation": "Versuchung widerstanden: Willens-Rettungswürfe",
"DCC.BirthAugur.charmedHouse": "Glückshaus: Rüstungsklasse",
"DCC.BirthAugur.speedOfTheCobra": "Schnelligkeit der Kobra: Initiative",
"DCC.BirthAugur.bountifulHarvest": "Reiche Ernte: Trefferpunkte (pro Stufe)",
"DCC.BirthAugur.warriorsBattle": "Kriegerschlacht: Kritische-Treffer-Tabellen",
"DCC.BirthAugur.markOfTheDemon": "Zeichen des Dämons: Korruptionswürfe",
"DCC.BirthAugur.doomedToFail": "Zum Scheitern verurteilt: Patzer",
"DCC.BirthAugur.twinned": "Vogelgesang: Sprachen",
"DCC.BirthAugur.wildChild": "Wildkind: Geschwindigkeit (×1,5 m)",
"DCC.BlindnessDeafness": "Blindheit/Taubheit",
"DCC.Bonus": "Bonus",
"DCC.BrokenLimbs": "Gebrochene Gliedmaßen",
Expand Down
Loading