102 lines
4.6 KiB
JavaScript
102 lines
4.6 KiB
JavaScript
import { SYSTEM } from "../config/system.mjs"
|
|
|
|
export default class OathHammerWeapon 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 })
|
|
|
|
// Proficiency group (Common, Dueling, Heavy, Polearms, Bows, Throwing)
|
|
schema.proficiencyGroup = new fields.StringField({
|
|
required: true, initial: "common", choices: SYSTEM.WEAPON_PROFICIENCY_GROUPS
|
|
})
|
|
|
|
// Damage: melee/throwing = Might rank + damageMod dice; bows = baseDice (fixed, no Might)
|
|
// usesMight is now derived from proficiencyGroup (see getter below)
|
|
schema.damageMod = new fields.NumberField({ ...requiredInteger, initial: 0, min: -4, max: 16 })
|
|
|
|
// AP (Armor Penetration): penalty imposed on armor/defense rolls
|
|
schema.ap = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0, max: 16 })
|
|
|
|
// 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: [] }
|
|
)
|
|
|
|
// Special Properties — crafting enhancements (Accurate, Master-Crafted, etc. p.98)
|
|
schema.specialProperties = new fields.SetField(
|
|
new fields.StringField({ choices: SYSTEM.WEAPON_SPECIAL_PROPERTIES }),
|
|
{ required: true, initial: [] }
|
|
)
|
|
|
|
// Item slots (when stowed; 0 = does not occupy slots)
|
|
schema.slots = new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 })
|
|
|
|
schema.rarity = new fields.StringField({ required: true, initial: "common", choices: SYSTEM.RARITY_CHOICES })
|
|
|
|
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: false, nullable: true, initial: null, choices: SYSTEM.CLASS_RESTRICTION_CHOICES })
|
|
|
|
// Override which skill (and its linked attribute) is used for attack rolls.
|
|
// Null / "" = auto-detect (fighting for melee, shooting for ranged).
|
|
// Use this for abilities like Magic Bolt that roll Magic+Willpower instead.
|
|
schema.skillOverride = new fields.StringField({ required: false, nullable: true, initial: null })
|
|
|
|
// Attached runic spells (max 2; each is a snapshot of the spell item)
|
|
schema.runes = new fields.ArrayField(new fields.ObjectField(), { required: true, initial: [] })
|
|
|
|
return schema
|
|
}
|
|
|
|
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Weapon"]
|
|
|
|
static migrateData(source) {
|
|
if (typeof source.rarity === "number") {
|
|
const map = { 0: "common", 1: "uncommon", 2: "rare", 3: "very-rare", 4: "legendary", 5: "legendary", 6: "legendary" }
|
|
source.rarity = map[source.rarity] ?? "common"
|
|
}
|
|
// Remove legacy usesMight field — now derived from proficiencyGroup
|
|
delete source.usesMight
|
|
return super.migrateData(source)
|
|
}
|
|
|
|
/** Derived: only bows skip Might for damage. Throwing weapons keep Might (arm strength). */
|
|
get usesMight() {
|
|
return this.proficiencyGroup !== "bows"
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
}
|
|
}
|