Iteam cleanup + less migration

This commit is contained in:
2026-03-07 19:18:03 +01:00
parent 97cd50ed12
commit c6f7a9e966
60 changed files with 1633 additions and 851 deletions

View File

@@ -2,7 +2,6 @@ export { default as OathHammerCharacterSheet } from "./sheets/character-sheet.mj
export { default as OathHammerNPCSheet } from "./sheets/npc-sheet.mjs"
export { default as OathHammerWeaponSheet } from "./sheets/weapon-sheet.mjs"
export { default as OathHammerArmorSheet } from "./sheets/armor-sheet.mjs"
export { default as OathHammerShieldSheet } from "./sheets/shield-sheet.mjs"
export { default as OathHammerAmmunitionSheet } from "./sheets/ammunition-sheet.mjs"
export { default as OathHammerEquipmentSheet } from "./sheets/equipment-sheet.mjs"
export { default as OathHammerSpellSheet } from "./sheets/spell-sheet.mjs"
@@ -10,4 +9,3 @@ export { default as OathHammerMiracleSheet } from "./sheets/miracle-sheet.mjs"
export { default as OathHammerMagicItemSheet } from "./sheets/magic-item-sheet.mjs"
export { default as OathHammerAbilitySheet } from "./sheets/ability-sheet.mjs"
export { default as OathHammerOathSheet } from "./sheets/oath-sheet.mjs"
export { default as OathHammerConditionSheet } from "./sheets/condition-sheet.mjs"

View File

@@ -86,7 +86,6 @@ export default class OathHammerCharacterSheet extends OathHammerActorSheet {
context.tab = context.tabs.combat
context.weapons = doc.itemTypes.weapon
context.armors = doc.itemTypes.armor
context.shields = doc.itemTypes.shield
context.ammunition = doc.itemTypes.ammunition
break
case "magic":
@@ -98,7 +97,6 @@ export default class OathHammerCharacterSheet extends OathHammerActorSheet {
context.tab = context.tabs.equipment
context.equipment = doc.itemTypes.equipment
context.magicItems = doc.itemTypes["magic-item"]
context.conditions = doc.itemTypes.condition
break
case "notes":
context.tab = context.tabs.notes

View File

@@ -1,27 +0,0 @@
import OathHammerItemSheet from "./base-item-sheet.mjs"
export default class OathHammerConditionSheet extends OathHammerItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["condition"],
position: {
width: 620,
},
window: {
contentClasses: ["condition-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-oath-hammer/templates/item/condition-sheet.hbs",
},
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
return context
}
}

View File

@@ -22,8 +22,9 @@ export default class OathHammerOathSheet extends OathHammerItemSheet {
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.enrichedBenefit = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.benefit, { async: true })
context.enrichedViolation = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.violation, { async: true })
context.enrichedTenet = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.tenet, { async: true })
context.enrichedBoon = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.boon, { async: true })
context.enrichedBane = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.bane, { async: true })
return context
}
}

View File

@@ -1,27 +0,0 @@
import OathHammerItemSheet from "./base-item-sheet.mjs"
export default class OathHammerShieldSheet extends OathHammerItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["shield"],
position: {
width: 620,
},
window: {
contentClasses: ["shield-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-oath-hammer/templates/item/shield-sheet.hbs",
},
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
return context
}
}

View File

