Initial import

This commit is contained in:
2026-03-07 17:19:40 +01:00
commit 03bc0b7043
69 changed files with 3263 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
export { default as OathHammerCharacterSheet } from "./sheets/character-sheet.mjs"
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"
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

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

View File

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

View File

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

View File

@@ -0,0 +1,138 @@
const { HandlebarsApplicationMixin } = foundry.applications.api
export default class OathHammerActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
static SHEET_MODES = { EDIT: 0, PLAY: 1 }
constructor(options = {}) {
super(options)
this.#dragDrop = this.#createDragDropHandlers()
}
#dragDrop
/** @override */
static DEFAULT_OPTIONS = {
classes: ["oathhammer", "actor"],
position: {
width: 900,
height: "auto",
},
form: {
submitOnChange: true,
},
window: {
resizable: true,
},
dragDrop: [{ dragSelector: '[data-drag="true"], .rollable', dropSelector: null }],
actions: {
editImage: OathHammerActorSheet.#onEditImage,
toggleSheet: OathHammerActorSheet.#onToggleSheet,
edit: OathHammerActorSheet.#onItemEdit,
delete: OathHammerActorSheet.#onItemDelete,
},
}
_sheetMode = this.constructor.SHEET_MODES.PLAY
get isPlayMode() {
return this._sheetMode === this.constructor.SHEET_MODES.PLAY
}
get isEditMode() {
return this._sheetMode === this.constructor.SHEET_MODES.EDIT
}
/** @override */
async _prepareContext() {
const context = {
fields: this.document.schema.fields,
systemFields: this.document.system.schema.fields,
actor: this.document,
system: this.document.system,
source: this.document.toObject(),
isEditMode: this.isEditMode,
isPlayMode: this.isPlayMode,
isEditable: this.isEditable,
}
return context
}
/** @override */
_onRender(context, options) {
this.#dragDrop.forEach((d) => d.bind(this.element))
}
#createDragDropHandlers() {
return this.options.dragDrop.map((d) => {
d.permissions = {
dragstart: this._canDragStart.bind(this),
drop: this._canDragDrop.bind(this),
}
d.callbacks = {
dragstart: this._onDragStart.bind(this),
dragover: this._onDragOver.bind(this),
drop: this._onDrop.bind(this),
}
return new foundry.applications.ux.DragDrop.implementation(d)
})
}
async _onDrop(event) {}
_canDragStart(selector) {
return this.isEditable
}
_canDragDrop(selector) {
return this.isEditable && this.document.isOwner
}
_onDragStart(event) {
if ("link" in event.target.dataset) return
}
_onDragOver(event) {}
async _onDropItem(item) {
const itemData = item.toObject()
await this.document.createEmbeddedDocuments("Item", [itemData], { renderSheet: false })
}
static #onToggleSheet(event, target) {
const modes = this.constructor.SHEET_MODES
this._sheetMode = this.isEditMode ? modes.PLAY : modes.EDIT
this.render()
}
static async #onEditImage(event, target) {
const attr = target.dataset.edit
const current = foundry.utils.getProperty(this.document, attr)
const { img } = this.document.constructor.getDefaultArtwork?.(this.document.toObject()) ?? {}
const fp = new FilePicker({
current,
type: "image",
redirectToRoot: img ? [img] : [],
callback: (path) => {
this.document.update({ [attr]: path })
},
top: this.position.top + 40,
left: this.position.left + 10,
})
return fp.browse()
}
static async #onItemEdit(event, target) {
const id = target.getAttribute("data-item-id")
const uuid = target.getAttribute("data-item-uuid")
let item = await fromUuid(uuid)
if (!item) item = this.document.items.get(id)
if (!item) return
item.sheet.render(true)
}
static async #onItemDelete(event, target) {
const itemUuid = target.getAttribute("data-item-uuid")
const item = await fromUuid(itemUuid)
await item.deleteDialog()
}
}

View File

