Files
fvtt-oath-hammer/module/models/character.mjs
LeRatierBretonnier b3fd7e1aa1 feat: add Settlement actor type with Overview/Buildings/Inventory tabs
- New TypeDataModel: archetype, territory, renown, currency (gp/sp/cp),
  garrison, underSiege, isCapital, founded, taxNotes, description, notes
- 3-tab ApplicationV2 sheet with drag & drop for building/weapon/armor/equipment
- Currency steppers (+/−), building constructed toggle, qty controls
- LESS-based CSS (settlement-sheet.less) + base.less updated for shared styles
- Full i18n keys in lang/en.json (8 settlement archetypes)
- system.json: registered settlement actor type

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-20 17:01:38 +01:00

138 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export default class OathHammerCharacter extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const requiredInteger = { required: true, nullable: false, integer: true }
const schema = {}
schema.background = new fields.HTMLField({ required: true, textSearch: true })
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.notes = new fields.HTMLField({ required: true, textSearch: true })
// Lineage (simple fields on the actor — not an Item)
schema.lineage = new fields.SchemaField({
name: new fields.StringField({ required: true, nullable: false, initial: "" }),
traits: new fields.HTMLField({ required: true, textSearch: true }),
})
const attributeField = () => new fields.SchemaField({
rank: new fields.NumberField({ ...requiredInteger, initial: 1, min: 1, max: 4 })
})
schema.attributes = new fields.SchemaField({
might: attributeField(),
toughness: attributeField(),
agility: attributeField(),
willpower: attributeField(),
intelligence: attributeField(),
fate: attributeField()
})
// Skills: 26 skills each with a rank (04), a bonus/penalty modifier, and color dice.
// Total dice = attr rank + skill rank. Modifier = bonus (+) or penalty (-) dice.
// Color dice: type (white 4+, red 3+, black 2+) + count of colored dice in the pool.
const skillField = () => new fields.SchemaField({
rank: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0, max: 4 }),
modifier: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
colorDiceType: new fields.StringField({ required: true, nullable: false, initial: "white",
choices: { white: "OATHHAMMER.ColorDice.White", red: "OATHHAMMER.ColorDice.Red", black: "OATHHAMMER.ColorDice.Black" } }),
colorDice: new fields.NumberField({ ...requiredInteger, initial: 4, min: 0 }),
})
schema.skills = new fields.SchemaField({
academics: skillField(),
acrobatics: skillField(),
animalHandling:skillField(),
athletics: skillField(),
brewing: skillField(),
carpentry: skillField(),
defense: skillField(),
dexterity: skillField(),
diplomacy: skillField(),
discipline: skillField(),
fighting: skillField(),
folklore: skillField(),
fortune: skillField(),
heal: skillField(),
leadership: skillField(),
magic: skillField(),
masonry: skillField(),
orientation: skillField(),
perception: skillField(),
resilience: skillField(),
ride: skillField(),
shooting: skillField(),
smithing: skillField(),
stealth: skillField(),
survival: skillField(),
tracking: skillField(),
})
schema.grit = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 2, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 2, min: 0 })
})
// Luck.max is derived from fate.rank; resets at session start.
schema.luck = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 })
})
schema.arcaneStress = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
threshold: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
thresholdBonus: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.miracleBlocked = new fields.BooleanField({ required: true, initial: false })
schema.movement = new fields.SchemaField({
base: new fields.NumberField({ ...requiredInteger, initial: 30, min: 0 }),
adjusted: new fields.NumberField({ ...requiredInteger, initial: 30, min: 0 })
})
schema.defense = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 10, min: 0 }),
armorRating: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.experience = new fields.SchemaField({
current: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
total: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
level: new fields.NumberField({ ...requiredInteger, initial: 1, min: 1 })
})
schema.biodata = new fields.SchemaField({
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: "" }),
weight: new fields.StringField({ required: true, nullable: false, initial: "" }),
eyes: new fields.StringField({ required: true, nullable: false, initial: "" }),
hair: new fields.StringField({ required: true, nullable: false, initial: "" }),
alignment: new fields.StringField({ required: true, nullable: false, initial: "" })
})
schema.currency = new fields.SchemaField({
gold: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
silver: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
copper: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
return schema
}
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Character"]
prepareDerivedData() {
super.prepareDerivedData()
// Grit max = Resilience skill rank + Toughness attribute rank (rulebook p.5)
this.grit.max = this.skills.resilience.rank + this.attributes.toughness.rank
// Luck max = Fate rank; restores at session start
this.luck.max = this.attributes.fate.rank
// Defense score = 10 + Agility + Armor Rating + bonus
this.defense.value = 10 + this.attributes.agility.rank + this.defense.armorRating + this.defense.bonus
// Stress Threshold = Willpower rank + Magic rank + bonus (rulebook p.101)
this.arcaneStress.threshold = this.attributes.willpower.rank + this.skills.magic.rank + this.arcaneStress.thresholdBonus
}
}