Files
fvtt-oath-hammer/oath-hammer.mjs

203 lines
9.5 KiB
JavaScript
Raw Permalink 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.
import { SYSTEM, STATUS_EFFECTS } from "./module/config/system.mjs"
globalThis.SYSTEM = SYSTEM
import * as models from "./module/models/_module.mjs"
import * as documents from "./module/documents/_module.mjs"
import * as applications from "./module/applications/_module.mjs"
import OathHammerUtils from "./module/utils.mjs"
import OathHammerWeaponDialog from "./module/applications/weapon-dialog.mjs"
import OathHammerCombat from "./module/combat.mjs"
import { rollWeaponDamage } from "./module/rolls.mjs"
import { injectFreeRollBar } from "./module/applications/free-roll.mjs"
Hooks.once("init", function () {
console.info(SYSTEM.ASCII)
console.info("Oath Hammer | Initializing System")
globalThis.oathHammer = game.system
game.system.CONST = SYSTEM
game.system.api = { applications, models, documents }
CONFIG.Actor.documentClass = documents.OathHammerActor
CONFIG.Combat.documentClass = OathHammerCombat
CONFIG.Actor.dataModels = {
character: models.OathHammerCharacter,
npc: models.OathHammerNPC,
settlement: models.OathHammerSettlement,
regiment: models.OathHammerRegiment,
party: models.OathHammerParty,
army: models.OathHammerArmy,
}
CONFIG.Item.documentClass = documents.OathHammerItem
CONFIG.Item.dataModels = {
weapon: models.OathHammerWeapon,
armor: models.OathHammerArmor,
ammunition: models.OathHammerAmmunition,
equipment: models.OathHammerEquipment,
spell: models.OathHammerSpell,
miracle: models.OathHammerMiracle,
"magic-item": models.OathHammerMagicItem,
trait: models.OathHammerTrait,
oath: models.OathHammerOath,
"class": models.OathHammerClass,
building: models.OathHammerBuilding,
skillnpc: models.OathHammerSkillNPC,
npcattack: models.OathHammerNpcAttack,
}
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet)
foundry.documents.collections.Actors.registerSheet("fvtt-oath-hammer", applications.OathHammerCharacterSheet, {
types: ["character"],
makeDefault: true,
label: "OATHHAMMER.Sheet.Character"
})
foundry.documents.collections.Actors.registerSheet("fvtt-oath-hammer", applications.OathHammerNPCSheet, {
types: ["npc"],
makeDefault: true,
label: "OATHHAMMER.Sheet.NPC"
})
foundry.documents.collections.Actors.registerSheet("fvtt-oath-hammer", applications.OathHammerSettlementSheet, {
types: ["settlement"],
makeDefault: true,
label: "OATHHAMMER.Sheet.Settlement"
})
foundry.documents.collections.Actors.registerSheet("fvtt-oath-hammer", applications.OathHammerRegimentSheet, {
types: ["regiment"],
makeDefault: true,
label: "OATHHAMMER.Sheet.Regiment"
})
foundry.documents.collections.Actors.registerSheet("fvtt-oath-hammer", applications.OathHammerPartySheet, {
types: ["party"],
makeDefault: true,
label: "OATHHAMMER.Sheet.Party"
})
foundry.documents.collections.Actors.registerSheet("fvtt-oath-hammer", applications.OathHammerArmySheet, {
types: ["army"],
makeDefault: true,
label: "OATHHAMMER.Sheet.Army"
})
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet)
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerWeaponSheet, { types: ["weapon"], makeDefault: true, label: "OATHHAMMER.Sheet.Weapon" })
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerArmorSheet, { types: ["armor"], makeDefault: true, label: "OATHHAMMER.Sheet.Armor" })
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerAmmunitionSheet, { types: ["ammunition"], makeDefault: true, label: "OATHHAMMER.Sheet.Ammunition" })
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerEquipmentSheet, { types: ["equipment"], makeDefault: true, label: "OATHHAMMER.Sheet.Equipment" })
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerSpellSheet, { types: ["spell"], makeDefault: true, label: "OATHHAMMER.Sheet.Spell" })
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerMiracleSheet, { types: ["miracle"], makeDefault: true, label: "OATHHAMMER.Sheet.Miracle" })
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerMagicItemSheet, { types: ["magic-item"], makeDefault: true, label: "OATHHAMMER.Sheet.MagicItem" })
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerTraitSheet, { types: ["trait"], makeDefault: true, label: "OATHHAMMER.Sheet.Trait" })
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerOathSheet, { types: ["oath"], makeDefault: true, label: "OATHHAMMER.Sheet.Oath" })
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerClassSheet, { types: ["class"], makeDefault: true, label: "OATHHAMMER.Sheet.Class" })
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerBuildingSheet, { types: ["building"], makeDefault: true, label: "OATHHAMMER.Sheet.Building" })
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerSkillNPCSheet, { types: ["skillnpc"], makeDefault: true, label: "OATHHAMMER.Sheet.SkillNPC" })
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerNpcAttackSheet, { types: ["npcattack"], makeDefault: true, label: "OATHHAMMER.Sheet.NpcAttack" })
CONFIG.statusEffects = STATUS_EFFECTS
OathHammerUtils.registerHandlebarsHelpers()
// Pre-register Handlebars partials so {{> "path"}} works in item templates
foundry.applications.handlebars.loadTemplates([
"systems/fvtt-oath-hammer/templates/item/rune-zone.hbs",
])
console.info("Oath Hammer | System Initialized")
})
Hooks.once("ready", async function () {
console.info("Oath Hammer | System Ready")
// Migration: remove orphaned items with removed types (lineage → actor field, ability → trait, regiment → actor type)
const removedTypes = new Set(["lineage", "ability", "regiment"])
for (const actor of game.actors) {
const toDelete = []
// Catch items that failed validation and landed in invalidDocumentIds
for (const id of (actor.items.invalidDocumentIds ?? [])) {
const raw = actor.items.getInvalid(id)
if (raw && removedTypes.has(raw._source?.type)) toDelete.push(id)
}
// Also catch any that slipped through in _source (belt-and-suspenders)
for (const src of (actor._source.items ?? [])) {
if (removedTypes.has(src.type) && !toDelete.includes(src._id)) toDelete.push(src._id)
}
if (toDelete.length) {
console.info(`Oath Hammer | Migrating ${actor.name}: removing ${toDelete.length} obsolete item(s)`)
await actor.deleteEmbeddedDocuments("Item", toDelete)
}
}
// Purge invalid world items of removed types
for (const id of game.items.invalidDocumentIds) {
const item = game.items.getInvalid(id)
if (item && removedTypes.has(item._source?.type)) {
console.info(`Oath Hammer | Deleting world item: ${item._source.name} (${item._source.type})`)
await item.delete()
}
}
// Auto-import the welcome scene if GM and not already present
if (game.user.isGM) await _importWelcomeScene()
})
// Auto-link regiment (and army) actor tokens so they can be added to garrisons/armies
Hooks.on("preCreateActor", (actor, _data, _options, _userId) => {
if (actor.type === "regiment" || actor.type === "army") {
actor.updateSource({ "prototypeToken.actorLink": true })
}
})
// Handle "Roll Damage" button in weapon attack chat cards
Hooks.on("renderChatMessageHTML", (message, html) => {
const btn = html.querySelector("[data-action=\"rollWeaponDamage\"]")
if (!btn) return
btn.addEventListener("click", async () => {
const flagData = message.getFlag("fvtt-oath-hammer", "weaponAttack")
if (!flagData) return
const { actorUuid, weaponUuid, attackSuccesses } = flagData
const actor = await fromUuid(actorUuid)
const weapon = await fromUuid(weaponUuid)
if (!actor || !weapon) return ui.notifications.warn(game.i18n.localize("OATHHAMMER.Roll.NoActor"))
const opts = await OathHammerWeaponDialog.promptDamage(actor, weapon, attackSuccesses ?? 0)
if (opts) await rollWeaponDamage(actor, weapon, opts)
})
})
// Inject Free Roll bar into the chat sidebar
Hooks.on("renderChatLog", (_chatLog, html) => injectFreeRollBar(_chatLog, html))
// ============================================================
// WELCOME SCENE — auto-create on first world load (GM only)
// ============================================================
const WELCOME_SCENE_NAME = "Oath Hammer"
const WELCOME_SCENE_MAP = "systems/fvtt-oath-hammer/assets/images/oathhammer_map.webp"
/** Scene data for the welcome map (3600×5400 px, no grid — world map). */
function _welcomeSceneData() {
return {
name: WELCOME_SCENE_NAME,
background: { src: WELCOME_SCENE_MAP },
width: 3600,
height: 5400,
grid: { type: 0, size: 100 }, // gridless
padding: 0,
initial: { x: 1800, y: 2700, scale: 0.25 },
tokenVision: false,
flags: { "fvtt-oath-hammer": { welcomeScene: true } },
}
}
async function _importWelcomeScene() {
// Skip if the scene already exists in the world
if (game.scenes.find(s => s.name === WELCOME_SCENE_NAME)) return
console.info("Oath Hammer | Creating welcome scene…")
const scene = await Scene.create(_welcomeSceneData())
await scene.activate()
console.info("Oath Hammer | Welcome scene created and activated.")
}