@@ -0,0 +1,119 @@
const { HandlebarsApplicationMixin } = foundry.applications.api
export default class OathHammerItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
static SHEET_MODES = { EDIT: 0, PLAY: 1 }
constructor(options = {}) {
super(options)
this.#dragDrop = this.#createDragDropHandlers()
}
#dragDrop
/** @override */
static DEFAULT_OPTIONS = {
classes: ["oathhammer", "item"],
position: {
width: 600,
height: "auto",
},
form: {
submitOnChange: true,
},
window: {
resizable: true,
},
dragDrop: [{ dragSelector: "[data-drag]", dropSelector: null }],
actions: {
toggleSheet: OathHammerItemSheet.#onToggleSheet,
editImage: OathHammerItemSheet.#onEditImage,
},
}
_sheetMode = this.constructor.SHEET_MODES.PLAY
get isPlayMode() {
return this._sheetMode === this.constructor.SHEET_MODES.PLAY
}
get isEditMode() {
return this._sheetMode === this.constructor.SHEET_MODES.EDIT
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.fields = this.document.schema.fields
context.systemFields = this.document.system.schema.fields
context.item = this.document
context.system = this.document.system
context.source = this.document.toObject()
context.isEditMode = this.isEditMode
context.isPlayMode = this.isPlayMode
context.isEditable = this.isEditable
if (this.document.system.description !== undefined) {
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description ?? "", { async: true })
}
return context
}
/** @override */
_onRender(context, options) {
super._onRender(context, options)
this.#dragDrop.forEach((d) => d.bind(this.element))
}
#createDragDropHandlers() {
return this.options.dragDrop.map((d) => {
d.permissions = {
dragstart: this._canDragStart.bind(this),
drop: this._canDragDrop.bind(this),
}
d.callbacks = {
dragstart: this._onDragStart.bind(this),
dragover: this._onDragOver.bind(this),
drop: this._onDrop.bind(this),
}
return new foundry.applications.ux.DragDrop.implementation(d)
})
}
_canDragStart(selector) {
return this.isEditable
}
_canDragDrop(selector) {
return this.isEditable && this.document.isOwner
}
_onDragStart(event) {
if ("link" in event.target.dataset) return
}
_onDragOver(event) {}
async _onDrop(event) {}
static #onToggleSheet(event, target) {
const modes = this.constructor.SHEET_MODES
this._sheetMode = this.isEditMode ? modes.PLAY : modes.EDIT
this.render()
}
static async #onEditImage(event, target) {
const attr = target.dataset.edit
const current = foundry.utils.getProperty(this.document, attr)
const { img } = this.document.constructor.getDefaultArtwork?.(this.document.toObject()) ?? {}
const fp = new FilePicker({
current,
type: "image",
redirectToRoot: img ? [img] : [],
callback: (path) => {
this.document.update({ [attr]: path })
},
top: this.position.top + 40,
left: this.position.left + 10,
})
return fp.browse()
}
}

View File

