diff --git a/docs/dev/BIRTH_AUGUR_AUTOMATION.md b/docs/dev/BIRTH_AUGUR_AUTOMATION.md new file mode 100644 index 00000000..634771fa --- /dev/null +++ b/docs/dev/BIRTH_AUGUR_AUTOMATION.md @@ -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 diff --git a/docs/user-guide/Birth-Augur.md b/docs/user-guide/Birth-Augur.md new file mode 100644 index 00000000..f6ff9772 --- /dev/null +++ b/docs/user-guide/Birth-Augur.md @@ -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. diff --git a/lang/cn.json b/lang/cn.json index b79aae87..b7f0f595 100644 --- a/lang/cn.json +++ b/lang/cn.json @@ -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": "残破肢体", diff --git a/lang/de.json b/lang/de.json index bde49fe2..9665244c 100644 --- a/lang/de.json +++ b/lang/de.json @@ -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", diff --git a/lang/en.json b/lang/en.json index 93022b1a..1ef2f860 100644 --- a/lang/en.json +++ b/lang/en.json @@ -92,6 +92,63 @@ "DCC.BadValueFormulaWarning": "Bad formula in item value field!", "DCC.BaseACAbilityConfig": "Base AC Ability", "DCC.BirthAugur": "Birth Augur", + "DCC.BirthAugurMode": "Birth Augur Mode", + "DCC.BirthAugurModeHint": "Static uses the luck modifier at character creation (RAW). Floating uses the current luck modifier (house rule).", + "DCC.BirthAugurModeStatic": "Static (RAW)", + "DCC.BirthAugurModeFloating": "Floating (House Rule)", + "DCC.BirthAugurMod": "Birth Augur Modifier", + "DCC.BirthAugurNone": "— None —", + "DCC.BirthAugurNotAutomated": "(not yet automated)", + "DCC.BirthAugurItem": "Birth Augur", + "DCC.BirthAugurDetails": "Birth Augur Details", + "DCC.BirthAugurEffect": "Effect Type", + "DCC.BirthAugurEffectNone": "None (not automated)", + "DCC.BirthAugurEffectAllAttack": "All attack rolls", + "DCC.BirthAugurEffectMeleeAttack": "Melee attack rolls", + "DCC.BirthAugurEffectMissileAttack": "Missile fire attack rolls", + "DCC.BirthAugurEffectAllDamage": "All damage rolls", + "DCC.BirthAugurEffectMeleeDamage": "Melee damage rolls", + "DCC.BirthAugurEffectMissileDamage": "Missile fire damage rolls", + "DCC.BirthAugurEffectSpellCheck": "Spell checks", + "DCC.BirthAugurEffectAllSaves": "All saving throws", + "DCC.BirthAugurEffectReflexSave": "Reflex saving throws", + "DCC.BirthAugurEffectFortSave": "Fortitude saving throws", + "DCC.BirthAugurEffectWillSave": "Willpower saving throws", + "DCC.BirthAugurEffectArmorClass": "Armor Class", + "DCC.BirthAugurEffectInitiative": "Initiative", + "DCC.BirthAugurEffectSpeed": "Speed", + "DCC.BirthAugurDragHint": "Drag from compendium", + "DCC.BirthAugurOpenCompendium": "Open Birth Augurs compendium", + "DCC.BirthAugur.harshWinter": "Harsh winter: All attack rolls", + "DCC.BirthAugur.theBull": "The bull: Melee attack rolls", + "DCC.BirthAugur.fortunateDate": "Fortunate date: Missile fire attack rolls", + "DCC.BirthAugur.raisedByWolves": "Raised by wolves: Unarmed attack rolls", + "DCC.BirthAugur.conceivedOnHorseback": "Conceived on horseback: Mounted attack/damage rolls", + "DCC.BirthAugur.bornOnTheBattlefield": "Born on the battlefield: Damage rolls", + "DCC.BirthAugur.pathOfTheBear": "Path of the bear: Melee damage rolls", + "DCC.BirthAugur.hawkeye": "Hawkeye: Missile fire damage rolls", + "DCC.BirthAugur.packHunter": "Pack hunter: Attack/damage for 0-level trained weapons", + "DCC.BirthAugur.bornUnderTheLoom": "Born under the loom: Skill checks", + "DCC.BirthAugur.foxsCunning": "Fox's cunning: Find/disable traps", + "DCC.BirthAugur.fourLeafedClover": "Four-leafed clover: Find secret doors", + "DCC.BirthAugur.seventhSon": "Seventh son: Spell checks", + "DCC.BirthAugur.theDwarvenStar": "The Dwarven star: Spell damage", + "DCC.BirthAugur.unholy": "Unholy: Turn unholy checks", + "DCC.BirthAugur.scepter": "Scepter: Healing spells", + "DCC.BirthAugur.luckySign": "Lucky sign: Saving throws", + "DCC.BirthAugur.guardianAngel": "Guardian angel: Saves vs. traps", + "DCC.BirthAugur.survivedThePlague": "Survived the plague: Saves vs. poison", + "DCC.BirthAugur.struckByLightning": "Struck by lightning: Reflex saving throws", + "DCC.BirthAugur.livedThroughFamine": "Lived through famine: Fortitude saving throws", + "DCC.BirthAugur.resistedTemptation": "Resisted temptation: Willpower saving throws", + "DCC.BirthAugur.charmedHouse": "Charmed house: Armor Class", + "DCC.BirthAugur.speedOfTheCobra": "Speed of the cobra: Initiative", + "DCC.BirthAugur.bountifulHarvest": "Bountiful harvest: Hit points (per level)", + "DCC.BirthAugur.warriorsBattle": "Warrior's battle: Critical hit tables", + "DCC.BirthAugur.markOfTheDemon": "Mark of the demon: Corruption rolls", + "DCC.BirthAugur.doomedToFail": "Doomed to fail: Fumbles", + "DCC.BirthAugur.twinned": "Birdsong: Languages", + "DCC.BirthAugur.wildChild": "Wild child: Speed (×5 feet)", "DCC.BlindnessDeafness": "Blindness/Deafness", "DCC.Bonus": "Bonus", "DCC.BrokenLimbs": "Broken limbs", diff --git a/lang/es.json b/lang/es.json index c78eab4d..45fdfc1a 100644 --- a/lang/es.json +++ b/lang/es.json @@ -92,6 +92,63 @@ "DCC.BadValueFormulaWarning": "¡Fórmula incorrecta en el campo de valor del objeto!", "DCC.BaseACAbilityConfig": "Habilidad base para CA", "DCC.BirthAugur": "Augurio de nacimiento", + "DCC.BirthAugurMode": "Modo de augurio natal", + "DCC.BirthAugurModeHint": "Estático usa el modificador de suerte de la creación del personaje (RAW). Flotante usa el modificador de suerte actual (regla de la casa).", + "DCC.BirthAugurModeStatic": "Estático (RAW)", + "DCC.BirthAugurModeFloating": "Flotante (Regla de la casa)", + "DCC.BirthAugurMod": "Modificador de augurio natal", + "DCC.BirthAugurNone": "— Ninguno —", + "DCC.BirthAugurNotAutomated": "(aún no automatizado)", + "DCC.BirthAugurItem": "Augurio de Nacimiento", + "DCC.BirthAugurDetails": "Detalles del Augurio", + "DCC.BirthAugurEffect": "Tipo de Efecto", + "DCC.BirthAugurEffectNone": "Ninguno (no automatizado)", + "DCC.BirthAugurEffectAllAttack": "Todas las tiradas de ataque", + "DCC.BirthAugurEffectMeleeAttack": "Tiradas de ataque cuerpo a cuerpo", + "DCC.BirthAugurEffectMissileAttack": "Tiradas de ataque a distancia", + "DCC.BirthAugurEffectAllDamage": "Todas las tiradas de daño", + "DCC.BirthAugurEffectMeleeDamage": "Tiradas de daño cuerpo a cuerpo", + "DCC.BirthAugurEffectMissileDamage": "Tiradas de daño a distancia", + "DCC.BirthAugurEffectSpellCheck": "Pruebas de conjuro", + "DCC.BirthAugurEffectAllSaves": "Todas las tiradas de salvación", + "DCC.BirthAugurEffectReflexSave": "Tiradas de salvación de Reflejos", + "DCC.BirthAugurEffectFortSave": "Tiradas de salvación de Fortaleza", + "DCC.BirthAugurEffectWillSave": "Tiradas de salvación de Voluntad", + "DCC.BirthAugurEffectArmorClass": "Clase de Armadura", + "DCC.BirthAugurEffectInitiative": "Iniciativa", + "DCC.BirthAugurEffectSpeed": "Velocidad", + "DCC.BirthAugurDragHint": "Arrastrar desde compendio", + "DCC.BirthAugurOpenCompendium": "Abrir compendio de Augurios", + "DCC.BirthAugur.harshWinter": "Invierno crudo: Todas las tiradas de ataque", + "DCC.BirthAugur.theBull": "El toro: Tiradas de ataque cuerpo a cuerpo", + "DCC.BirthAugur.fortunateDate": "Fecha afortunada: Tiradas de ataque a distancia", + "DCC.BirthAugur.raisedByWolves": "Criado por lobos: Tiradas de ataque desarmado", + "DCC.BirthAugur.conceivedOnHorseback": "Concebido a caballo: Tiradas de ataque/daño montado", + "DCC.BirthAugur.bornOnTheBattlefield": "Nacido en el campo de batalla: Tiradas de daño", + "DCC.BirthAugur.pathOfTheBear": "Senda del oso: Tiradas de daño cuerpo a cuerpo", + "DCC.BirthAugur.hawkeye": "Ojo de halcón: Tiradas de daño a distancia", + "DCC.BirthAugur.packHunter": "Cazador en manada: Ataque/daño con armas de nivel 0", + "DCC.BirthAugur.bornUnderTheLoom": "Nacido bajo el telar: Pruebas de habilidad", + "DCC.BirthAugur.foxsCunning": "Astucia del zorro: Encontrar/desactivar trampas", + "DCC.BirthAugur.fourLeafedClover": "Trébol de cuatro hojas: Encontrar puertas secretas", + "DCC.BirthAugur.seventhSon": "Séptimo hijo: Pruebas de conjuro", + "DCC.BirthAugur.theDwarvenStar": "La estrella enana: Daño de conjuros", + "DCC.BirthAugur.unholy": "Profano: Pruebas de expulsar profanos", + "DCC.BirthAugur.scepter": "Cetro: Conjuros de sanación", + "DCC.BirthAugur.luckySign": "Signo de la suerte: Todas las tiradas de salvación", + "DCC.BirthAugur.guardianAngel": "Ángel guardián: Salvación contra trampas", + "DCC.BirthAugur.survivedThePlague": "Sobrevivió a la plaga: Salvación contra veneno", + "DCC.BirthAugur.struckByLightning": "Alcanzado por un rayo: Tiradas de salvación de Reflejos", + "DCC.BirthAugur.livedThroughFamine": "Sobrevivió a la hambruna: Tiradas de salvación de Fortaleza", + "DCC.BirthAugur.resistedTemptation": "Resistió la tentación: Tiradas de salvación de Voluntad", + "DCC.BirthAugur.charmedHouse": "Casa encantada: Clase de armadura", + "DCC.BirthAugur.speedOfTheCobra": "Velocidad de la cobra: Iniciativa", + "DCC.BirthAugur.bountifulHarvest": "Cosecha abundante: Puntos de golpe (por nivel)", + "DCC.BirthAugur.warriorsBattle": "Batalla del guerrero: Tablas de golpe crítico", + "DCC.BirthAugur.markOfTheDemon": "Marca del demonio: Tiradas de corrupción", + "DCC.BirthAugur.doomedToFail": "Condenado al fracaso: Pifias", + "DCC.BirthAugur.twinned": "Canto de pájaro: Idiomas", + "DCC.BirthAugur.wildChild": "Niño salvaje: Velocidad (×1,5 m)", "DCC.BlindnessDeafness": "Ceguera/Sordera", "DCC.Bonus": "Bono", "DCC.BrokenLimbs": "Miembros rotos", diff --git a/lang/fr.json b/lang/fr.json index 1b544680..2398dbde 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -79,6 +79,63 @@ "DCC.BadValueFormulaWarning": "Mauvaise formule dans le champs de valeur de l'objet!", "DCC.BaseACAbilityConfig": "CA de base", "DCC.BirthAugur": "Augure de naissance", + "DCC.BirthAugurMode": "Mode d'augure de naissance", + "DCC.BirthAugurModeHint": "Statique utilise le modificateur de chance à la création du personnage (RAW). Flottant utilise le modificateur de chance actuel (règle maison).", + "DCC.BirthAugurModeStatic": "Statique (RAW)", + "DCC.BirthAugurModeFloating": "Flottant (Règle maison)", + "DCC.BirthAugurMod": "Modificateur d'augure de naissance", + "DCC.BirthAugurNone": "— Aucun —", + "DCC.BirthAugurNotAutomated": "(pas encore automatisé)", + "DCC.BirthAugurItem": "Augure de Naissance", + "DCC.BirthAugurDetails": "Détails de l'Augure", + "DCC.BirthAugurEffect": "Type d'Effet", + "DCC.BirthAugurEffectNone": "Aucun (non automatisé)", + "DCC.BirthAugurEffectAllAttack": "Tous les jets d'attaque", + "DCC.BirthAugurEffectMeleeAttack": "Jets d'attaque au corps à corps", + "DCC.BirthAugurEffectMissileAttack": "Jets d'attaque à distance", + "DCC.BirthAugurEffectAllDamage": "Tous les jets de dégâts", + "DCC.BirthAugurEffectMeleeDamage": "Jets de dégâts au corps à corps", + "DCC.BirthAugurEffectMissileDamage": "Jets de dégâts à distance", + "DCC.BirthAugurEffectSpellCheck": "Tests de sort", + "DCC.BirthAugurEffectAllSaves": "Tous les jets de sauvegarde", + "DCC.BirthAugurEffectReflexSave": "Jets de sauvegarde de Réflexes", + "DCC.BirthAugurEffectFortSave": "Jets de sauvegarde de Vigueur", + "DCC.BirthAugurEffectWillSave": "Jets de sauvegarde de Volonté", + "DCC.BirthAugurEffectArmorClass": "Classe d'Armure", + "DCC.BirthAugurEffectInitiative": "Initiative", + "DCC.BirthAugurEffectSpeed": "Vitesse", + "DCC.BirthAugurDragHint": "Glisser depuis le compendium", + "DCC.BirthAugurOpenCompendium": "Ouvrir le compendium des Augures", + "DCC.BirthAugur.harshWinter": "Hiver rude : Tous les jets d'attaque", + "DCC.BirthAugur.theBull": "Le taureau : Jets d'attaque au corps à corps", + "DCC.BirthAugur.fortunateDate": "Date chanceuse : Jets d'attaque à distance", + "DCC.BirthAugur.raisedByWolves": "Élevé par les loups : Jets d'attaque à mains nues", + "DCC.BirthAugur.conceivedOnHorseback": "Conçu à cheval : Jets d'attaque/dégâts en monture", + "DCC.BirthAugur.bornOnTheBattlefield": "Né sur le champ de bataille : Jets de dégâts", + "DCC.BirthAugur.pathOfTheBear": "Voie de l'ours : Jets de dégâts au corps à corps", + "DCC.BirthAugur.hawkeye": "Œil de faucon : Jets de dégâts à distance", + "DCC.BirthAugur.packHunter": "Chasseur en meute : Attaque/dégâts pour armes de niveau 0", + "DCC.BirthAugur.bornUnderTheLoom": "Né sous le métier à tisser : Jets de compétence", + "DCC.BirthAugur.foxsCunning": "Ruse du renard : Trouver/désamorcer les pièges", + "DCC.BirthAugur.fourLeafedClover": "Trèfle à quatre feuilles : Trouver les portes secrètes", + "DCC.BirthAugur.seventhSon": "Septième fils : Jets de sort", + "DCC.BirthAugur.theDwarvenStar": "L'étoile naine : Dégâts de sorts", + "DCC.BirthAugur.unholy": "Impie : Jets de renvoi des impies", + "DCC.BirthAugur.scepter": "Sceptre : Sorts de guérison", + "DCC.BirthAugur.luckySign": "Signe chanceux : Tous les jets de sauvegarde", + "DCC.BirthAugur.guardianAngel": "Ange gardien : Sauvegardes contre les pièges", + "DCC.BirthAugur.survivedThePlague": "A survécu à la peste : Sauvegardes contre le poison", + "DCC.BirthAugur.struckByLightning": "Frappé par la foudre : Jets de sauvegarde de Réflexes", + "DCC.BirthAugur.livedThroughFamine": "A survécu à la famine : Jets de sauvegarde de Vigueur", + "DCC.BirthAugur.resistedTemptation": "A résisté à la tentation : Jets de sauvegarde de Volonté", + "DCC.BirthAugur.charmedHouse": "Maison bénie : Classe d'armure", + "DCC.BirthAugur.speedOfTheCobra": "Vitesse du cobra : Initiative", + "DCC.BirthAugur.bountifulHarvest": "Récolte abondante : Points de vie (par niveau)", + "DCC.BirthAugur.warriorsBattle": "Bataille du guerrier : Tables de coups critiques", + "DCC.BirthAugur.markOfTheDemon": "Marque du démon : Jets de corruption", + "DCC.BirthAugur.doomedToFail": "Voué à l'échec : Maladresses", + "DCC.BirthAugur.twinned": "Chant d'oiseau : Langues", + "DCC.BirthAugur.wildChild": "Enfant sauvage : Vitesse (×1,5 m)", "DCC.BlindnessDeafness": "Cécité/Sourdité", "DCC.Bonus": "Bonus", "DCC.BrokenLimbs": "Membres brisés", diff --git a/lang/it.json b/lang/it.json index 2bcda1f0..52567806 100644 --- a/lang/it.json +++ b/lang/it.json @@ -92,6 +92,63 @@ "DCC.BadValueFormulaWarning": "Formula non valida nel campo valore dell'oggetto!", "DCC.BaseACAbilityConfig": "Caratteristica per la CA", "DCC.BirthAugur": "Presagio di Nascita", + "DCC.BirthAugurMode": "Modalità presagio di nascita", + "DCC.BirthAugurModeHint": "Statico usa il modificatore di fortuna alla creazione del personaggio (RAW). Variabile usa il modificatore di fortuna attuale (regola della casa).", + "DCC.BirthAugurModeStatic": "Statico (RAW)", + "DCC.BirthAugurModeFloating": "Variabile (Regola della casa)", + "DCC.BirthAugurMod": "Modificatore presagio di nascita", + "DCC.BirthAugurNone": "— Nessuno —", + "DCC.BirthAugurNotAutomated": "(non ancora automatizzato)", + "DCC.BirthAugurItem": "Auspicio di Nascita", + "DCC.BirthAugurDetails": "Dettagli dell'Auspicio", + "DCC.BirthAugurEffect": "Tipo di Effetto", + "DCC.BirthAugurEffectNone": "Nessuno (non automatizzato)", + "DCC.BirthAugurEffectAllAttack": "Tutti i tiri per colpire", + "DCC.BirthAugurEffectMeleeAttack": "Tiri per colpire in mischia", + "DCC.BirthAugurEffectMissileAttack": "Tiri per colpire a distanza", + "DCC.BirthAugurEffectAllDamage": "Tutti i tiri per i danni", + "DCC.BirthAugurEffectMeleeDamage": "Tiri per i danni in mischia", + "DCC.BirthAugurEffectMissileDamage": "Tiri per i danni a distanza", + "DCC.BirthAugurEffectSpellCheck": "Prove di incantesimo", + "DCC.BirthAugurEffectAllSaves": "Tutti i tiri salvezza", + "DCC.BirthAugurEffectReflexSave": "Tiri salvezza sui Riflessi", + "DCC.BirthAugurEffectFortSave": "Tiri salvezza sulla Tempra", + "DCC.BirthAugurEffectWillSave": "Tiri salvezza sulla Volontà", + "DCC.BirthAugurEffectArmorClass": "Classe Armatura", + "DCC.BirthAugurEffectInitiative": "Iniziativa", + "DCC.BirthAugurEffectSpeed": "Velocità", + "DCC.BirthAugurDragHint": "Trascina dal compendio", + "DCC.BirthAugurOpenCompendium": "Apri il compendio degli Auspici", + "DCC.BirthAugur.harshWinter": "Inverno rigido: Tutti i tiri per colpire", + "DCC.BirthAugur.theBull": "Il toro: Tiri per colpire in mischia", + "DCC.BirthAugur.fortunateDate": "Data fortunata: Tiri per colpire a distanza", + "DCC.BirthAugur.raisedByWolves": "Allevato dai lupi: Tiri per colpire a mani nude", + "DCC.BirthAugur.conceivedOnHorseback": "Concepito a cavallo: Tiri per colpire/danni in sella", + "DCC.BirthAugur.bornOnTheBattlefield": "Nato sul campo di battaglia: Tiri per i danni", + "DCC.BirthAugur.pathOfTheBear": "Sentiero dell'orso: Tiri per i danni in mischia", + "DCC.BirthAugur.hawkeye": "Occhio di falco: Tiri per i danni a distanza", + "DCC.BirthAugur.packHunter": "Cacciatore in branco: Attacco/danni per armi di livello 0", + "DCC.BirthAugur.bornUnderTheLoom": "Nato sotto il telaio: Prove di abilità", + "DCC.BirthAugur.foxsCunning": "Astuzia della volpe: Trovare/disattivare trappole", + "DCC.BirthAugur.fourLeafedClover": "Quadrifoglio: Trovare porte segrete", + "DCC.BirthAugur.seventhSon": "Settimo figlio: Prove di incantesimo", + "DCC.BirthAugur.theDwarvenStar": "La stella nanica: Danni degli incantesimi", + "DCC.BirthAugur.unholy": "Empio: Prove di scacciare gli empi", + "DCC.BirthAugur.scepter": "Scettro: Incantesimi di guarigione", + "DCC.BirthAugur.luckySign": "Segno fortunato: Tutti i tiri salvezza", + "DCC.BirthAugur.guardianAngel": "Angelo custode: Tiri salvezza contro trappole", + "DCC.BirthAugur.survivedThePlague": "Sopravvissuto alla peste: Tiri salvezza contro veleno", + "DCC.BirthAugur.struckByLightning": "Colpito da un fulmine: Tiri salvezza sui Riflessi", + "DCC.BirthAugur.livedThroughFamine": "Sopravvissuto alla carestia: Tiri salvezza sulla Tempra", + "DCC.BirthAugur.resistedTemptation": "Ha resistito alla tentazione: Tiri salvezza sulla Volontà", + "DCC.BirthAugur.charmedHouse": "Casa incantata: Classe armatura", + "DCC.BirthAugur.speedOfTheCobra": "Velocità del cobra: Iniziativa", + "DCC.BirthAugur.bountifulHarvest": "Raccolto abbondante: Punti ferita (per livello)", + "DCC.BirthAugur.warriorsBattle": "Battaglia del guerriero: Tabelle dei colpi critici", + "DCC.BirthAugur.markOfTheDemon": "Marchio del demone: Tiri di corruzione", + "DCC.BirthAugur.doomedToFail": "Destinato a fallire: Insuccessi critici", + "DCC.BirthAugur.twinned": "Canto degli uccelli: Lingue", + "DCC.BirthAugur.wildChild": "Bambino selvaggio: Velocità (×1,5 m)", "DCC.BlindnessDeafness": "Cecità/Sordità", "DCC.Bonus": "Bonus", "DCC.BrokenLimbs": "Membra rotte", diff --git a/lang/pl.json b/lang/pl.json index 443389d2..92e86711 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -92,6 +92,63 @@ "DCC.BadValueFormulaWarning": "Błędna formuła w polu wartości przedmiotu!", "DCC.BaseACAbilityConfig": "Bazowa Cecha KP", "DCC.BirthAugur": "Wróżba Narodzin", + "DCC.BirthAugurMode": "Tryb wróżby narodzin", + "DCC.BirthAugurModeHint": "Statyczny używa modyfikatora szczęścia z tworzenia postaci (RAW). Zmienny używa aktualnego modyfikatora szczęścia (zasada domowa).", + "DCC.BirthAugurModeStatic": "Statyczny (RAW)", + "DCC.BirthAugurModeFloating": "Zmienny (Zasada domowa)", + "DCC.BirthAugurMod": "Modyfikator wróżby narodzin", + "DCC.BirthAugurNone": "— Brak —", + "DCC.BirthAugurNotAutomated": "(jeszcze nie zautomatyzowane)", + "DCC.BirthAugurItem": "Wróżba Urodzinowa", + "DCC.BirthAugurDetails": "Szczegóły Wróżby", + "DCC.BirthAugurEffect": "Typ Efektu", + "DCC.BirthAugurEffectNone": "Brak (niezautomatyzowany)", + "DCC.BirthAugurEffectAllAttack": "Wszystkie rzuty na atak", + "DCC.BirthAugurEffectMeleeAttack": "Rzuty na atak wręcz", + "DCC.BirthAugurEffectMissileAttack": "Rzuty na atak dystansowy", + "DCC.BirthAugurEffectAllDamage": "Wszystkie rzuty na obrażenia", + "DCC.BirthAugurEffectMeleeDamage": "Rzuty na obrażenia wręcz", + "DCC.BirthAugurEffectMissileDamage": "Rzuty na obrażenia dystansowe", + "DCC.BirthAugurEffectSpellCheck": "Testy zaklęć", + "DCC.BirthAugurEffectAllSaves": "Wszystkie rzuty obronne", + "DCC.BirthAugurEffectReflexSave": "Rzuty obronne na Refleks", + "DCC.BirthAugurEffectFortSave": "Rzuty obronne na Wytrwałość", + "DCC.BirthAugurEffectWillSave": "Rzuty obronne na Wolę", + "DCC.BirthAugurEffectArmorClass": "Klasa Pancerza", + "DCC.BirthAugurEffectInitiative": "Inicjatywa", + "DCC.BirthAugurEffectSpeed": "Szybkość", + "DCC.BirthAugurDragHint": "Przeciągnij z kompendium", + "DCC.BirthAugurOpenCompendium": "Otwórz kompendium Wróżb", + "DCC.BirthAugur.harshWinter": "Sroga zima: Wszystkie rzuty na atak", + "DCC.BirthAugur.theBull": "Byk: Rzuty na atak wręcz", + "DCC.BirthAugur.fortunateDate": "Szczęśliwa data: Rzuty na atak dystansowy", + "DCC.BirthAugur.raisedByWolves": "Wychowany przez wilki: Rzuty na atak bez broni", + "DCC.BirthAugur.conceivedOnHorseback": "Poczęty na koniu: Rzuty na atak/obrażenia konno", + "DCC.BirthAugur.bornOnTheBattlefield": "Urodzony na polu bitwy: Rzuty na obrażenia", + "DCC.BirthAugur.pathOfTheBear": "Ścieżka niedźwiedzia: Rzuty na obrażenia wręcz", + "DCC.BirthAugur.hawkeye": "Sokolie oko: Rzuty na obrażenia dystansowe", + "DCC.BirthAugur.packHunter": "Łowca w stadzie: Atak/obrażenia bronią z poziomu 0", + "DCC.BirthAugur.bornUnderTheLoom": "Urodzony pod krosnem: Testy umiejętności", + "DCC.BirthAugur.foxsCunning": "Spryt lisa: Znajdowanie/rozbrajanie pułapek", + "DCC.BirthAugur.fourLeafedClover": "Czterolistna koniczyna: Znajdowanie tajnych drzwi", + "DCC.BirthAugur.seventhSon": "Siódmy syn: Testy zaklęć", + "DCC.BirthAugur.theDwarvenStar": "Krasnoludzka gwiazda: Obrażenia od zaklęć", + "DCC.BirthAugur.unholy": "Bezbożny: Testy odpędzania bezbożnych", + "DCC.BirthAugur.scepter": "Berło: Zaklęcia leczące", + "DCC.BirthAugur.luckySign": "Znak szczęścia: Wszystkie rzuty obronne", + "DCC.BirthAugur.guardianAngel": "Anioł stróż: Rzuty obronne przeciw pułapkom", + "DCC.BirthAugur.survivedThePlague": "Przeżył zarazę: Rzuty obronne przeciw truciźnie", + "DCC.BirthAugur.struckByLightning": "Trafiony piorunem: Rzuty obronne na Refleks", + "DCC.BirthAugur.livedThroughFamine": "Przeżył głód: Rzuty obronne na Wytrwałość", + "DCC.BirthAugur.resistedTemptation": "Oparł się pokusie: Rzuty obronne na Wolę", + "DCC.BirthAugur.charmedHouse": "Zaczarowany dom: Klasa pancerza", + "DCC.BirthAugur.speedOfTheCobra": "Szybkość kobry: Inicjatywa", + "DCC.BirthAugur.bountifulHarvest": "Obfite żniwa: Punkty wytrzymałości (na poziom)", + "DCC.BirthAugur.warriorsBattle": "Bitwa wojownika: Tabele trafień krytycznych", + "DCC.BirthAugur.markOfTheDemon": "Znak demona: Rzuty na korupcję", + "DCC.BirthAugur.doomedToFail": "Skazany na porażkę: Fuszerki", + "DCC.BirthAugur.twinned": "Śpiew ptaków: Języki", + "DCC.BirthAugur.wildChild": "Dzikie dziecko: Szybkość (×1,5 m)", "DCC.BlindnessDeafness": "Paraliż/Słuch", "DCC.Bonus": "Bonus", "DCC.BrokenLimbs": "Złamane Kończyny", diff --git a/module/__mocks__/foundry.js b/module/__mocks__/foundry.js index e20f113e..ba87bbf6 100644 --- a/module/__mocks__/foundry.js +++ b/module/__mocks__/foundry.js @@ -1099,6 +1099,8 @@ class ActorMock { adjustment: '+0' } }, + birthAugurIndex: null, + birthAugurLuckMod: 0, lastRolledAttackBonus: '', level: { value: 1 @@ -1153,7 +1155,8 @@ class ActorMock { baseACAbility: 'agl', initiativeDieOverride: '', sortInventory: true, - removeEmptyItems: true + removeEmptyItems: true, + birthAugurMode: 'static' } } }) @@ -1800,6 +1803,7 @@ const DOCUMENT_DEFAULTS = { attackHitBonus: { melee: { value: '+0', adjustment: '+0' }, missile: { value: '+0', adjustment: '+0' } }, attackDamageBonus: { melee: { value: '+0', adjustment: '+0' }, missile: { value: '+0', adjustment: '+0' } }, birthAugur: '', + birthAugurIndex: null, birthAugurLuckMod: 0, critRange: 20, languages: '', @@ -1835,7 +1839,8 @@ const DOCUMENT_DEFAULTS = { showSpells: false, showSkills: false, showBackstab: false, - showSwimFlySpeed: false + showSwimFlySpeed: false, + birthAugurMode: 'static' } }, Player: { diff --git a/module/__tests__/birth-augur.test.js b/module/__tests__/birth-augur.test.js new file mode 100644 index 00000000..e6b27e18 --- /dev/null +++ b/module/__tests__/birth-augur.test.js @@ -0,0 +1,377 @@ +/** + * Tests for birth augur automation + */ + +import { describe, test, expect, vi } from 'vitest' +import '../__mocks__/foundry.js' +import DCCActor from '../actor' +import { BIRTH_AUGURS, matchAugurFromText } from '../birth-augurs.mjs' + +// Mock the actor-level-change module +vi.mock('../actor-level-change.js') + +/** + * Create a test actor with isPC set to true + * The mock doesn't set type/isPC automatically like the real actor does + */ +function createPCActor () { + const actor = new DCCActor() + actor.isPC = true + return actor +} + +/** + * Create a mock birthAugur item + * @param {string} effect - The effect type (e.g. 'allAttack') + * @returns {object} A mock item object matching Collection.find() expectations + */ +function createMockAugurItem (effect) { + return { type: 'birthAugur', system: { effect } } +} + +describe('BIRTH_AUGURS table', () => { + test('has 30 entries', () => { + expect(BIRTH_AUGURS).toHaveLength(30) + }) + + test('indices are 1-30', () => { + BIRTH_AUGURS.forEach((augur, i) => { + expect(augur.index).toBe(i + 1) + }) + }) + + test('each augur has key and effect', () => { + BIRTH_AUGURS.forEach(augur => { + expect(augur.key).toBeTruthy() + expect(augur.effect).toBeTruthy() + }) + }) +}) + +describe('matchAugurFromText', () => { + test('returns null for empty/invalid input', () => { + expect(matchAugurFromText(null)).toBeNull() + expect(matchAugurFromText('')).toBeNull() + expect(matchAugurFromText(undefined)).toBeNull() + expect(matchAugurFromText(42)).toBeNull() + }) + + test('matches augur names', () => { + expect(matchAugurFromText('Harsh winter: All attack rolls (+2)')).toBe(1) + expect(matchAugurFromText('The bull: Melee attack rolls (-1)')).toBe(2) + expect(matchAugurFromText('Fortunate date: Missile fire attack rolls (+3)')).toBe(3) + expect(matchAugurFromText('Hawkeye: Missile fire damage rolls (+1)')).toBe(8) + expect(matchAugurFromText('Seventh son: Spell checks (-2)')).toBe(13) + expect(matchAugurFromText('Lucky sign: Saving throws (+1)')).toBe(17) + expect(matchAugurFromText('Struck by lightning: Reflex saving throws (+2)')).toBe(20) + expect(matchAugurFromText('Charmed house: Armor Class (+1)')).toBe(23) + expect(matchAugurFromText('Speed of the cobra: Initiative (+2)')).toBe(24) + expect(matchAugurFromText('Wild child: Speed (+1)')).toBe(30) + }) + + test('matches case-insensitively', () => { + expect(matchAugurFromText('HARSH WINTER: All attack rolls')).toBe(1) + expect(matchAugurFromText('the bull: melee attack rolls')).toBe(2) + }) + + test('returns null for unrecognized text', () => { + expect(matchAugurFromText('Some random text')).toBeNull() + expect(matchAugurFromText('Not a real augur')).toBeNull() + }) +}) + +describe('_getBirthAugurBonusFor', () => { + test('returns 0 for NPC', () => { + const actor = createPCActor() + actor.isPC = false + actor.system.details.birthAugurIndex = 1 + actor.system.details.birthAugurLuckMod = 2 + expect(actor._getBirthAugurBonusFor('allAttack')).toBe(0) + }) + + test('returns 0 when birthAugurIndex is null', () => { + const actor = createPCActor() + expect(actor._getBirthAugurBonusFor('allAttack')).toBe(0) + }) + + test('returns 0 when effect type does not match', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 1 // harshWinter -> allAttack + actor.system.details.birthAugurLuckMod = 2 + expect(actor._getBirthAugurBonusFor('meleeAttack')).toBe(0) + }) + + test('returns birthAugurLuckMod in static mode', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 1 // harshWinter -> allAttack + actor.system.details.birthAugurLuckMod = 2 + actor.system.config.birthAugurMode = 'static' + expect(actor._getBirthAugurBonusFor('allAttack')).toBe(2) + }) + + test('returns lck.mod in floating mode', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 1 // harshWinter -> allAttack + actor.system.details.birthAugurLuckMod = 2 + actor.system.config.birthAugurMode = 'floating' + // actor has lck value 18 -> mod 3 + expect(actor._getBirthAugurBonusFor('allAttack')).toBe(3) + }) + + test('matches multiple effect types', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 1 // harshWinter -> allAttack + actor.system.details.birthAugurLuckMod = 2 + actor.system.config.birthAugurMode = 'static' + expect(actor._getBirthAugurBonusFor('meleeAttack', 'allAttack')).toBe(2) + }) +}) + +describe('birth augur integration with compute methods', () => { + test('allAttack augur adds to both melee and missile attack', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 1 // harshWinter -> allAttack + actor.system.details.birthAugurLuckMod = 2 + actor.system.config.birthAugurMode = 'static' + actor.computeMeleeAndMissileAttackAndDamage() + // Base: str mod (-1) + attack bonus (0) + augur (+2) = +1 + expect(actor.system.details.attackHitBonus.melee.value).toBe('+1') + // Missile: agl mod (-1) + attack bonus (0) + augur (+2) = +1 + expect(actor.system.details.attackHitBonus.missile.value).toBe('+1') + }) + + test('meleeAttack augur adds only to melee attack', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 2 // theBull -> meleeAttack + actor.system.details.birthAugurLuckMod = 3 + actor.system.config.birthAugurMode = 'static' + actor.computeMeleeAndMissileAttackAndDamage() + // Melee: str mod (-1) + attack bonus (0) + augur (+3) = +2 + expect(actor.system.details.attackHitBonus.melee.value).toBe('+2') + // Missile: agl mod (-1) + attack bonus (0) + no augur = -1 + expect(actor.system.details.attackHitBonus.missile.value).toBe('-1') + }) + + test('missileAttack augur adds only to missile attack', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 3 // fortunateDate -> missileAttack + actor.system.details.birthAugurLuckMod = 1 + actor.system.config.birthAugurMode = 'static' + actor.computeMeleeAndMissileAttackAndDamage() + // Melee: str mod (-1) + attack bonus (0) + no augur = -1 + expect(actor.system.details.attackHitBonus.melee.value).toBe('-1') + // Missile: agl mod (-1) + attack bonus (0) + augur (+1) = +0 + expect(actor.system.details.attackHitBonus.missile.value).toBe('+0') + }) + + test('allDamage augur adds to both melee and missile damage', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 6 // bornOnTheBattlefield -> allDamage + actor.system.details.birthAugurLuckMod = 2 + actor.system.config.birthAugurMode = 'static' + actor.computeMeleeAndMissileAttackAndDamage() + // Melee damage: str mod (-1) + augur (+2) = +1 + expect(actor.system.details.attackDamageBonus.melee.value).toBe('+1') + // Missile damage: augur (+2) = +2 + expect(actor.system.details.attackDamageBonus.missile.value).toBe('+2') + }) + + test('meleeDamage augur adds only to melee damage', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 7 // pathOfTheBear -> meleeDamage + actor.system.details.birthAugurLuckMod = 1 + actor.system.config.birthAugurMode = 'static' + actor.computeMeleeAndMissileAttackAndDamage() + // Melee damage: str mod (-1) + augur (+1) = +0 + expect(actor.system.details.attackDamageBonus.melee.value).toBe('+0') + // Missile damage: no augur = +0 + expect(actor.system.details.attackDamageBonus.missile.value).toBe('+0') + }) + + test('missileDamage augur adds only to missile damage', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 8 // hawkeye -> missileDamage + actor.system.details.birthAugurLuckMod = 3 + actor.system.config.birthAugurMode = 'static' + actor.computeMeleeAndMissileAttackAndDamage() + // Melee damage: str mod (-1) + no augur = -1 + expect(actor.system.details.attackDamageBonus.melee.value).toBe('-1') + // Missile damage: augur (+3) = +3 + expect(actor.system.details.attackDamageBonus.missile.value).toBe('+3') + }) + + test('reflexSave augur adds to reflex save', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 20 // struckByLightning -> reflexSave + actor.system.details.birthAugurLuckMod = 2 + actor.system.config.birthAugurMode = 'static' + actor.computeSavingThrows() + // Ref: agl mod (-1) + augur (+2) = +1 + expect(actor.system.saves.ref.value).toBe('+1') + // Fort: sta mod (0) = +0 + expect(actor.system.saves.frt.value).toBe('+0') + // Will: per mod (2) = +2 + expect(actor.system.saves.wil.value).toBe('+2') + }) + + test('fortSave augur adds to fort save', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 21 // livedThroughFamine -> fortSave + actor.system.details.birthAugurLuckMod = 1 + actor.system.config.birthAugurMode = 'static' + actor.computeSavingThrows() + expect(actor.system.saves.frt.value).toBe('+1') + expect(actor.system.saves.ref.value).toBe('-1') + }) + + test('willSave augur adds to will save', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 22 // resistedTemptation -> willSave + actor.system.details.birthAugurLuckMod = 1 + actor.system.config.birthAugurMode = 'static' + actor.computeSavingThrows() + expect(actor.system.saves.wil.value).toBe('+3') + expect(actor.system.saves.ref.value).toBe('-1') + }) + + test('allSaves augur adds to all saves', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 17 // luckySign -> allSaves + actor.system.details.birthAugurLuckMod = 1 + actor.system.config.birthAugurMode = 'static' + actor.computeSavingThrows() + // Ref: agl mod (-1) + augur (+1) = +0 + expect(actor.system.saves.ref.value).toBe('+0') + // Fort: sta mod (0) + augur (+1) = +1 + expect(actor.system.saves.frt.value).toBe('+1') + // Will: per mod (2) + augur (+1) = +3 + expect(actor.system.saves.wil.value).toBe('+3') + }) + + test('spellCheck augur adds to spell check', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 13 // seventhSon -> spellCheck + actor.system.details.birthAugurLuckMod = 2 + actor.system.config.birthAugurMode = 'static' + actor.computeSpellCheck() + // Level (1) + int mod (+1) + augur (+2) = "+1+1+2" (formula string) + expect(actor.system.class.spellCheck).toBe('+1+1+2') + }) + + test('initiative augur adds to initiative', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 24 // speedOfTheCobra -> initiative + actor.system.details.birthAugurLuckMod = 2 + actor.system.config.birthAugurMode = 'static' + actor.computeInitiative({ addClassLevelToInitiative: false }) + // Agl mod (-1) + otherMod (0) + augur (+2) = 1 + expect(actor.system.attributes.init.value).toBe(1) + }) + + test('no augur selected does not affect computations', () => { + const actor = createPCActor() + // birthAugurIndex is null by default + actor.computeMeleeAndMissileAttackAndDamage() + expect(actor.system.details.attackHitBonus.melee.value).toBe('-1') + expect(actor.system.details.attackHitBonus.missile.value).toBe('-1') + actor.computeSavingThrows() + expect(actor.system.saves.ref.value).toBe('-1') + expect(actor.system.saves.frt.value).toBe('+0') + expect(actor.system.saves.wil.value).toBe('+2') + }) + + test('non-automated augur does not affect computations', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 4 // raisedByWolves -> 'none' + actor.system.details.birthAugurLuckMod = 3 + actor.system.config.birthAugurMode = 'static' + actor.computeMeleeAndMissileAttackAndDamage() + expect(actor.system.details.attackHitBonus.melee.value).toBe('-1') + actor.computeSavingThrows() + expect(actor.system.saves.ref.value).toBe('-1') + actor.computeInitiative({ addClassLevelToInitiative: false }) + expect(actor.system.attributes.init.value).toBe(-1) + }) + + test('armorClass augur returns correct bonus', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 23 // charmedHouse -> armorClass + actor.system.details.birthAugurLuckMod = 2 + actor.system.config.birthAugurMode = 'static' + expect(actor._getBirthAugurBonusFor('armorClass')).toBe(2) + // Verify it doesn't match unrelated effect types + expect(actor._getBirthAugurBonusFor('allAttack')).toBe(0) + }) + + test('speed augur returns correct bonus (caller multiplies by 5)', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 30 // wildChild -> speed + actor.system.details.birthAugurLuckMod = 1 + actor.system.config.birthAugurMode = 'static' + // _getBirthAugurBonusFor returns raw bonus; prepareDerivedData multiplies by 5 + expect(actor._getBirthAugurBonusFor('speed')).toBe(1) + expect(actor._getBirthAugurBonusFor('speed') * 5).toBe(5) + }) + + test('negative birthAugurLuckMod applies correctly', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 1 // harshWinter -> allAttack + actor.system.details.birthAugurLuckMod = -2 + actor.system.config.birthAugurMode = 'static' + actor.computeMeleeAndMissileAttackAndDamage() + // Melee: str mod (-1) + augur (-2) = -3 + expect(actor.system.details.attackHitBonus.melee.value).toBe('-3') + }) +}) + +describe('birthAugur item-based lookup', () => { + test('birthAugur item takes priority over birthAugurIndex', () => { + const actor = createPCActor() + // Legacy index points to allAttack + actor.system.details.birthAugurIndex = 1 + actor.system.details.birthAugurLuckMod = 2 + actor.system.config.birthAugurMode = 'static' + // Item overrides to meleeAttack + actor._birthAugurItem = createMockAugurItem('meleeAttack') + // Should match meleeAttack (from item), not allAttack (from index) + expect(actor._getBirthAugurBonusFor('meleeAttack')).toBe(2) + expect(actor._getBirthAugurBonusFor('allAttack')).toBe(0) + }) + + test('falls back to birthAugurIndex when no item exists', () => { + const actor = createPCActor() + actor.system.details.birthAugurIndex = 1 // harshWinter -> allAttack + actor.system.details.birthAugurLuckMod = 2 + actor.system.config.birthAugurMode = 'static' + actor._birthAugurItem = null + expect(actor._getBirthAugurBonusFor('allAttack')).toBe(2) + }) + + test('birthAugur item with none effect returns 0', () => { + const actor = createPCActor() + actor.system.details.birthAugurLuckMod = 2 + actor.system.config.birthAugurMode = 'static' + actor._birthAugurItem = createMockAugurItem('none') + expect(actor._getBirthAugurBonusFor('allAttack')).toBe(0) + }) + + test('birthAugur item bonus works with floating mode', () => { + const actor = createPCActor() + actor.system.details.birthAugurLuckMod = 2 + actor.system.config.birthAugurMode = 'floating' + actor._birthAugurItem = createMockAugurItem('allAttack') + // Floating mode uses lck.mod (value 18 -> mod 3) + expect(actor._getBirthAugurBonusFor('allAttack')).toBe(3) + }) + + test('birthAugur item integrates with compute methods', () => { + const actor = createPCActor() + actor.system.details.birthAugurLuckMod = 2 + actor.system.config.birthAugurMode = 'static' + actor._birthAugurItem = createMockAugurItem('allAttack') + actor.computeMeleeAndMissileAttackAndDamage() + // str mod (-1) + augur (+2) = +1 + expect(actor.system.details.attackHitBonus.melee.value).toBe('+1') + expect(actor.system.details.attackHitBonus.missile.value).toBe('+1') + }) +}) diff --git a/module/__tests__/pc-parser.test.js b/module/__tests__/pc-parser.test.js index 0d6f0930..3faaf33e 100644 --- a/module/__tests__/pc-parser.test.js +++ b/module/__tests__/pc-parser.test.js @@ -83,6 +83,14 @@ Languages: Common`) }, isCoins: true } + }, + { + name: 'BirthAugur.foxsCunning', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'none' + } } ] } @@ -193,6 +201,14 @@ test('beekeeper', () => { }, isCoins: true } + }, + { + name: 'BirthAugur.unholy', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'none' + } } ] } @@ -266,6 +282,14 @@ Languages: Common`) }, isCoins: true } + }, + { + name: 'BirthAugur.foxsCunning', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'none' + } } ] } @@ -373,6 +397,14 @@ Languages: Common `) }, isCoins: true } + }, + { + name: 'BirthAugur.foxsCunning', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'none' + } } ] }, @@ -434,6 +466,14 @@ Languages: Common `) }, isCoins: true } + }, + { + name: 'BirthAugur.survivedThePlague', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'none' + } } ] } @@ -577,6 +617,14 @@ Spells: (Spell Check: d20+2) isCoins: true } }, + { + name: 'BirthAugur.luckySign', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'allSaves' + } + }, { name: 'Darkness', type: 'spell', @@ -787,6 +835,14 @@ Cast Spell From Scroll (d16)` }, isCoins: true } + }, + { + name: 'BirthAugur.foxsCunning', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'none' + } } ] } @@ -930,6 +986,14 @@ Hide In Shadows: 11 (-4)` }, isCoins: true } + }, + { + name: 'BirthAugur.harshWinter', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'allAttack' + } } ] } @@ -1066,6 +1130,14 @@ Warrior trait: Lucky weapon - choose one weapon that you apply your luck mod to` }, isCoins: true } + }, + { + name: 'BirthAugur.resistedTemptation', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'willSave' + } } ] } @@ -1222,6 +1294,14 @@ Spells: (Spell Check: d20+12) isCoins: true } }, + { + name: 'BirthAugur.warriorsBattle', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'none' + } + }, { name: 'Chill Touch', type: 'spell', @@ -1573,6 +1653,14 @@ Dwarf skill: Shield bash - make an extra d14 attack with your shield. (1d3 damag }, isCoins: true } + }, + { + name: 'BirthAugur.raisedByWolves', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'none' + } } ] } @@ -1720,6 +1808,14 @@ Spells: (Spell Check: d20+5) isCoins: true } }, + { + name: 'BirthAugur.bornOnTheBattlefield', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'allDamage' + } + }, { name: 'Patron Bond', type: 'spell', @@ -1918,6 +2014,14 @@ Warrior trait: Lucky weapon - choose one weapon that you apply your luck mod to` }, isCoins: true } + }, + { + name: 'BirthAugur.unholy', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'none' + } }] } expect(parsedNPC).toMatchObject([expected]) @@ -2033,6 +2137,14 @@ Warrior trait: Lucky weapon - choose one weapon that you apply your luck mod to` }, isCoins: true } + }, + { + name: 'BirthAugur.unholy', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'none' + } }] } expect(parsedNPC).toMatchObject([expected]) @@ -2148,6 +2260,14 @@ Warrior trait: Lucky weapon - choose one weapon that you apply your luck mod to` }, isCoins: true } + }, + { + name: 'BirthAugur.unholy', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'none' + } }] } @@ -2324,6 +2444,14 @@ Dwarf skill: Shield bash - make an extra d14 attack with your shield. (1d3 damag isCoins: true } }, + { + name: 'BirthAugur.bornOnTheBattlefield', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'allDamage' + } + }, { name: 'Patron Bond', type: 'spell', @@ -2506,6 +2634,14 @@ Dwarf skill: Shield bash - make an extra d14 attack with your shield. (1d3 damag }, isCoins: true } + }, + { + name: 'BirthAugur.raisedByWolves', + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: 'none' + } } ] } diff --git a/module/actor-sheet.js b/module/actor-sheet.js index a9d779d1..7827176b 100644 --- a/module/actor-sheet.js +++ b/module/actor-sheet.js @@ -4,6 +4,7 @@ import DCCActorConfig from './actor-config.js' import MeleeMissileBonusConfig from './melee-missile-bonus-config.js' import SavingThrowConfig from './saving-throw-config.js' import EntityImages from './entity-images.js' +import { BIRTH_AUGURS } from './birth-augurs.mjs' const { HandlebarsApplicationMixin } = foundry.applications.api // eslint-disable-next-line no-unused-vars @@ -164,6 +165,10 @@ class DCCActorSheet extends HandlebarsApplicationMixin(ActorSheetV2) { saveEffects: this.#prepareSaveEffects(), attributeEffects: this.#prepareAttributeEffects(), actor: this.options.document, + birthAugurs: this.#prepareBirthAugurs(), + birthAugurItem: this.options.document._birthAugurItem ?? null, + birthAugurMod: this.options.document._computedBirthAugurMod ?? null, + birthAugurEffect: this.options.document._computedBirthAugurEffect ?? null, compendiumLinks: this.#prepareCompendiumLinks(), config: CONFIG.DCC, corruptionHTML: await this.#prepareCorruption(), @@ -809,6 +814,18 @@ class DCCActorSheet extends HandlebarsApplicationMixin(ActorSheetV2) { return CONFIG.DCC.coreBookCompendiumLinks } + /** + * Prepare birth augur select options for the template + * @returns {Object} Object keyed by augur index with i18n labels, suitable for selectOptions + */ + #prepareBirthAugurs () { + const options = {} + for (const augur of BIRTH_AUGURS) { + options[augur.index] = `DCC.BirthAugur.${augur.key}` + } + return options + } + /** * Search the object and then its parent elements for a dataset attribute @param {Object} element The starting element diff --git a/module/actor.js b/module/actor.js index 0e170dc6..aa959569 100644 --- a/module/actor.js +++ b/module/actor.js @@ -3,6 +3,7 @@ import { ensurePlus, getCritTableResult, getCritTableLink, getFumbleTableResult, getNPCFumbleTableResult, getFumbleTableNameFromCritTableName, addDamageFlavorToRolls } from './utilities.js' import DCCActorLevelChange from './actor-level-change.js' +import { BIRTH_AUGURS } from './birth-augurs.mjs' const { TextEditor } = foundry.applications.ux @@ -115,6 +116,11 @@ class DCCActor extends Actor { // Get configuration data const config = this._getConfig() + // Cache birth augur item before compute methods that use _getBirthAugurBonusFor + if (this.isPC) { + this._birthAugurItem = this.items.find(i => i.type === 'birthAugur') ?? null + } + // Compute melee/missile attack and damage bonuses (after effects have modified ability modifiers) if (config.computeMeleeAndMissileAttackAndDamage) { this.computeMeleeAndMissileAttackAndDamage() @@ -183,14 +189,16 @@ class DCCActor extends Actor { } } if (config.computeAC) { + const augurACBonus = this._getBirthAugurBonusFor('armorClass') this.system.attributes.ac.baseAbility = abilityMod this.system.attributes.ac.baseAbilityLabel = abilityLabel this.system.attributes.ac.armorBonus = armorBonus - this.system.attributes.ac.value = 10 + abilityMod + armorBonus + acOtherMod + this.system.attributes.ac.value = 10 + abilityMod + armorBonus + acOtherMod + augurACBonus } if (config.computeSpeed) { + const augurSpeedBonus = this._getBirthAugurBonusFor('speed') * 5 this.system.attributes.ac.speedPenalty = speedPenalty - this.system.attributes.speed.value = baseSpeed + speedPenalty + this.system.attributes.speed.value = baseSpeed + speedPenalty + augurSpeedBonus } } @@ -199,6 +207,35 @@ class DCCActor extends Actor { this.computeInitiative(config) } + // Compute birth augur display info for UI + if (this.isPC) { + let effect = null + + if (this._birthAugurItem) { + // Primary: birthAugur item + effect = this._birthAugurItem.system.effect + } else { + // Fallback: legacy birthAugurIndex + const augurIndex = this.system.details.birthAugurIndex + if (augurIndex != null) { + const augur = BIRTH_AUGURS[augurIndex - 1] + if (augur) { + effect = augur.effect + } + } + } + + if (effect && effect !== 'none') { + this._computedBirthAugurEffect = effect + this._computedBirthAugurMod = this.system.config.birthAugurMode === 'floating' + ? this.system.abilities.lck.mod + : this.system.details.birthAugurLuckMod + } else { + this._computedBirthAugurEffect = null + this._computedBirthAugurMod = null + } + } + // Re-prepare embedded items so they can see active effect modifications // Items initially prepare before applyActiveEffects runs, so they need // to re-read actor values that may have been modified by effects @@ -409,6 +446,27 @@ class DCCActor extends Actor { overrides[key] = newValue } + /** @inheritDoc */ + _onCreateDescendantDocuments (parent, collection, documents, data, options, userId) { + super._onCreateDescendantDocuments(parent, collection, documents, data, options, userId) + + // Enforce single birth augur: when a new birthAugur item is added, delete existing ones + if (collection === 'items' && game.user.id === userId) { + const newAugurs = documents.filter(d => d.type === 'birthAugur') + if (newAugurs.length > 0) { + const newestId = newAugurs[newAugurs.length - 1].id + const toDelete = this.items + .filter(i => i.type === 'birthAugur' && i.id !== newestId) + .map(i => i.id) + if (toDelete.length > 0) { + this.deleteEmbeddedDocuments('Item', toDelete).catch(err => { + console.error(`DCC | Failed to remove duplicate birthAugur items from actor "${this.name}":`, err) + }) + } + } + } + } + /** * Get per actor configuration * @@ -585,16 +643,49 @@ class DCCActor extends Actor { return actionDice } + /** + * Get the birth augur bonus for one or more effect types. + * Checks birthAugur items first, then falls back to legacy birthAugurIndex. + * @param {...string} effectTypes - Effect types to check (e.g. 'allAttack', 'meleeAttack') + * @returns {number} The birth augur bonus, or 0 if no matching augur + */ + _getBirthAugurBonusFor (...effectTypes) { + if (!this.isPC) return 0 + + // Determine active effect: item first, then legacy index fallback + let effect = null + if (this._birthAugurItem) { + effect = this._birthAugurItem.system.effect + } else { + const augurIndex = this.system.details.birthAugurIndex + if (augurIndex != null) { + const augur = BIRTH_AUGURS[augurIndex - 1] + if (augur) effect = augur.effect + } + } + + if (!effect || !effectTypes.includes(effect)) return 0 + return this.system.config.birthAugurMode === 'floating' + ? this.system.abilities.lck.mod + : this.system.details.birthAugurLuckMod + } + /** Compute Melee/Missile Base Attack and Damage Modifiers */ computeMeleeAndMissileAttackAndDamage () { const attackBonus = this.system.details.attackBonus || '0' const strengthBonus = parseInt(this.system.abilities.str.mod) || 0 const agilityBonus = parseInt(this.system.abilities.agl.mod) || 0 - const meleeAttackBonusAdjustment = parseInt(this.system.details.attackHitBonus?.melee?.adjustment) || 0 - const meleeDamageBonusAdjustment = parseInt(this.system.details.attackDamageBonus?.melee?.adjustment) || 0 - const missileAttackBonusAdjustment = parseInt(this.system.details.attackHitBonus?.missile?.adjustment) || 0 - const missileDamageBonusAdjustment = parseInt(this.system.details.attackDamageBonus?.missile?.adjustment) || 0 + const augurAttackBonus = this._getBirthAugurBonusFor('allAttack') + const augurMeleeAttackBonus = this._getBirthAugurBonusFor('meleeAttack') + const augurMissileAttackBonus = this._getBirthAugurBonusFor('missileAttack') + const augurDamageBonus = this._getBirthAugurBonusFor('allDamage') + const augurMeleeDamageBonus = this._getBirthAugurBonusFor('meleeDamage') + const augurMissileDamageBonus = this._getBirthAugurBonusFor('missileDamage') + const meleeAttackBonusAdjustment = (parseInt(this.system.details.attackHitBonus?.melee?.adjustment) || 0) + augurAttackBonus + augurMeleeAttackBonus + const meleeDamageBonusAdjustment = (parseInt(this.system.details.attackDamageBonus?.melee?.adjustment) || 0) + augurDamageBonus + augurMeleeDamageBonus + const missileAttackBonusAdjustment = (parseInt(this.system.details.attackHitBonus?.missile?.adjustment) || 0) + augurAttackBonus + augurMissileAttackBonus + const missileDamageBonusAdjustment = (parseInt(this.system.details.attackDamageBonus?.missile?.adjustment) || 0) + augurDamageBonus + augurMissileDamageBonus let meleeAttackBonus let missileAttackBonus let meleeAttackDamage @@ -627,14 +718,15 @@ class DCCActor extends Actor { const perMod = parseInt(this.system.abilities.per.mod) const aglMod = parseInt(this.system.abilities.agl.mod) const staMod = parseInt(this.system.abilities.sta.mod) + const augurAllSaves = this._getBirthAugurBonusFor('allSaves') const refSaveClassBonus = parseInt(this.system.saves.ref.classBonus || 0) - const refSaveOtherBonus = parseInt(this.system.saves.ref.otherBonus || 0) + const refSaveOtherBonus = parseInt(this.system.saves.ref.otherBonus || 0) + augurAllSaves + this._getBirthAugurBonusFor('reflexSave') const refSaveOverride = this.system.saves.ref.override const frtSaveClassBonus = parseInt(this.system.saves.frt.classBonus || 0) - const frtSaveOtherBonus = parseInt(this.system.saves.frt.otherBonus || 0) + const frtSaveOtherBonus = parseInt(this.system.saves.frt.otherBonus || 0) + augurAllSaves + this._getBirthAugurBonusFor('fortSave') const frtSaveOverride = this.system.saves.frt.override const wilSaveClassBonus = parseInt(this.system.saves.wil.classBonus || 0) - const wilSaveOtherBonus = parseInt(this.system.saves.wil.otherBonus || 0) + const wilSaveOtherBonus = parseInt(this.system.saves.wil.otherBonus || 0) + augurAllSaves + this._getBirthAugurBonusFor('willSave') const wilSaveOverride = this.system.saves.wil.override this.system.saves.ref.value = ensurePlus(`${aglMod + refSaveClassBonus + refSaveOtherBonus}`) @@ -672,7 +764,12 @@ class DCCActor extends Actor { if (this.system.class.spellCheckOtherMod) { otherMod = ensurePlus(this.system.class.spellCheckOtherMod) } - this.system.class.spellCheck = ensurePlus(this.system.details.level.value + abilityMod + otherMod) + let augurMod = '' + const augurSpellBonus = this._getBirthAugurBonusFor('spellCheck') + if (augurSpellBonus) { + augurMod = ensurePlus(augurSpellBonus) + } + this.system.class.spellCheck = ensurePlus(this.system.details.level.value + abilityMod + otherMod + augurMod) if (this.system.class.spellCheckOverride) { this.system.class.spellCheck = this.system.class.spellCheckOverride } @@ -691,7 +788,7 @@ class DCCActor extends Actor { * @param {Object} config - Actor configuration */ computeInitiative (config) { - this.system.attributes.init.value = parseInt(this.system.abilities.agl.mod) + parseInt(this.system.attributes.init.otherMod || 0) + this.system.attributes.init.value = parseInt(this.system.abilities.agl.mod) + parseInt(this.system.attributes.init.otherMod || 0) + this._getBirthAugurBonusFor('initiative') if (config.addClassLevelToInitiative) { this.system.attributes.init.value += this.system.details.level.value } diff --git a/module/birth-augurs.mjs b/module/birth-augurs.mjs new file mode 100644 index 00000000..0198dcec --- /dev/null +++ b/module/birth-augurs.mjs @@ -0,0 +1,106 @@ +/** + * Canonical table of all 30 DCC birth augurs + * + * Each augur has: + * - index: 1-30 (matches the d30 roll from the rulebook) + * - key: camelCase identifier used for i18n lookup (DCC.BirthAugur.{key}) + * - effect: the effect type used by _getBirthAugurBonusFor() for automation + * + * Effect types automated in Phase 1: + * allAttack, meleeAttack, missileAttack, allDamage, meleeDamage, missileDamage, + * spellCheck, allSaves, reflexSave, fortSave, willSave, armorClass, initiative, speed + * + * Effect type 'none' means the augur is defined but not yet automated. + */ +export const BIRTH_AUGURS = [ + { index: 1, key: 'harshWinter', effect: 'allAttack' }, + { index: 2, key: 'theBull', effect: 'meleeAttack' }, + { index: 3, key: 'fortunateDate', effect: 'missileAttack' }, + { index: 4, key: 'raisedByWolves', effect: 'none' }, + { index: 5, key: 'conceivedOnHorseback', effect: 'none' }, + { index: 6, key: 'bornOnTheBattlefield', effect: 'allDamage' }, + { index: 7, key: 'pathOfTheBear', effect: 'meleeDamage' }, + { index: 8, key: 'hawkeye', effect: 'missileDamage' }, + { index: 9, key: 'packHunter', effect: 'none' }, + { index: 10, key: 'bornUnderTheLoom', effect: 'none' }, + { index: 11, key: 'foxsCunning', effect: 'none' }, + { index: 12, key: 'fourLeafedClover', effect: 'none' }, + { index: 13, key: 'seventhSon', effect: 'spellCheck' }, + { index: 14, key: 'theDwarvenStar', effect: 'none' }, + { index: 15, key: 'unholy', effect: 'none' }, + { index: 16, key: 'scepter', effect: 'none' }, + { index: 17, key: 'luckySign', effect: 'allSaves' }, + { index: 18, key: 'guardianAngel', effect: 'none' }, + { index: 19, key: 'survivedThePlague', effect: 'none' }, + { index: 20, key: 'struckByLightning', effect: 'reflexSave' }, + { index: 21, key: 'livedThroughFamine', effect: 'fortSave' }, + { index: 22, key: 'resistedTemptation', effect: 'willSave' }, + { index: 23, key: 'charmedHouse', effect: 'armorClass' }, + { index: 24, key: 'speedOfTheCobra', effect: 'initiative' }, + { index: 25, key: 'bountifulHarvest', effect: 'none' }, + { index: 26, key: 'warriorsBattle', effect: 'none' }, + { index: 27, key: 'markOfTheDemon', effect: 'none' }, + { index: 28, key: 'doomedToFail', effect: 'none' }, + { index: 29, key: 'twinned', effect: 'none' }, + { index: 30, key: 'wildChild', effect: 'speed' } +] + +/** + * Match birth augur text against the augur table to find the index. + * Used by migration and PC parser to auto-detect augur from free-text. + * + * @param {string} text - The birth augur text to match + * @returns {number|null} - The augur index (1-30) or null if no match + */ +export function matchAugurFromText (text) { + if (!text || typeof text !== 'string') return null + + const normalizedText = text.toLowerCase().trim() + if (!normalizedText) return null + + // Match patterns from the DCC rulebook birth augur descriptions + // Order matters: more specific patterns (e.g. "melee damage") must come before + // general patterns (e.g. "damage rolls") to avoid false matches. + const patterns = [ + { index: 1, patterns: ['harsh winter', 'all attack rolls'] }, + { index: 2, patterns: ['the bull', 'melee attack rolls'] }, + { index: 3, patterns: ['fortunate date', 'missile fire attack rolls'] }, + { index: 4, patterns: ['raised by wolves', 'unarmed attack rolls'] }, + { index: 5, patterns: ['conceived on horseback', 'mounted'] }, + { index: 7, patterns: ['path of the bear', 'melee damage rolls'] }, + { index: 8, patterns: ['hawkeye', 'missile fire damage rolls'] }, + { index: 6, patterns: ['born on the battlefield', 'damage rolls'] }, + { index: 9, patterns: ['pack hunter', 'attack and damage rolls for 0-level'] }, + { index: 10, patterns: ['born under the loom', 'skill checks'] }, + { index: 11, patterns: ["fox's cunning", 'fox\u2019s cunning', 'find.+trap', 'disable.+trap'] }, + { index: 12, patterns: ['four-leafed clover', 'four leafed clover', 'find secret doors'] }, + { index: 13, patterns: ['seventh son', 'spell checks'] }, + { index: 14, patterns: ['the dwarven star', 'spell damage'] }, + { index: 15, patterns: ['unholy', 'turn unholy checks'] }, + { index: 16, patterns: ['scepter', 'healing spells'] }, + { index: 18, patterns: ['guardian angel', 'saves versus traps'] }, + { index: 19, patterns: ['survived the plague', 'saves versus poison'] }, + { index: 20, patterns: ['struck by lightning', 'reflex saving throws'] }, + { index: 21, patterns: ['lived through famine', 'fortitude saving throws'] }, + { index: 22, patterns: ['resisted temptation', 'willpower saving throws'] }, + { index: 17, patterns: ['lucky sign', 'saving throws'] }, + { index: 23, patterns: ['charmed house', 'armor class'] }, + { index: 24, patterns: ['speed of the cobra', 'initiative'] }, + { index: 25, patterns: ['bountiful harvest', 'hit points'] }, + { index: 26, patterns: ["warrior's battle", 'warrior\u2019s battle', 'critical hit tables'] }, + { index: 27, patterns: ['mark of the demon', 'corruption rolls'] }, + { index: 28, patterns: ['doomed to fail', 'fumbles'] }, + { index: 29, patterns: ['twinned', 'birdsong'] }, + { index: 30, patterns: ['wild child', 'speed\\b'] } + ] + + for (const entry of patterns) { + for (const pattern of entry.patterns) { + if (normalizedText.match(new RegExp(pattern, 'i'))) { + return entry.index + } + } + } + + return null +} diff --git a/module/config.js b/module/config.js index 06d50262..f9095b54 100644 --- a/module/config.js +++ b/module/config.js @@ -178,7 +178,8 @@ DCC.items = { mount: 'DCC.Mount', spell: 'DCC.Spell', skill: 'DCC.Skill', - treasure: 'DCC.Treasure' + treasure: 'DCC.Treasure', + birthAugur: 'DCC.BirthAugurItem' } /** @@ -207,6 +208,35 @@ DCC.attackBonusModes = { autoPerAttack: 'DCC.AttackBonusConfigModeAutoPerAttack' } +/** + * Birth Augur modes + */ +DCC.birthAugurModes = { + static: 'DCC.BirthAugurModeStatic', + floating: 'DCC.BirthAugurModeFloating' +} + +/** + * Birth Augur effect types for the item sheet dropdown + */ +DCC.birthAugurEffects = { + none: 'DCC.BirthAugurEffectNone', + allAttack: 'DCC.BirthAugurEffectAllAttack', + meleeAttack: 'DCC.BirthAugurEffectMeleeAttack', + missileAttack: 'DCC.BirthAugurEffectMissileAttack', + allDamage: 'DCC.BirthAugurEffectAllDamage', + meleeDamage: 'DCC.BirthAugurEffectMeleeDamage', + missileDamage: 'DCC.BirthAugurEffectMissileDamage', + spellCheck: 'DCC.BirthAugurEffectSpellCheck', + allSaves: 'DCC.BirthAugurEffectAllSaves', + reflexSave: 'DCC.BirthAugurEffectReflexSave', + fortSave: 'DCC.BirthAugurEffectFortSave', + willSave: 'DCC.BirthAugurEffectWillSave', + armorClass: 'DCC.BirthAugurEffectArmorClass', + initiative: 'DCC.BirthAugurEffectInitiative', + speed: 'DCC.BirthAugurEffectSpeed' +} + /** * The valid currency denominations supported by the DCC system * @type {Object} @@ -634,7 +664,8 @@ DCC.defaultItemImages = { spell: 'systems/dcc/styles/images/spell.webp', treasure: 'systems/dcc/styles/images/coins.webp', weapon: 'systems/dcc/styles/images/weapon.webp', - skill: 'systems/dcc/styles/images/skill.webp' + skill: 'systems/dcc/styles/images/skill.webp', + birthAugur: 'systems/dcc/styles/images/item.webp' } // Default macro images diff --git a/module/data/actor/base-actor.mjs b/module/data/actor/base-actor.mjs index e9360675..24dbae55 100644 --- a/module/data/actor/base-actor.mjs +++ b/module/data/actor/base-actor.mjs @@ -4,6 +4,7 @@ * Contains the common template fields shared by all actor types */ import { AbilityField, CurrencyField, DiceField, SaveField, isValidDiceNotation, migrateFieldsToInteger } from '../fields/_module.mjs' +import { matchAugurFromText } from '../../birth-augurs.mjs' const { SchemaField, StringField, NumberField, ArrayField, HTMLField } = foundry.data.fields @@ -93,6 +94,14 @@ export class BaseActorData extends foundry.abstract.TypeDataModel { } } + // Auto-populate birthAugurIndex from birthAugur text if not already set + if (source.details?.birthAugur && source.details.birthAugurIndex == null) { + const matchedIndex = matchAugurFromText(source.details.birthAugur) + if (matchedIndex !== null) { + source.details.birthAugurIndex = matchedIndex + } + } + // Convert currency values to integers if needed migrateFieldsToInteger(source.currency, ['pp', 'ep', 'gp', 'sp', 'cp'], 0) @@ -225,6 +234,7 @@ export class BaseActorData extends foundry.abstract.TypeDataModel { }) }), birthAugur: new StringField({ initial: '' }), + birthAugurIndex: new NumberField({ initial: null, nullable: true, integer: true, min: 1, max: 30 }), birthAugurLuckMod: new NumberField({ initial: 0, integer: true }), critRange: new NumberField({ initial: 20, integer: true, min: 1 }), sheetClass: new StringField({ initial: '' }), diff --git a/module/data/actor/player-data.mjs b/module/data/actor/player-data.mjs index d2e19ac4..879e6eab 100644 --- a/module/data/actor/player-data.mjs +++ b/module/data/actor/player-data.mjs @@ -213,6 +213,7 @@ export class PlayerData extends BaseActorData { computeInitiative: new BooleanField({ initial: true }), computeMeleeAndMissileAttackAndDamage: new BooleanField({ initial: true }), computeSavingThrows: new BooleanField({ initial: true }), + birthAugurMode: new StringField({ initial: 'static' }), sortInventory: new BooleanField({ initial: true }), removeEmptyItems: new BooleanField({ initial: true }), showSpells: new BooleanField({ initial: false }), diff --git a/module/data/item/_module.mjs b/module/data/item/_module.mjs index 8ae2ec7d..5a5d139d 100644 --- a/module/data/item/_module.mjs +++ b/module/data/item/_module.mjs @@ -3,6 +3,7 @@ */ export { BaseItemData, PhysicalItemData } from './base-item.mjs' export { AmmunitionData } from './ammunition-data.mjs' +export { BirthAugurData } from './birth-augur-data.mjs' export { ArmorData } from './armor-data.mjs' export { EquipmentData } from './equipment-data.mjs' export { LevelData } from './level-data.mjs' diff --git a/module/data/item/birth-augur-data.mjs b/module/data/item/birth-augur-data.mjs new file mode 100644 index 00000000..0347f8b2 --- /dev/null +++ b/module/data/item/birth-augur-data.mjs @@ -0,0 +1,16 @@ +/* global foundry */ +/** + * Data model for birth augur items + */ +import { BaseItemData } from './base-item.mjs' + +const { StringField } = foundry.data.fields + +export class BirthAugurData extends BaseItemData { + static defineSchema () { + return { + ...super.defineSchema(), + effect: new StringField({ initial: 'none' }) + } + } +} diff --git a/module/dcc.js b/module/dcc.js index f284408e..586de519 100644 --- a/module/dcc.js +++ b/module/dcc.js @@ -44,6 +44,7 @@ import { WeaponData, AmmunitionData, ArmorData, + BirthAugurData, EquipmentData, LevelData, MountData, @@ -87,7 +88,8 @@ Hooks.once('init', async function () { mount: MountData, spell: SpellData, treasure: TreasureData, - skill: SkillData + skill: SkillData, + birthAugur: BirthAugurData } // noinspection JSUndefinedPropertyAssignment,JSUnusedGlobalSymbols @@ -396,14 +398,15 @@ function setupCoreBookCompendiumLinks () { 'dcc-core-book.dcc-core-spells-cleric-3', 'dcc-core-book.dcc-core-spells-cleric-4', 'dcc-core-book.dcc-core-spells-cleric-5' - ] + ], + birthAugurs: 'dcc-core-book.dcc-core-birth-augurs' } } function checkMigrations () { // Determine whether a system migration is required and feasible const currentVersion = game.settings.get('dcc', 'systemMigrationVersion') - const NEEDS_MIGRATION_VERSION = 0.22 + const NEEDS_MIGRATION_VERSION = 0.67 const needMigration = (currentVersion <= NEEDS_MIGRATION_VERSION) || (currentVersion === null) // Perform the migration diff --git a/module/item-piles-support.js b/module/item-piles-support.js index f8b0268d..342f6172 100644 --- a/module/item-piles-support.js +++ b/module/item-piles-support.js @@ -16,7 +16,7 @@ export async function setupItemPilesForDCC () { ITEM_FILTERS: [ { path: 'type', - filters: 'spell,skill' + filters: 'spell,skill,birthAugur' } ], diff --git a/module/migrations.js b/module/migrations.js index eff8366f..38b4d642 100644 --- a/module/migrations.js +++ b/module/migrations.js @@ -1,5 +1,7 @@ /* global foundry, game, ui, isObjectEmpty */ +import { BIRTH_AUGURS } from './birth-augurs.mjs' + /** * Core class keys used for migration lookups */ @@ -62,6 +64,8 @@ export const migrateWorld = async function () { console.log(game.i18n.format('DCC.MigrationMessage', { type: 'Actor', name: a.name })) await a.update(updateData, { enforceTypes: false }) } + // Create birthAugur items for Player actors with a legacy birthAugurIndex + await migrateBirthAugurToItem(a) } catch (err) { console.error(err) } @@ -350,3 +354,38 @@ const migrateSceneData = async function (scene) { } return { tokens } } + +/** + * Create a birthAugur item for a Player actor that has a legacy birthAugurIndex + * but no birthAugur item yet + * @param {Actor} actor - The actor to migrate + * @returns {Promise} + */ +async function migrateBirthAugurToItem (actor) { + if (actor.type !== 'Player') return + + const augurIndex = actor.system?.details?.birthAugurIndex + if (augurIndex == null) return + + // Skip if actor already has a birthAugur item + const hasAugurItem = actor.items.some(i => i.type === 'birthAugur') + if (hasAugurItem) return + + const augur = BIRTH_AUGURS[augurIndex - 1] + if (!augur) { + console.warn(`DCC | Migration: Actor "${actor.name}" has birthAugurIndex ${augurIndex} which is out of range (1-30). Skipping birthAugur item creation.`) + return + } + + const name = game.i18n.localize(`DCC.BirthAugur.${augur.key}`) + console.log(`DCC | Migration: Creating birthAugur item "${name}" for actor "${actor.name}"`) + + await actor.createEmbeddedDocuments('Item', [{ + name, + type: 'birthAugur', + img: 'systems/dcc/styles/images/item.webp', + system: { + effect: augur.effect + } + }]) +} diff --git a/module/pc-parser.js b/module/pc-parser.js index 1dff2800..3c232ca8 100644 --- a/module/pc-parser.js +++ b/module/pc-parser.js @@ -3,6 +3,7 @@ import EntityImages from './entity-images.js' import { getFirstDie, getFirstMod } from './utilities.js' import DCC from './config.js' +import { BIRTH_AUGURS, matchAugurFromText } from './birth-augurs.mjs' /** * Parses Player Stat Blocks (e.g. from Purple Sorcerer) into an Actor sheet @@ -225,6 +226,21 @@ function _parseJSONPCs (pcObject) { if (pcObject.luckySign) { notes = notes + game.i18n.localize('DCC.BirthAugur') + ': ' + pcObject.luckySign + '
' pc['details.birthAugur'] = pcObject.luckySign + const augurIndex = matchAugurFromText(pcObject.luckySign) + if (augurIndex !== null) { + pc['details.birthAugurIndex'] = augurIndex + const augur = BIRTH_AUGURS[augurIndex - 1] + if (augur) { + pc.items.push({ + name: game.i18n.localize(`DCC.BirthAugur.${augur.key}`), + type: 'birthAugur', + img: EntityImages.imageForItem('birthAugur'), + system: { + effect: augur.effect + } + }) + } + } } if (pcObject.languages) { notes = notes + game.i18n.localize('DCC.Languages') + ': ' + pcObject.languages + '
' diff --git a/styles/dcc.css b/styles/dcc.css index d80221ad..e643fe74 100644 --- a/styles/dcc.css +++ b/styles/dcc.css @@ -2142,6 +2142,41 @@ body.theme-dark .dcc.sheet .sheet-tabs.responsive-tabs .tabs-overflow .tabs-over .dcc .character .lucky-roll textarea:focus { box-shadow: none; } +.dcc .character .birth-augur-item-display { + align-items: center; + display: flex; + gap: 4px; + padding: 0 3px; +} +.dcc .character .birth-augur-item-display .birth-augur-icon { + border: 0; + flex-shrink: 0; +} +.dcc .character .birth-augur-item-display .birth-augur-name { + flex-grow: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.dcc .character .birth-augur-item-display .birth-augur-mod { + flex-shrink: 0; + font-weight: bold; +} +.dcc .character .birth-augur-item-display .birth-augur-delete { + flex-shrink: 0; + opacity: 0.5; +} +.dcc .character .birth-augur-item-display .birth-augur-delete:hover { + opacity: 1; +} +.dcc .character .lucky-roll .compendium-link { + font-size: 11px; + opacity: 0.7; + padding: 0 3px; +} +.dcc .character .lucky-roll .compendium-link:hover { + opacity: 1; +} .dcc .character .melee-missile-attack-damage { align-items: center; display: grid; diff --git a/styles/dcc.css.map b/styles/dcc.css.map index 714bba24..f2b045d2 100644 --- a/styles/dcc.css.map +++ b/styles/dcc.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["_grid.scss","dcc.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;ACnOF;AACA;AACA;AACA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;AACA;EACE;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACE;;AAIF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;;AAGF;EAEE;EACA;EACA;;AAGF;EAEE;EACA;;AAGF;EAEE;EACA;;AAGF;EAIE;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEE;;AAGF;EAIE;;AAGF;EAEE;;AAGF;EAEE;EACA;EACA;EACA;;AAGF;EAIE;EACA;EACA;;AAGF;EAEE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAGA;EACE;;AAIF;EACE;EACA;EACA;;;AAKN;AACA;AAAA;AAEA;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAIA;AAAA;EAEE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAMJ;EACE;EACA;;AAGF;EACE;EACA;;AAIF;AAAA;EAEE;EACA;;;AAQJ;AAAA;EACE;EACA;EACA;EACA;EACA;;AAEA;AAAA;EACE;EACA;EACA;EACA;EACA;;;AAKN;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;;AAKN;AACA;AACA;AACA;EACE;EACA;EACA;;;AAGF;AAAA;AAAA;AAAA;EAIE;EACA;;;AAGF;AAAA;AAAA;EAGE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIF;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAEA;EACE;;AAEA;EACE;EACA;;;AAMR;EACE;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;AACA;AACA;AAEE;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;EAME;;AAMR;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAKN;AACA;AACA;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;AAAA;EAEE;EACA;;;AAKN;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAIA;EACA;;AAEA;EACE;;AAGF;EACE;;;AAOV;AACA;AACA;AAGE;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;;AAKN;EACE;EACA;EACA;EACA;EACA;;;AAGF;AACA;AAAA;AAGA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGA;EACE;EACA;;AASF;EACE;EACA;EACA;EACA;;AAEA;AAAA;EAEE;EACA;;;AAOV;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGA;EACE;EACA;EACA;EACA;;AAEA;AAAA;EAEE;EACA;;;AAOV;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;;AAKN;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;AAAA;EAEE;EACA;;AAIJ;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;;;AAKN;AACA;AACA;AAGE;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;AAIJ;EACE;;AAGF;EACE;EACA;EACA;EACA;;;AAIJ;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAIJ;EACE;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;;AAIJ;EACE;;AAEA;EACE;;AAIJ;EACE;;AAEA;EACE;;AAIJ;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAKJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAKN;AACA;AACA;AAGE;AAAA;EAEE;;AAGF;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAKN;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;;;AAKN;AACA;AAAA;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;AACA;AACA;AAGE;EACE;EACA;EACA;EACA;;AALJ;AAQE;;AACA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;;AAKN;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AA7FZ;AAoGE;;AACA;EACE;EACA;;AAEA;EACE;;AAEA;EACE;EACA;;AAGF;EACE;;AAKN;EACE;;AAEA;EACE;EACA;;AAIJ;EACE;;AAGF;EACE;;AAIF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;AAAA;AAAA;AAAA;EAIE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;;AAIJ;AACA;AACA;AAEE;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIJ;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;EAEA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;;;AAIJ;AACA;AACA;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAKN;AACA;AACA;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAIA;EACE;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;AAAA;AAAA;EAGE;EACA;;AAEA;AAAA;AAAA;EACE;;AAIJ;EACE;EACA;;AAKN;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAKN;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EACE;;;AAMR;AACA;AAAA;AAIE;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;;AAGF;AAAA;EAEE;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAOV;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;;AAIJ;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;AAAA;EACE;EACA;;AAIJ;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;AAAA;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAMR;EACE;EACA;EACA;;;AAMJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAKF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAad;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;;AAMF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;;AAIJ;AAEE;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAGE;EACE;;AAKN;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;;AAKN;AAGI;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAIJ;EACE;;;AAKN;AAEE;EACE;EACA;EACA","file":"dcc.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["_grid.scss","dcc.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;ACnOF;AACA;AACA;AACA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;AACA;EACE;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACE;;AAIF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;;AAGF;EAEE;EACA;EACA;;AAGF;EAEE;EACA;;AAGF;EAEE;EACA;;AAGF;EAIE;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEE;;AAGF;EAIE;;AAGF;EAEE;;AAGF;EAEE;EACA;EACA;EACA;;AAGF;EAIE;EACA;EACA;;AAGF;EAEE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAGA;EACE;;AAIF;EACE;EACA;EACA;;;AAKN;AACA;AAAA;AAEA;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAIA;AAAA;EAEE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAMJ;EACE;EACA;;AAGF;EACE;EACA;;AAIF;AAAA;EAEE;EACA;;;AAQJ;AAAA;EACE;EACA;EACA;EACA;EACA;;AAEA;AAAA;EACE;EACA;EACA;EACA;EACA;;;AAKN;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;;AAKN;AACA;AACA;AACA;EACE;EACA;EACA;;;AAGF;AAAA;AAAA;AAAA;EAIE;EACA;;;AAGF;AAAA;AAAA;EAGE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIF;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAEA;EACE;;AAEA;EACE;EACA;;;AAMR;EACE;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;AACA;AACA;AAEE;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;EAME;;AAMR;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAKN;AACA;AACA;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;AAAA;EAEE;EACA;;;AAKN;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAIA;EACA;;AAEA;EACE;;AAGF;EACE;;;AAOV;AACA;AACA;AAGE;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;;AAKN;EACE;EACA;EACA;EACA;EACA;;;AAGF;AACA;AAAA;AAGA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGA;EACE;EACA;;AASF;EACE;EACA;EACA;EACA;;AAEA;AAAA;EAEE;EACA;;;AAOV;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGA;EACE;EACA;EACA;EACA;;AAEA;AAAA;EAEE;EACA;;;AAOV;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;;AAKN;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;AAAA;EAEE;EACA;;AAIJ;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;;;AAKN;AACA;AACA;AAGE;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;AAIJ;EACE;;AAGF;EACE;EACA;EACA;EACA;;;AAIJ;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAIJ;EACE;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;;AAIJ;EACE;;AAEA;EACE;;AAIJ;EACE;;AAEA;EACE;;AAIJ;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAKJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAKN;AACA;AACA;AAGE;AAAA;EAEE;;AAGF;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAKN;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;;;AAKN;AACA;AAAA;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;AACA;AACA;AAGE;EACE;EACA;EACA;EACA;;AALJ;AAQE;;AACA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;;AAKN;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AA7FZ;AAoGE;;AACA;EACE;EACA;;AAEA;EACE;;AAEA;EACE;EACA;;AAGF;EACE;;AAKN;EACE;;AAEA;EACE;EACA;;AAIJ;EACE;;AAGF;EACE;;AAIF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;AAAA;AAAA;AAAA;EAIE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;;AAIJ;AACA;AACA;AAEE;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIJ;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;EAEA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;;;AAIJ;AACA;AACA;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAKN;AACA;AACA;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAIA;EACE;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;AAAA;AAAA;EAGE;EACA;;AAEA;AAAA;AAAA;EACE;;AAIJ;EACE;EACA;;AAKN;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAKN;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EACE;;;AAMR;AACA;AAAA;AAIE;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;;AAGF;AAAA;EAEE;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAOV;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;;AAIJ;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;AAAA;EACE;EACA;;AAIJ;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;AAAA;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;;AAKN;EACE;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAMR;EACE;EACA;EACA;;;AAMJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAKF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAad;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;;AAMF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;;AAIJ;AAEE;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAGE;EACE;;AAKN;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;;AAKN;AAGI;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAIJ;EACE;;;AAKN;AAEE;EACE;EACA;EACA","file":"dcc.css"} \ No newline at end of file diff --git a/styles/dcc.scss b/styles/dcc.scss index af7e09d4..9b01faa6 100644 --- a/styles/dcc.scss +++ b/styles/dcc.scss @@ -2278,6 +2278,49 @@ a.inline-roll { } } + .birth-augur-item-display { + align-items: center; + display: flex; + gap: 4px; + padding: 0 3px; + + .birth-augur-icon { + border: 0; + flex-shrink: 0; + } + + .birth-augur-name { + flex-grow: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .birth-augur-mod { + flex-shrink: 0; + font-weight: bold; + } + + .birth-augur-delete { + flex-shrink: 0; + opacity: 0.5; + + &:hover { + opacity: 1; + } + } + } + + .lucky-roll .compendium-link { + font-size: 11px; + opacity: 0.7; + padding: 0 3px; + + &:hover { + opacity: 1; + } + } + .melee-missile-attack-damage { align-items: center; display: grid; diff --git a/system.json b/system.json index 565538b6..f74fe095 100644 --- a/system.json +++ b/system.json @@ -16,7 +16,8 @@ "mount": {}, "spell": {}, "treasure": {}, - "skill": {} + "skill": {}, + "birthAugur": {} } }, "description": "Dungeon Crawl Classics from Goodman Games", diff --git a/templates/actor-partial-pc-common.html b/templates/actor-partial-pc-common.html index b77a3072..9b0bb8b0 100644 --- a/templates/actor-partial-pc-common.html +++ b/templates/actor-partial-pc-common.html @@ -338,10 +338,40 @@ - {{!-- Lucky Roll --}} + {{!-- Lucky Roll / Birth Augur --}}
- - + + {{#if birthAugurItem}} +
+ {{birthAugurItem.name}} + {{birthAugurItem.name}} + {{#if birthAugurEffect}} + + ({{numberFormat birthAugurMod decimals=0 sign=true}}) + + {{/if}} + + + +
+ {{else}} + + {{#if birthAugurEffect}} +
+ {{localize "DCC.BirthAugurMod"}}: {{numberFormat birthAugurMod decimals=0 sign=true}} +
+ {{/if}} + {{/if}} + {{#if compendiumLinks.birthAugurs}} + + {{localize "DCC.BirthAugurDragHint"}} + + {{/if}} +
{{!-- Row separators --}} diff --git a/templates/dialog-actor-config.html b/templates/dialog-actor-config.html index 8ed5f49c..a3cd4fd6 100644 --- a/templates/dialog-actor-config.html +++ b/templates/dialog-actor-config.html @@ -84,6 +84,12 @@ id="system.config.computeSavingThrows" {{checked system.config.computeSavingThrows}} data-dtype="boolean"/> +
+ + +