@@ -10,79 +10,102 @@ export const ATTRIBUTES = {
}
export const LINEAGE_CHOICES = {
dwarf: { id: "dwarf", label: "OATHHAMMER.Lineage.Dwarf" },
human: { id: "human", label: "OATHHAMMER.Lineage.Human" },
elf: { id: "elf", label: "OATHHAMMER.Lineage.Elf" },
halfelf: { id: "halfelf", label: "OATHHAMMER.Lineage.HalfElf" },
halfling: { id: "halfling", label: "OATHHAMMER.Lineage.Halfling" }
dwarf: { id: "dwarf", label: "OATHHAMMER.Lineage.Dwarf" },
firbolg: { id: "firbolg", label: "OATHHAMMER.Lineage.Firbolg" },
halfling: { id: "halfling", label: "OATHHAMMER.Lineage.Halfling" },
"high-elf": { id: "high-elf", label: "OATHHAMMER.Lineage.HighElf" },
human: { id: "human", label: "OATHHAMMER.Lineage.Human" },
"wood-elf": { id: "wood-elf", label: "OATHHAMMER.Lineage.WoodElf" }
}
export const CLASS_CHOICES = {
fighter: { id: "fighter", label: "OATHHAMMER.Class.Fighter" },
ranger: { id: "ranger", label: "OATHHAMMER.Class.Ranger" },
wizard: { id: "wizard", label: "OATHHAMMER.Class.Wizard" },
cleric: { id: "cleric", label: "OATHHAMMER.Class.Cleric" },
rogue: { id: "rogue", label: "OATHHAMMER.Class.Rogue" },
paladin: { id: "paladin", label: "OATHHAMMER.Class.Paladin" }
berserker: { id: "berserker", label: "OATHHAMMER.Class.Berserker" },
champion: { id: "champion", label: "OATHHAMMER.Class.Champion" },
delver: { id: "delver", label: "OATHHAMMER.Class.Delver" },
knight: { id: "knight", label: "OATHHAMMER.Class.Knight" },
mage: { id: "mage", label: "OATHHAMMER.Class.Mage" },
priest: { id: "priest", label: "OATHHAMMER.Class.Priest" },
scout: { id: "scout", label: "OATHHAMMER.Class.Scout" },
soldier: { id: "soldier", label: "OATHHAMMER.Class.Soldier" },
spellblade: { id: "spellblade", label: "OATHHAMMER.Class.Spellblade" },
troubadour: { id: "troubadour", label: "OATHHAMMER.Class.Troubadour" }
}
export const OATH_TYPES = {
"oath-of-justice": { id: "oath-of-justice", label: "OATHHAMMER.Oath.Justice" },
"oath-of-courage": { id: "oath-of-courage", label: "OATHHAMMER.Oath.Courage" },
"oath-of-honor": { id: "oath-of-honor", label: "OATHHAMMER.Oath.Honor" },
"oath-of-mercy": { id: "oath-of-mercy", label: "OATHHAMMER.Oath.Mercy" },
"oath-of-truth": { id: "oath-of-truth", label: "OATHHAMMER.Oath.Truth" },
"oath-of-valor": { id: "oath-of-valor", label: "OATHHAMMER.Oath.Valor" },
"oath-of-protection": { id: "oath-of-protection", label: "OATHHAMMER.Oath.Protection" },
"oath-of-vengeance": { id: "oath-of-vengeance", label: "OATHHAMMER.Oath.Vengeance" },
"oath-of-sacrifice": { id: "oath-of-sacrifice", label: "OATHHAMMER.Oath.Sacrifice" },
"oath-of-faith": { id: "oath-of-faith", label: "OATHHAMMER.Oath.Faith" },
"oath-of-service": { id: "oath-of-service", label: "OATHHAMMER.Oath.Service" },
"oath-of-brotherhood": { id: "oath-of-brotherhood", label: "OATHHAMMER.Oath.Brotherhood" }
"oath-of-compassion": { id: "oath-of-compassion", label: "OATHHAMMER.OathType.Compassion" },
"oath-of-courage": { id: "oath-of-courage", label: "OATHHAMMER.OathType.Courage" },
"oath-of-diligence": { id: "oath-of-diligence", label: "OATHHAMMER.OathType.Diligence" },
"oath-of-faith": { id: "oath-of-faith", label: "OATHHAMMER.OathType.Faith" },
"oath-of-humility": { id: "oath-of-humility", label: "OATHHAMMER.OathType.Humility" },
"oath-of-justice": { id: "oath-of-justice", label: "OATHHAMMER.OathType.Justice" },
"oath-of-loyalty": { id: "oath-of-loyalty", label: "OATHHAMMER.OathType.Loyalty" },
"oath-of-peace": { id: "oath-of-peace", label: "OATHHAMMER.OathType.Peace" },
"oath-of-perseverance": { id: "oath-of-perseverance", label: "OATHHAMMER.OathType.Perseverance" },
"oath-of-purity": { id: "oath-of-purity", label: "OATHHAMMER.OathType.Purity" },
"oath-of-truth": { id: "oath-of-truth", label: "OATHHAMMER.OathType.Truth" },
"oath-of-wisdom": { id: "oath-of-wisdom", label: "OATHHAMMER.OathType.Wisdom" }
}
export const SORCEROUS_TRADITIONS = {
elemental: { id: "elemental", label: "OATHHAMMER.Tradition.Elemental" },
elemental: { id: "elemental", label: "OATHHAMMER.Tradition.Elemental" },
illusionist: { id: "illusionist", label: "OATHHAMMER.Tradition.Illusionist" },
imperial: { id: "imperial", label: "OATHHAMMER.Tradition.Imperial" },
infernal: { id: "infernal", label: "OATHHAMMER.Tradition.Infernal" },
runic: { id: "runic", label: "OATHHAMMER.Tradition.Runic" },
stygian: { id: "stygian", label: "OATHHAMMER.Tradition.Stygian" }
imperial: { id: "imperial", label: "OATHHAMMER.Tradition.Imperial" },
infernal: { id: "infernal", label: "OATHHAMMER.Tradition.Infernal" },
runic: { id: "runic", label: "OATHHAMMER.Tradition.Runic" },
stygian: { id: "stygian", label: "OATHHAMMER.Tradition.Stygian" }
}
export const WEAPON_TYPE_CHOICES = {
melee: "OATHHAMMER.WeaponType.Melee",
ranged: "OATHHAMMER.WeaponType.Ranged"
// Three divine traditions for miracles (p.130)
export const DIVINE_TRADITIONS = {
druidic: "OATHHAMMER.DivineTradition.Druidic",
profane: "OATHHAMMER.DivineTradition.Profane",
sanctified: "OATHHAMMER.DivineTradition.Sanctified"
}
export const DAMAGE_TYPE_CHOICES = {
slashing: "OATHHAMMER.DamageType.Slashing",
piercing: "OATHHAMMER.DamageType.Piercing",
bludgeoning: "OATHHAMMER.DamageType.Bludgeoning",
fire: "OATHHAMMER.DamageType.Fire",
cold: "OATHHAMMER.DamageType.Cold",
lightning: "OATHHAMMER.DamageType.Lightning",
acid: "OATHHAMMER.DamageType.Acid",
poison: "OATHHAMMER.DamageType.Poison",
necrotic: "OATHHAMMER.DamageType.Necrotic",
radiant: "OATHHAMMER.DamageType.Radiant"
// Elemental sub-types for Elemental tradition spells (p.103)
export const ELEMENTAL_CHOICES = {
air: "OATHHAMMER.Element.Air",
earth: "OATHHAMMER.Element.Earth",
fire: "OATHHAMMER.Element.Fire",
water: "OATHHAMMER.Element.Water",
varies: "OATHHAMMER.Element.Varies"
}
export const ATTRIBUTE_BONUS_CHOICES = {
might: "OATHHAMMER.Attribute.Might",
agility: "OATHHAMMER.Attribute.Agility",
none: "OATHHAMMER.Label.None"
// Rune types for Runic tradition spells (p.120)
export const RUNE_TYPE_CHOICES = {
armor: "OATHHAMMER.RuneType.Armor",
talisman: "OATHHAMMER.RuneType.Talisman",
warding: "OATHHAMMER.RuneType.Warding",
weapon: "OATHHAMMER.RuneType.Weapon"
}
export const RANGE_CHOICES = {
short: "OATHHAMMER.Range.Short",
medium: "OATHHAMMER.Range.Medium",
long: "OATHHAMMER.Range.Long"
// Weapon proficiency groups (from book pp.82-84)
export const WEAPON_PROFICIENCY_GROUPS = {
common: "OATHHAMMER.WeaponGroup.Common",
dueling: "OATHHAMMER.WeaponGroup.Dueling",
heavy: "OATHHAMMER.WeaponGroup.Heavy",
polearms: "OATHHAMMER.WeaponGroup.Polearms",
bows: "OATHHAMMER.WeaponGroup.Bows",
throwing: "OATHHAMMER.WeaponGroup.Throwing"
}
export const HANDS_CHOICES = {
"one-handed": "OATHHAMMER.Hands.OneHanded",
"two-handed": "OATHHAMMER.Hands.TwoHanded"
// Weapon traits (from book p.82)
export const WEAPON_TRAITS = {
block: "OATHHAMMER.WeaponTrait.Block",
brutal: "OATHHAMMER.WeaponTrait.Brutal",
clumsy: "OATHHAMMER.WeaponTrait.Clumsy",
couched: "OATHHAMMER.WeaponTrait.Couched",
deadly: "OATHHAMMER.WeaponTrait.Deadly",
fast: "OATHHAMMER.WeaponTrait.Fast",
flaming: "OATHHAMMER.WeaponTrait.Flaming",
nimble: "OATHHAMMER.WeaponTrait.Nimble",
parry: "OATHHAMMER.WeaponTrait.Parry",
reload: "OATHHAMMER.WeaponTrait.Reload",
repel: "OATHHAMMER.WeaponTrait.Repel",
stunning: "OATHHAMMER.WeaponTrait.Stunning",
sweep: "OATHHAMMER.WeaponTrait.Sweep",
"two-handed": "OATHHAMMER.WeaponTrait.TwoHanded",
versatile: "OATHHAMMER.WeaponTrait.Versatile"
}
export const CURRENCY_CHOICES = {
@@ -92,42 +115,52 @@ export const CURRENCY_CHOICES = {
}
export const ARMOR_TYPE_CHOICES = {
light: "OATHHAMMER.ArmorType.Light",
light: "OATHHAMMER.ArmorType.Light",
medium: "OATHHAMMER.ArmorType.Medium",
heavy: "OATHHAMMER.ArmorType.Heavy"
heavy: "OATHHAMMER.ArmorType.Heavy"
}
// Armor traits (p.88): Clanging = -1 Stealth; Reinforced = red dice for armor rolls
export const ARMOR_TRAITS = {
clanging: "OATHHAMMER.ArmorTrait.Clanging",
reinforced: "OATHHAMMER.ArmorTrait.Reinforced"
}
// Ammunition types (p.88): standard = arrow/bolt; bodkin = -1 armor; envenomed = poison; incendiary = flaming
export const AMMO_TYPE_CHOICES = {
arrow: "OATHHAMMER.AmmoType.Arrow",
bolt: "OATHHAMMER.AmmoType.Bolt",
stone: "OATHHAMMER.AmmoType.Stone",
javelin: "OATHHAMMER.AmmoType.Javelin",
"throwing-knife": "OATHHAMMER.AmmoType.ThrowingKnife"
standard: "OATHHAMMER.AmmoType.Standard",
bodkin: "OATHHAMMER.AmmoType.Bodkin",
envenomed: "OATHHAMMER.AmmoType.Envenomed",
incendiary: "OATHHAMMER.AmmoType.Incendiary"
}
// Equipment sub-categories matching the rulebook sections (pp.90-96)
export const EQUIPMENT_TYPE_CHOICES = {
potion: "OATHHAMMER.EquipmentType.Potion",
container: "OATHHAMMER.EquipmentType.Container",
tool: "OATHHAMMER.EquipmentType.Tool",
consumable: "OATHHAMMER.EquipmentType.Consumable",
misc: "OATHHAMMER.EquipmentType.Misc",
potion: "OATHHAMMER.EquipmentType.Potion",
container: "OATHHAMMER.EquipmentType.Container",
mount: "OATHHAMMER.EquipmentType.Mount",
"healing-supply": "OATHHAMMER.EquipmentType.HealingSupply",
food: "OATHHAMMER.EquipmentType.Food",
mount: "OATHHAMMER.EquipmentType.Mount",
vehicle: "OATHHAMMER.EquipmentType.Vehicle",
"war-machine": "OATHHAMMER.EquipmentType.WarMachine"
food: "OATHHAMMER.EquipmentType.Food",
"light-source": "OATHHAMMER.EquipmentType.LightSource",
misc: "OATHHAMMER.EquipmentType.Misc",
vehicle: "OATHHAMMER.EquipmentType.Vehicle",
animal: "OATHHAMMER.EquipmentType.Animal",
"war-machine": "OATHHAMMER.EquipmentType.WarMachine"
}
// Magic item sub-types for Focus/Talisman/Trinket (p.136)
// Weapon and Armor magic items use their respective item types with isMagic=true.
export const MAGIC_ITEM_TYPE_CHOICES = {
weapon: "OATHHAMMER.MagicItemType.Weapon",
armor: "OATHHAMMER.MagicItemType.Armor",
wondrous: "OATHHAMMER.MagicItemType.Wondrous",
potion: "OATHHAMMER.MagicItemType.Potion",
ring: "OATHHAMMER.MagicItemType.Ring",
staff: "OATHHAMMER.MagicItemType.Staff",
wand: "OATHHAMMER.MagicItemType.Wand",
scroll: "OATHHAMMER.MagicItemType.Scroll",
rod: "OATHHAMMER.MagicItemType.Rod"
focus: "OATHHAMMER.MagicItemType.Focus",
talisman: "OATHHAMMER.MagicItemType.Talisman",
trinket: "OATHHAMMER.MagicItemType.Trinket"
}
// Magic item quality (p.136): determines power and how they are found
export const MAGIC_QUALITY_CHOICES = {
lesser: "OATHHAMMER.MagicQuality.Lesser",
greater: "OATHHAMMER.MagicQuality.Greater",
legendary: "OATHHAMMER.MagicQuality.Legendary"
}
export const RARITY_CHOICES = {
@@ -138,24 +171,112 @@ export const RARITY_CHOICES = {
legendary: "OATHHAMMER.Rarity.Legendary"
}
// Two types of ability: class traits and lineage traits. No feats in Oath Hammer.
export const ABILITY_TYPE_CHOICES = {
"class-ability": "OATHHAMMER.AbilityType.ClassAbility",
"lineage-trait": "OATHHAMMER.AbilityType.LineageTrait",
feat: "OATHHAMMER.AbilityType.Feat"
"lineage-trait": "OATHHAMMER.AbilityType.LineageTrait"
}
export const CONDITION_TYPE_CHOICES = {
blinded: "OATHHAMMER.Condition.Blinded",
deafened: "OATHHAMMER.Condition.Deafened",
prone: "OATHHAMMER.Condition.Prone",
stunned: "OATHHAMMER.Condition.Stunned",
frightened: "OATHHAMMER.Condition.Frightened",
poisoned: "OATHHAMMER.Condition.Poisoned",
restrained: "OATHHAMMER.Condition.Restrained",
wounded: "OATHHAMMER.Condition.Wounded",
other: "OATHHAMMER.Condition.Other"
// When an ability's uses reset (none = passive/always on)
export const ABILITY_USAGE_PERIOD = {
none: "OATHHAMMER.UsagePeriod.None",
encounter: "OATHHAMMER.UsagePeriod.Encounter",
day: "OATHHAMMER.UsagePeriod.Day"
}
export const STATUS_EFFECTS = [
{
id: "blinded",
name: "OATHHAMMER.Condition.Blinded",
img: "systems/fvtt-oath-hammer/assets/icons/conditions/blinded.svg",
description: "OATHHAMMER.Condition.BlindedDesc"
},
{
id: "confused",
name: "OATHHAMMER.Condition.Confused",
img: "systems/fvtt-oath-hammer/assets/icons/conditions/confused.svg",
description: "OATHHAMMER.Condition.ConfusedDesc"
},
{
id: "dazed",
name: "OATHHAMMER.Condition.Dazed",
img: "systems/fvtt-oath-hammer/assets/icons/conditions/dazed.svg",
description: "OATHHAMMER.Condition.DazedDesc"
},
{
id: "deafened",
name: "OATHHAMMER.Condition.Deafened",
img: "systems/fvtt-oath-hammer/assets/icons/conditions/deafened.svg",
description: "OATHHAMMER.Condition.DeafenedDesc"
},
{
id: "demoralized",
name: "OATHHAMMER.Condition.Demoralized",
img: "systems/fvtt-oath-hammer/assets/icons/conditions/demoralized.svg",
description: "OATHHAMMER.Condition.DemoralizedDesc"
},
{
id: "diseased",
name: "OATHHAMMER.Condition.Diseased",
img: "systems/fvtt-oath-hammer/assets/icons/conditions/diseased.svg",
description: "OATHHAMMER.Condition.DiseasedDesc"
},
{
id: "enfeebled",
name: "OATHHAMMER.Condition.Enfeebled",
img: "systems/fvtt-oath-hammer/assets/icons/conditions/enfeebled.svg",
description: "OATHHAMMER.Condition.EnfeebledDesc"
},
{
id: "fatigued",
name: "OATHHAMMER.Condition.Fatigued",
img: "systems/fvtt-oath-hammer/assets/icons/conditions/fatigued.svg",
description: "OATHHAMMER.Condition.FatiguedDesc"
},
{
id: "frightened",
name: "OATHHAMMER.Condition.Frightened",
img: "systems/fvtt-oath-hammer/assets/icons/conditions/frightened.svg",
description: "OATHHAMMER.Condition.FrightenedDesc"
},
{
id: "ignited",
name: "OATHHAMMER.Condition.Ignited",
img: "systems/fvtt-oath-hammer/assets/icons/conditions/ignited.svg",
description: "OATHHAMMER.Condition.IgnitedDesc"
},
{
id: "inspired",
name: "OATHHAMMER.Condition.Inspired",
img: "systems/fvtt-oath-hammer/assets/icons/conditions/inspired.svg",
description: "OATHHAMMER.Condition.InspiredDesc"
},
{
id: "invisible",
name: "OATHHAMMER.Condition.Invisible",
img: "systems/fvtt-oath-hammer/assets/icons/conditions/invisible.svg",
description: "OATHHAMMER.Condition.InvisibleDesc"
},
{
id: "poisoned",
name: "OATHHAMMER.Condition.Poisoned",
img: "systems/fvtt-oath-hammer/assets/icons/conditions/poisoned.svg",
description: "OATHHAMMER.Condition.PoisonedDesc"
},
{
id: "restrained",
name: "OATHHAMMER.Condition.Restrained",
img: "systems/fvtt-oath-hammer/assets/icons/conditions/restrained.svg",
description: "OATHHAMMER.Condition.RestrainedDesc"
},
{
id: "stunned",
name: "OATHHAMMER.Condition.Stunned",
img: "systems/fvtt-oath-hammer/assets/icons/conditions/stunned.svg",
description: "OATHHAMMER.Condition.StunnedDesc"
}
]
export const ATTRIBUTE_RANK_CHOICES = { 1: "1", 2: "2", 3: "3", 4: "4" }
export const ASCII = `
@@ -176,19 +297,22 @@ export const SYSTEM = {
CLASS_CHOICES,
OATH_TYPES,
SORCEROUS_TRADITIONS,
WEAPON_TYPE_CHOICES,
DAMAGE_TYPE_CHOICES,
ATTRIBUTE_BONUS_CHOICES,
RANGE_CHOICES,
HANDS_CHOICES,
CURRENCY_CHOICES,
DIVINE_TRADITIONS,
ELEMENTAL_CHOICES,
RUNE_TYPE_CHOICES,
WEAPON_PROFICIENCY_GROUPS,
WEAPON_TRAITS,
ARMOR_TYPE_CHOICES,
ARMOR_TRAITS,
CURRENCY_CHOICES,
AMMO_TYPE_CHOICES,
EQUIPMENT_TYPE_CHOICES,
MAGIC_ITEM_TYPE_CHOICES,
MAGIC_QUALITY_CHOICES,
RARITY_CHOICES,
ABILITY_TYPE_CHOICES,
CONDITION_TYPE_CHOICES,
ABILITY_USAGE_PERIOD,
STATUS_EFFECTS,
ATTRIBUTE_RANK_CHOICES,
ASCII
}

View File

@@ -20,16 +20,12 @@ export default class OathHammerActor extends Actor {
}
}
getArmorRating() {
let rating = 0
getArmorValue() {
for (const item of this.items) {
if (item.type === "armor" && item.system.equipped) {
rating += Number(item.system.armorRating) || 0
}
if (item.type === "shield" && item.system.equipped) {
rating += Number(item.system.shieldBonus) || 0
return Number(item.system.armorValue) || 0
}
}
return rating
return 0
}
}

View File

@@ -1,15 +1,13 @@
const defaultItemImg = {
weapon: "systems/fvtt-oath-hammer/assets/icons/icon_weapon.webp",
armor: "systems/fvtt-oath-hammer/assets/icons/icon_armor.webp",
shield: "systems/fvtt-oath-hammer/assets/icons/icon_shield.webp",
ammunition: "systems/fvtt-oath-hammer/assets/icons/icon_ammunition.webp",
equipment: "systems/fvtt-oath-hammer/assets/icons/icon_equipment.webp",
spell: "systems/fvtt-oath-hammer/assets/icons/icon_spell.webp",
miracle: "systems/fvtt-oath-hammer/assets/icons/icon_miracle.webp",
weapon: "systems/fvtt-oath-hammer/assets/icons/icon_weapon.webp",
armor: "systems/fvtt-oath-hammer/assets/icons/icon_armor.webp",
ammunition: "systems/fvtt-oath-hammer/assets/icons/icon_ammunition.webp",
equipment: "systems/fvtt-oath-hammer/assets/icons/icon_equipment.webp",
spell: "systems/fvtt-oath-hammer/assets/icons/icon_spell.webp",
miracle: "systems/fvtt-oath-hammer/assets/icons/icon_miracle.webp",
"magic-item": "systems/fvtt-oath-hammer/assets/icons/icon_magic_item.webp",
ability: "systems/fvtt-oath-hammer/assets/icons/icon_ability.webp",
oath: "systems/fvtt-oath-hammer/assets/icons/icon_oath.webp",
condition: "systems/fvtt-oath-hammer/assets/icons/icon_condition.webp"
ability: "systems/fvtt-oath-hammer/assets/icons/icon_ability.webp",
oath: "systems/fvtt-oath-hammer/assets/icons/icon_oath.webp"
}
export default class OathHammerItem extends Item {

View File

@@ -2,7 +2,6 @@ export { default as OathHammerCharacter } from "./character.mjs"
export { default as OathHammerNPC } from "./npc.mjs"
export { default as OathHammerWeapon } from "./weapon.mjs"
export { default as OathHammerArmor } from "./armor.mjs"
export { default as OathHammerShield } from "./shield.mjs"
export { default as OathHammerAmmunition } from "./ammunition.mjs"
export { default as OathHammerEquipment } from "./equipment.mjs"
export { default as OathHammerSpell } from "./spell.mjs"
@@ -10,4 +9,3 @@ export { default as OathHammerMiracle } from "./miracle.mjs"
export { default as OathHammerMagicItem } from "./magic-item.mjs"
export { default as OathHammerAbility } from "./ability.mjs"
export { default as OathHammerOath } from "./oath.mjs"
export { default as OathHammerCondition } from "./condition.mjs"

View File

@@ -3,13 +3,28 @@ import { SYSTEM } from "../config/system.mjs"
export default class OathHammerAbility extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const requiredInteger = { required: true, nullable: false, integer: true }
const schema = {}
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.abilityType = new fields.StringField({ required: true, initial: "class-ability", choices: SYSTEM.ABILITY_TYPE_CHOICES })
// lineage-trait (racial) or class-ability (starting or advancement trait)
schema.abilityType = new fields.StringField({
required: true, initial: "class-ability", choices: SYSTEM.ABILITY_TYPE_CHOICES
})
// Which class or lineage this trait belongs to (e.g. "Berserker", "Wood Elf")
schema.source = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.prerequisite = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.passiveBonus = new fields.StringField({ required: true, nullable: false, initial: "" })
// When uses reset: none = passive (always on), encounter, day
schema.usagePeriod = new fields.StringField({
required: true, initial: "none", choices: SYSTEM.ABILITY_USAGE_PERIOD
})
// Maximum uses per period. 0 = passive / unlimited.
// Use a descriptive string when the limit is formula-based
// (e.g. "equal to Fate ranks") — store the note in the description.
schema.maxUses = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
return schema
}

View File

@@ -7,11 +7,21 @@ export default class OathHammerAmmunition extends foundry.abstract.TypeDataModel
const schema = {}
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.ammoType = new fields.StringField({ required: true, initial: "arrow", choices: SYSTEM.AMMO_TYPE_CHOICES })
schema.quantity = new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 })
schema.properties = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.cost = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
schema.currency = new fields.StringField({ required: true, initial: "gp", choices: SYSTEM.CURRENCY_CHOICES })
// Ammo type determines special effect (p.88):
// bodkin → 1 to target's armor roll
// envenomed → poison damage
// incendiary → flaming damage
schema.ammoType = new fields.StringField({ required: true, initial: "standard", choices: SYSTEM.AMMO_TYPE_CHOICES })
// Quantity of individual arrows/bolts in this stack
schema.quantity = new fields.NumberField({ ...requiredInteger, initial: 20, min: 0 })
// Rarity: DV for Fortune check when purchasing; 0 = always available
schema.rarity = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0, max: 6 })
schema.cost = new fields.NumberField({ required: true, nullable: false, initial: 0, min: 0 })
schema.currency = new fields.StringField({ required: true, initial: "sp", choices: SYSTEM.CURRENCY_CHOICES })
return schema
}

View File

@@ -7,14 +7,41 @@ export default class OathHammerArmor extends foundry.abstract.TypeDataModel {
const schema = {}
schema.description = new fields.HTMLField({ required: true, textSearch: true })
// Proficiency group: light / medium / heavy (p.88)
schema.armorType = new fields.StringField({ required: true, initial: "light", choices: SYSTEM.ARMOR_TYPE_CHOICES })
schema.armorRating = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
schema.movementPenalty = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
schema.equipped = new fields.BooleanField({ required: true, initial: false })
schema.encumbrance = new fields.NumberField({ required: true, initial: 1, min: 0 })
schema.cost = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
// Armor Value (AV): number of armor dice rolled when receiving damage
schema.armorValue = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0, max: 12 })
// Penalty: modifier to Acrobatics checks AND defense rolls (0, -1, -2, -3…)
schema.penalty = new fields.NumberField({ ...requiredInteger, initial: 0, min: -5, max: 0 })
// Item slots occupied while worn or stowed
schema.slots = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
// Armor traits: Clanging (1 Stealth) / Reinforced (red dice for armor rolls)
schema.traits = new fields.SetField(
new fields.StringField({ choices: SYSTEM.ARMOR_TRAITS })
)
// Rarity: DV for Fortune check when purchasing; 0 = always available
schema.rarity = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0, max: 6 })
schema.equipped = new fields.BooleanField({ initial: false })
schema.cost = new fields.NumberField({ required: true, nullable: false, initial: 0, min: 0 })
schema.currency = new fields.StringField({ required: true, initial: "gp", choices: SYSTEM.CURRENCY_CHOICES })
// --- Magic properties (only relevant when isMagic = true) ---
schema.isMagic = new fields.BooleanField({ initial: false })
schema.magicQuality = new fields.StringField({
required: false, nullable: true, initial: null, choices: SYSTEM.MAGIC_QUALITY_CHOICES
})
schema.isCursed = new fields.BooleanField({ initial: false })
schema.magicEffect = new fields.HTMLField({ required: false, textSearch: true })
// Class/lineage restriction, e.g. "Dwarves only" (empty = no restriction)
schema.classRestriction = new fields.StringField({ required: true, nullable: false, initial: "" })
return schema
}

View File

@@ -54,7 +54,7 @@ export default class OathHammerCharacter extends foundry.abstract.TypeDataModel
schema.biodata = new fields.SchemaField({
lineage: new fields.StringField({ required: true, initial: "dwarf", choices: SYSTEM.LINEAGE_CHOICES }),
class: new fields.StringField({ required: true, initial: "fighter", choices: SYSTEM.CLASS_CHOICES }),
class: new fields.StringField({ required: true, initial: "soldier", choices: SYSTEM.CLASS_CHOICES }),
age: new fields.StringField({ required: true, nullable: false, initial: "" }),
gender: new fields.StringField({ required: true, nullable: false, initial: "" }),
height: new fields.StringField({ required: true, nullable: false, initial: "" }),

View File

@@ -1,17 +0,0 @@
import { SYSTEM } from "../config/system.mjs"
export default class OathHammerCondition extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const schema = {}
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.conditionType = new fields.StringField({ required: true, initial: "stunned", choices: SYSTEM.CONDITION_TYPE_CHOICES })
schema.duration = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.source = new fields.StringField({ required: true, nullable: false, initial: "" })
return schema
}
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Condition"]
}

View File

@@ -7,10 +7,22 @@ export default class OathHammerEquipment extends foundry.abstract.TypeDataModel
const schema = {}
schema.description = new fields.HTMLField({ required: true, textSearch: true })
// Sub-category matching the rulebook sections (pp.90-96)
schema.itemType = new fields.StringField({ required: true, initial: "misc", choices: SYSTEM.EQUIPMENT_TYPE_CHOICES })
schema.quantity = new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 })
schema.weight = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.cost = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
// Item slots occupied when carried. 0 = small item (no slots).
schema.slots = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
// Rarity: DV for Fortune check when purchasing; 0 = always available
schema.rarity = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0, max: 6 })
// Light radius in feet — only relevant for light-source items (Candle/Lamp/Lantern/Torch)
schema.lightRadius = new fields.NumberField({ required: false, nullable: true, initial: null, min: 0 })
schema.cost = new fields.NumberField({ required: true, nullable: false, initial: 0, min: 0 })
schema.currency = new fields.StringField({ required: true, initial: "gp", choices: SYSTEM.CURRENCY_CHOICES })
return schema

View File

@@ -6,18 +6,39 @@ export default class OathHammerMagicItem extends foundry.abstract.TypeDataModel
const requiredInteger = { required: true, nullable: false, integer: true }
const schema = {}
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.itemType = new fields.StringField({ required: true, initial: "wondrous", choices: SYSTEM.MAGIC_ITEM_TYPE_CHOICES })
schema.rarity = new fields.StringField({ required: true, initial: "common", choices: SYSTEM.RARITY_CHOICES })
schema.attunement = new fields.BooleanField({ required: true, initial: false })
schema.charges = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
// The magical effect / description of the item
schema.effect = new fields.HTMLField({ required: true, textSearch: true })
// Sub-type: Focus (arcane/divine instrument), Talisman (worn item), Trinket (misc object)
// Note: magic weapons and armor use the weapon/armor item types with isMagic=true instead.
schema.itemType = new fields.StringField({
required: true, initial: "talisman", choices: SYSTEM.MAGIC_ITEM_TYPE_CHOICES
})
schema.recharge = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.equipped = new fields.BooleanField({ required: true, initial: false })
schema.cost = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
schema.currency = new fields.StringField({ required: true, initial: "gp", choices: SYSTEM.CURRENCY_CHOICES })
// Quality: lesser / greater / legendary (determines power and how found, p.136)
schema.quality = new fields.StringField({
required: true, initial: "lesser", choices: SYSTEM.MAGIC_QUALITY_CHOICES
})
// Cursed items impose a bane that cannot be removed until the curse is broken
schema.isCursed = new fields.BooleanField({ initial: false })
// Legendary items bond to a single character (cannot be shared, p.136)
schema.isBonded = new fields.BooleanField({ initial: false })
// Class/lineage restriction printed in the item's type line, e.g. "Troubadour Only"
schema.classRestriction = new fields.StringField({ required: true, nullable: false, initial: "" })
// Limited-use items (e.g. "once per day"); none = always active
schema.usagePeriod = new fields.StringField({
required: true, initial: "none", choices: SYSTEM.ABILITY_USAGE_PERIOD
})
schema.maxUses = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
// Item slots occupied when carried; 0 = small item (no slots)
schema.slots = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
schema.equipped = new fields.BooleanField({ initial: false })
return schema
}

View File

@@ -7,17 +7,28 @@ export default class OathHammerMiracle extends foundry.abstract.TypeDataModel {
const schema = {}
schema.effect = new fields.HTMLField({ required: true, textSearch: true })
schema.piety = new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 })
schema.castingTime = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.range = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.duration = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.components = new fields.SchemaField({
verbal: new fields.BooleanField(),
somatic: new fields.BooleanField(),
material: new fields.BooleanField()
// Divine tradition (Druidic / Profane / Sanctified)
schema.divineTradition = new fields.StringField({
required: true, initial: "sanctified", choices: SYSTEM.DIVINE_TRADITIONS
})
schema.materialComponent = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.savingThrow = new fields.StringField({ required: true, nullable: false, initial: "" })
// Difficulty Value: 0 = scales dynamically (1st = DV1, 2nd = DV2…).
// Non-zero only for Ritual miracles which have a fixed DV (p.129).
schema.difficultyValue = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0, max: 10 })
// Ritual miracles require 1 hour; need a holy book; fixed DV; don't
// increment the daily miracle counter (p.129).
schema.isRitual = new fields.BooleanField({ initial: false })
// Range: "Touch", "Self", "20", "100", "1 mile", etc.
schema.range = new fields.StringField({ required: true, nullable: false, initial: "" })
// Duration: "1 hour", "Encounter", "1 day", etc. Empty = instantaneous.
schema.duration = new fields.StringField({ required: true, nullable: false, initial: "" })
// Spell Save: e.g. "DV4 Athletics", "DV5 Fortune". Empty = no save.
schema.spellSave = new fields.StringField({ required: true, nullable: false, initial: "" })
return schema
}

View File

@@ -5,9 +5,10 @@ export default class OathHammerOath extends foundry.abstract.TypeDataModel {
const fields = foundry.data.fields
const schema = {}
schema.benefit = new fields.HTMLField({ required: true, textSearch: true })
schema.violation = new fields.HTMLField({ required: true, textSearch: true })
schema.oathType = new fields.StringField({ required: true, initial: "oath-of-justice", choices: SYSTEM.OATH_TYPES })
schema.tenet = new fields.HTMLField({ required: false, textSearch: true })
schema.boon = new fields.HTMLField({ required: true, textSearch: true })
schema.bane = new fields.HTMLField({ required: true, textSearch: true })
schema.violated = new fields.BooleanField({ required: true, initial: false })
return schema

View File

@@ -1,20 +0,0 @@
import { SYSTEM } from "../config/system.mjs"
export default class OathHammerShield extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const requiredInteger = { required: true, nullable: false, integer: true }
const schema = {}
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.shieldBonus = new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 })
schema.equipped = new fields.BooleanField({ required: true, initial: false })
schema.encumbrance = new fields.NumberField({ required: true, initial: 1, min: 0 })
schema.cost = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
schema.currency = new fields.StringField({ required: true, initial: "gp", choices: SYSTEM.CURRENCY_CHOICES })
return schema
}
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Shield"]
}

View File

@@ -7,20 +7,42 @@ export default class OathHammerSpell extends foundry.abstract.TypeDataModel {
const schema = {}
schema.effect = new fields.HTMLField({ required: true, textSearch: true })
schema.tradition = new fields.StringField({ required: true, initial: "elemental", choices: SYSTEM.SORCEROUS_TRADITIONS })
schema.level = new fields.NumberField({ ...requiredInteger, initial: 1, min: 1, max: 6 })
schema.castingTime = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.range = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.duration = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.arcaneStress = new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 })
schema.components = new fields.SchemaField({
verbal: new fields.BooleanField(),
somatic: new fields.BooleanField(),
material: new fields.BooleanField()
// Arcane tradition (Elemental / Illusionist / Imperial / Infernal / Runic / Stygian)
schema.tradition = new fields.StringField({
required: true, initial: "elemental", choices: SYSTEM.SORCEROUS_TRADITIONS
})
schema.materialComponent = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.savingThrow = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.enhancement = new fields.StringField({ required: true, nullable: false, initial: "" })
// Difficulty Value: the Magic check DV needed to cast this spell
schema.difficultyValue = new fields.NumberField({ ...requiredInteger, initial: 1, min: 1, max: 10 })
// Ritual spells take 1 hour to cast; DV is listed with "(Ritual)" in the book
schema.isRitual = new fields.BooleanField({ initial: false })
// Magic Missile spells can be intercepted like ranged attacks (p.101)
schema.isMagicMissile = new fields.BooleanField({ initial: false })
// Range: "Touch", "Self", "40", "200", "Cone AoE", "Large AoE", etc.
schema.range = new fields.StringField({ required: true, nullable: false, initial: "" })
// Duration: "1 hour", "Encounter", "2d3 rounds", etc. Empty = instantaneous.
schema.duration = new fields.StringField({ required: true, nullable: false, initial: "" })
// Spell Save: e.g. "DV5 Resilience", "DV3 Acrobatics". Empty = no save.
schema.spellSave = new fields.StringField({ required: true, nullable: false, initial: "" })
// Elemental tradition only — sub-element (Air / Earth / Fire / Water / Varies)
schema.element = new fields.StringField({
required: false, nullable: true, initial: null, choices: SYSTEM.ELEMENTAL_CHOICES
})
// Runic tradition only — what surface/object the rune is inscribed upon
schema.runeType = new fields.StringField({
required: false, nullable: true, initial: null, choices: SYSTEM.RUNE_TYPE_CHOICES
})
// Runic tradition only — exalted runes activate once then vanish (p.120)
schema.isExalted = new fields.BooleanField({ initial: false })
return schema
}

View File

@@ -7,20 +7,69 @@ export default class OathHammerWeapon extends foundry.abstract.TypeDataModel {
const schema = {}
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.weaponType = new fields.StringField({ required: true, initial: "melee", choices: SYSTEM.WEAPON_TYPE_CHOICES })
schema.damageFormula = new fields.StringField({ required: true, nullable: false, initial: "1d6" })
schema.damageType = new fields.StringField({ required: true, initial: "slashing", choices: SYSTEM.DAMAGE_TYPE_CHOICES })
schema.attributeBonus = new fields.StringField({ required: true, initial: "might", choices: SYSTEM.ATTRIBUTE_BONUS_CHOICES })
schema.range = new fields.StringField({ required: true, initial: "short", choices: SYSTEM.RANGE_CHOICES })
schema.hands = new fields.StringField({ required: true, initial: "one-handed", choices: SYSTEM.HANDS_CHOICES })
schema.properties = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.equipped = new fields.BooleanField({ required: true, initial: false })
schema.encumbrance = new fields.NumberField({ required: true, initial: 1, min: 0 })
schema.cost = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
schema.currency = new fields.StringField({ required: true, initial: "gp", choices: SYSTEM.CURRENCY_CHOICES })
// Proficiency group (Common, Dueling, Heavy, Polearms, Bows, Throwing)
schema.proficiencyGroup = new fields.StringField({
required: true, initial: "common", choices: SYSTEM.WEAPON_PROFICIENCY_GROUPS
})
// Damage: melee = Might rank + damageMod dice; bows = baseDice (fixed, no Might)
// usesMight=true → formula displayed as "M+2", "M-1", etc.
// usesMight=false → formula displayed as e.g. "6" (fixed dice for bows)
schema.usesMight = new fields.BooleanField({ required: true, initial: true })
schema.damageMod = new fields.NumberField({ ...requiredInteger, initial: 0, min: -4, max: 5 })
// AP (Armor Penetration): penalty imposed on armor/defense rolls
schema.ap = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0, max: 6 })
// Reach (melee, in ft: 5 / 10 / 15) — ignored for ranged/throwing
schema.reach = new fields.NumberField({ ...requiredInteger, initial: 5, min: 5 })
// Range (ranged & throwing, in ft): short and long
schema.shortRange = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
schema.longRange = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
// Traits — stored as a Set of trait keys (Block, Brutal, Nimble, Parry, etc.)
schema.traits = new fields.SetField(
new fields.StringField({ choices: SYSTEM.WEAPON_TRAITS }),
{ required: true, initial: [] }
)
// Item slots (when stowed; 0 = does not occupy slots)
schema.slots = new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 })
// Rarity (DV for Fortune check to find item for sale)
schema.rarity = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0, max: 5 })
schema.equipped = new fields.BooleanField({ required: true, initial: false })
schema.cost = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
schema.currency = new fields.StringField({ required: true, initial: "gp", choices: SYSTEM.CURRENCY_CHOICES })
// --- Magic properties (only relevant when isMagic = true) ---
schema.isMagic = new fields.BooleanField({ initial: false })
schema.magicQuality = new fields.StringField({
required: false, nullable: true, initial: null, choices: SYSTEM.MAGIC_QUALITY_CHOICES
})
schema.isCursed = new fields.BooleanField({ initial: false })
// Enchantment description (displayed when isMagic is true)
schema.magicEffect = new fields.HTMLField({ required: false, textSearch: true })
// Class/lineage restriction, e.g. "Dwarves only" (empty = no restriction)
schema.classRestriction = new fields.StringField({ required: true, nullable: false, initial: "" })
return schema
}
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Weapon"]
/**
* Human-readable damage formula for display, e.g. "M+2", "M-1", "6"
*/
get damageLabel() {
if (this.usesMight) {
const mod = this.damageMod
if (mod === 0) return "M+0"
return mod > 0 ? `M+${mod}` : `M${mod}`
}
return String(this.damageMod) // bows: store base dice count in damageMod
}
}