@@ -0,0 +1,136 @@
import OathHammerActorSheet from "./base-actor-sheet.mjs"
export default class OathHammerCharacterSheet extends OathHammerActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["character"],
position: {
width: 972,
height: 780,
},
window: {
contentClasses: ["character-content"],
},
actions: {
createWeapon: OathHammerCharacterSheet.#onCreateWeapon,
createSpell: OathHammerCharacterSheet.#onCreateSpell,
createMiracle: OathHammerCharacterSheet.#onCreateMiracle,
createEquipment: OathHammerCharacterSheet.#onCreateEquipment,
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-oath-hammer/templates/actor/character-sheet.hbs",
},
tabs: {
template: "templates/generic/tab-navigation.hbs",
},
identity: {
template: "systems/fvtt-oath-hammer/templates/actor/character-identity.hbs",
},
combat: {
template: "systems/fvtt-oath-hammer/templates/actor/character-combat.hbs",
},
magic: {
template: "systems/fvtt-oath-hammer/templates/actor/character-magic.hbs",
},
equipment: {
template: "systems/fvtt-oath-hammer/templates/actor/character-equipment.hbs",
},
notes: {
template: "systems/fvtt-oath-hammer/templates/actor/character-notes.hbs",
},
}
/** @override */
tabGroups = {
sheet: "identity",
}
#getTabs() {
const tabs = {
identity: { id: "identity", group: "sheet", icon: "fa-solid fa-person", label: "OATHHAMMER.Tab.Identity" },
combat: { id: "combat", group: "sheet", icon: "fa-solid fa-swords", label: "OATHHAMMER.Tab.Combat" },
magic: { id: "magic", group: "sheet", icon: "fa-solid fa-wand-magic-sparkles", label: "OATHHAMMER.Tab.Magic" },
equipment: { id: "equipment", group: "sheet", icon: "fa-solid fa-backpack", label: "OATHHAMMER.Tab.Equipment" },
notes: { id: "notes", group: "sheet", icon: "fa-solid fa-book", label: "OATHHAMMER.Tab.Notes" },
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
/** @override */
async _preparePartContext(partId, context) {
const doc = this.document
switch (partId) {
case "main":
break
case "identity":
context.tab = context.tabs.identity
context.abilities = doc.itemTypes.ability
context.oaths = doc.itemTypes.oath
break
case "combat":
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":
context.tab = context.tabs.magic
context.spells = doc.itemTypes.spell
context.miracles = doc.itemTypes.miracle
break
case "equipment":
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
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(doc.system.description, { async: true })
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(doc.system.notes, { async: true })
break
}
return context
}
async _onDrop(event) {
if (!this.isEditable || !this.isEditMode) return
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
if (data.type === "Item") {
const item = await fromUuid(data.uuid)
return this._onDropItem(item)
}
}
static #onCreateWeapon(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("OATHHAMMER.NewItem.Weapon"), type: "weapon" }])
}
static #onCreateSpell(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("OATHHAMMER.NewItem.Spell"), type: "spell" }])
}
static #onCreateMiracle(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("OATHHAMMER.NewItem.Miracle"), type: "miracle" }])
}
static #onCreateEquipment(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("OATHHAMMER.NewItem.Equipment"), type: "equipment" }])
}
}

View File

@@ -0,0 +1,27 @@
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

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

View File

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

View File

@@ -0,0 +1,28 @@
import OathHammerItemSheet from "./base-item-sheet.mjs"
export default class OathHammerMiracleSheet extends OathHammerItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["miracle"],
position: {
width: 620,
},
window: {
contentClasses: ["miracle-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-oath-hammer/templates/item/miracle-sheet.hbs",
},
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.enrichedEffect = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.effect, { async: true })
return context
}
}

View File

@@ -0,0 +1,83 @@
import OathHammerActorSheet from "./base-actor-sheet.mjs"
export default class OathHammerNPCSheet extends OathHammerActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["npc"],
position: {
width: 720,
height: "auto",
},
window: {
contentClasses: ["npc-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-oath-hammer/templates/actor/npc-sheet.hbs",
},
tabs: {
template: "templates/generic/tab-navigation.hbs",
},
combat: {
template: "systems/fvtt-oath-hammer/templates/actor/npc-combat.hbs",
},
notes: {
template: "systems/fvtt-oath-hammer/templates/actor/npc-notes.hbs",
},
}
/** @override */
tabGroups = {
sheet: "combat",
}
#getTabs() {
const tabs = {
combat: { id: "combat", group: "sheet", icon: "fa-solid fa-swords", label: "OATHHAMMER.Tab.Combat" },
notes: { id: "notes", group: "sheet", icon: "fa-solid fa-book", label: "OATHHAMMER.Tab.Notes" },
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
/** @override */
async _preparePartContext(partId, context) {
const doc = this.document
switch (partId) {
case "main":
break
case "combat":
context.tab = context.tabs.combat
context.weapons = doc.itemTypes.weapon
break
case "notes":
context.tab = context.tabs.notes
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(doc.system.description, { async: true })
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(doc.system.notes, { async: true })
break
}
return context
}
async _onDrop(event) {
if (!this.isEditable || !this.isEditMode) return
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
if (data.type === "Item") {
const item = await fromUuid(data.uuid)
return this._onDropItem(item)
}
}
}

View File

@@ -0,0 +1,29 @@
import OathHammerItemSheet from "./base-item-sheet.mjs"
export default class OathHammerOathSheet extends OathHammerItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["oath"],
position: {
width: 620,
},
window: {
contentClasses: ["oath-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-oath-hammer/templates/item/oath-sheet.hbs",
},
}
/** @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 })
return context
}
}

View File

@@ -0,0 +1,27 @@
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

@@ -0,0 +1,28 @@
import OathHammerItemSheet from "./base-item-sheet.mjs"
export default class OathHammerSpellSheet extends OathHammerItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["spell"],
position: {
width: 620,
},
window: {
contentClasses: ["spell-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-oath-hammer/templates/item/spell-sheet.hbs",
},
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.enrichedEffect = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.effect, { async: true })
return context
}
}

View File

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

194
module/config/system.mjs Normal file
View File

@@ -0,0 +1,194 @@
export const SYSTEM_ID = "fvtt-oath-hammer"
export const ATTRIBUTES = {
might: { id: "might", abbrev: "M", label: "OATHHAMMER.Attribute.Might" },
toughness: { id: "toughness", abbrev: "T", label: "OATHHAMMER.Attribute.Toughness" },
agility: { id: "agility", abbrev: "A", label: "OATHHAMMER.Attribute.Agility" },
willpower: { id: "willpower", abbrev: "WP", label: "OATHHAMMER.Attribute.Willpower" },
intelligence: { id: "intelligence", abbrev: "I", label: "OATHHAMMER.Attribute.Intelligence" },
fate: { id: "fate", abbrev: "F", label: "OATHHAMMER.Attribute.Fate" }
}
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" }
}
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" }
}
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" }
}
export const SORCEROUS_TRADITIONS = {
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" }
}
export const WEAPON_TYPE_CHOICES = {
melee: "OATHHAMMER.WeaponType.Melee",
ranged: "OATHHAMMER.WeaponType.Ranged"
}
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"
}
export const ATTRIBUTE_BONUS_CHOICES = {
might: "OATHHAMMER.Attribute.Might",
agility: "OATHHAMMER.Attribute.Agility",
none: "OATHHAMMER.Label.None"
}
export const RANGE_CHOICES = {
short: "OATHHAMMER.Range.Short",
medium: "OATHHAMMER.Range.Medium",
long: "OATHHAMMER.Range.Long"
}
export const HANDS_CHOICES = {
"one-handed": "OATHHAMMER.Hands.OneHanded",
"two-handed": "OATHHAMMER.Hands.TwoHanded"
}
export const CURRENCY_CHOICES = {
gp: "OATHHAMMER.Currency.GP",
sp: "OATHHAMMER.Currency.SP",
cp: "OATHHAMMER.Currency.CP"
}
export const ARMOR_TYPE_CHOICES = {
light: "OATHHAMMER.ArmorType.Light",
medium: "OATHHAMMER.ArmorType.Medium",
heavy: "OATHHAMMER.ArmorType.Heavy"
}
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"
}
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",
"healing-supply": "OATHHAMMER.EquipmentType.HealingSupply",
food: "OATHHAMMER.EquipmentType.Food",
mount: "OATHHAMMER.EquipmentType.Mount",
vehicle: "OATHHAMMER.EquipmentType.Vehicle",
"war-machine": "OATHHAMMER.EquipmentType.WarMachine"
}
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"
}
export const RARITY_CHOICES = {
common: "OATHHAMMER.Rarity.Common",
uncommon: "OATHHAMMER.Rarity.Uncommon",
rare: "OATHHAMMER.Rarity.Rare",
"very-rare": "OATHHAMMER.Rarity.VeryRare",
legendary: "OATHHAMMER.Rarity.Legendary"
}
export const ABILITY_TYPE_CHOICES = {
"class-ability": "OATHHAMMER.AbilityType.ClassAbility",
"lineage-trait": "OATHHAMMER.AbilityType.LineageTrait",
feat: "OATHHAMMER.AbilityType.Feat"
}
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"
}
export const ATTRIBUTE_RANK_CHOICES = { 1: "1", 2: "2", 3: "3", 4: "4" }
export const ASCII = `
·················································
: ___ _ _ _ _ :
: / _ \\ __ _| |_| |__ | | | | __ _ _ __ ___ :
: | | | / _\` | __| '_ \\ | |_| |/ _\` | '_ \` _ \\:
: | |_| | (_| | |_| | | | | _ | (_| | | | | | |
: \\___/ \\__,_|\\__|_| |_| |_| |_|\\__,_|_| |_| |_|
: :
·················································
`
export const SYSTEM = {
id: SYSTEM_ID,
ATTRIBUTES,
LINEAGE_CHOICES,
CLASS_CHOICES,
OATH_TYPES,
SORCEROUS_TRADITIONS,
WEAPON_TYPE_CHOICES,
DAMAGE_TYPE_CHOICES,
ATTRIBUTE_BONUS_CHOICES,
RANGE_CHOICES,
HANDS_CHOICES,
CURRENCY_CHOICES,
ARMOR_TYPE_CHOICES,
AMMO_TYPE_CHOICES,
EQUIPMENT_TYPE_CHOICES,
MAGIC_ITEM_TYPE_CHOICES,
RARITY_CHOICES,
ABILITY_TYPE_CHOICES,
CONDITION_TYPE_CHOICES,
ATTRIBUTE_RANK_CHOICES,
ASCII
}

View File

@@ -0,0 +1,2 @@
export { default as OathHammerActor } from "./actor.mjs"
export { default as OathHammerItem } from "./item.mjs"

View File

@@ -0,0 +1,35 @@
export default class OathHammerActor extends Actor {
async _preCreate(data, options, user) {
await super._preCreate(data, options, user)
const prototypeToken = {}
if (this.type === "character") {
Object.assign(prototypeToken, {
sight: { enabled: true },
actorLink: true,
disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY,
})
this.updateSource({ prototypeToken })
}
if (this.type === "npc") {
Object.assign(prototypeToken, {
sight: { enabled: false },
actorLink: false,
disposition: CONST.TOKEN_DISPOSITIONS.HOSTILE,
})
this.updateSource({ prototypeToken })
}
}
getArmorRating() {
let rating = 0
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 rating
}
}

22
module/documents/item.mjs Normal file
View File

@@ -0,0 +1,22 @@
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",
"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"
}
export default class OathHammerItem extends Item {
constructor(data, context) {
if (!data.img && defaultItemImg[data.type]) {
data.img = defaultItemImg[data.type]
}
super(data, context)
}
}

13
module/models/_module.mjs Normal file
View File

@@ -0,0 +1,13 @@
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"
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"

18
module/models/ability.mjs Normal file
View File

@@ -0,0 +1,18 @@
import { SYSTEM } from "../config/system.mjs"
export default class OathHammerAbility extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
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 })
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: "" })
return schema
}
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Ability"]
}

View File

@@ -0,0 +1,20 @@
import { SYSTEM } from "../config/system.mjs"
export default class OathHammerAmmunition 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.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 })
return schema
}
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Ammunition"]
}

22
module/models/armor.mjs Normal file
View File

@@ -0,0 +1,22 @@
import { SYSTEM } from "../config/system.mjs"
export default class OathHammerArmor 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.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 })
schema.currency = new fields.StringField({ required: true, initial: "gp", choices: SYSTEM.CURRENCY_CHOICES })
return schema
}
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Armor"]
}

View File

@@ -0,0 +1,83 @@
import { SYSTEM } from "../config/system.mjs"
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.description = new fields.HTMLField({ required: true, textSearch: true })
schema.notes = 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()
})
schema.grit = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 2, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 2, min: 0 })
})
schema.luck = new fields.SchemaField({
value: 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: 10, min: 0 })
})
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({
lineage: new fields.StringField({ required: true, initial: "dwarf", choices: SYSTEM.LINEAGE_CHOICES }),
class: new fields.StringField({ required: true, initial: "fighter", 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: "" }),
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()
this.grit.max = this.attributes.might.rank + this.attributes.toughness.rank
this.defense.value = 10 + this.attributes.agility.rank + this.defense.armorRating + this.defense.bonus
}
}

View File

@@ -0,0 +1,17 @@
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

@@ -0,0 +1,20 @@
import { SYSTEM } from "../config/system.mjs"
export default class OathHammerEquipment 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.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 })
schema.currency = new fields.StringField({ required: true, initial: "gp", choices: SYSTEM.CURRENCY_CHOICES })
return schema
}
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Equipment"]
}

View File

@@ -0,0 +1,26 @@
import { SYSTEM } from "../config/system.mjs"
export default class OathHammerMagicItem 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.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 })
})
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 })
return schema
}
static LOCALIZATION_PREFIXES = ["OATHHAMMER.MagicItem"]
}

26
module/models/miracle.mjs Normal file
View File

@@ -0,0 +1,26 @@
import { SYSTEM } from "../config/system.mjs"
export default class OathHammerMiracle extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const requiredInteger = { required: true, nullable: false, integer: true }
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()
})
schema.materialComponent = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.savingThrow = new fields.StringField({ required: true, nullable: false, initial: "" })
return schema
}
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Miracle"]
}

50
module/models/npc.mjs Normal file
View File

@@ -0,0 +1,50 @@
import { SYSTEM } from "../config/system.mjs"
export default class OathHammerNPC 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.notes = 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()
})
schema.grit = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 2, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 2, min: 0 })
})
schema.defense = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 10, min: 0 })
})
schema.movement = new fields.SchemaField({
base: new fields.NumberField({ ...requiredInteger, initial: 30, min: 0 })
})
schema.attackBonus = new fields.NumberField({ ...requiredInteger, initial: 0 })
schema.damageBonus = new fields.NumberField({ ...requiredInteger, initial: 0 })
schema.challengeRating = new fields.StringField({ required: true, nullable: false, initial: "1" })
return schema
}
static LOCALIZATION_PREFIXES = ["OATHHAMMER.NPC"]
prepareDerivedData() {
super.prepareDerivedData()
this.grit.max = this.attributes.might.rank + this.attributes.toughness.rank
}
}

17
module/models/oath.mjs Normal file
View File

@@ -0,0 +1,17 @@
import { SYSTEM } from "../config/system.mjs"
export default class OathHammerOath extends foundry.abstract.TypeDataModel {
static defineSchema() {
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.violated = new fields.BooleanField({ required: true, initial: false })
return schema
}
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Oath"]
}

20
module/models/shield.mjs Normal file
View File

@@ -0,0 +1,20 @@
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"]
}

29
module/models/spell.mjs Normal file
View File

@@ -0,0 +1,29 @@
import { SYSTEM } from "../config/system.mjs"
export default class OathHammerSpell extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const requiredInteger = { required: true, nullable: false, integer: true }
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()
})
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: "" })
return schema
}
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Spell"]
}

26
module/models/weapon.mjs Normal file
View File

@@ -0,0 +1,26 @@
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 })
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 })
return schema
}
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Weapon"]
}

19
module/utils.mjs Normal file
View File

@@ -0,0 +1,19 @@
export default class OathHammerUtils {
static registerHandlebarsHelpers() {
Handlebars.registerHelper("ifThen", (condition, trueVal, falseVal) => condition ? trueVal : falseVal)
Handlebars.registerHelper("capitalize", (str) => {
if (typeof str !== "string") return str
return str.charAt(0).toUpperCase() + str.slice(1)
})
Handlebars.registerHelper("concat", (...args) => {
args.pop() // remove handlebars options object
return args.join("")
})
}
static async loadCompendium(packId) {
const pack = game.packs.get(packId)
if (!pack) return []
return await pack.getDocuments()
}
}