fvtt-lethal-fantasy/lethal-fantasy.mjs

296 lines
9.6 KiB
JavaScript

/**
* Lethal Fantasy RPG System
* Author: LeRatierBretonnien/Uberwald
*/
import { SYSTEM } from "./module/config/system.mjs"
globalThis.SYSTEM = SYSTEM // Expose the SYSTEM object to the global scope
// Import modules
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 { handleSocketEvent } from "./module/socket.mjs"
import { configureDiceSoNice } from "./module/dice.mjs"
import { Macros } from "./module/macros.mjs"
import { initControlButtons } from "./module/control-buttons.mjs"
import { setupTextEnrichers } from "./module/enrichers.mjs"
Hooks.once("init", function () {
console.info("Lethal Fantasy RPG | Initializing System")
console.info(SYSTEM.ASCII)
globalThis.lethalFantasy = game.system
game.system.CONST = SYSTEM
// Expose the system API
game.system.api = {
applications,
models,
documents,
}
CONFIG.Actor.documentClass = documents.LethalFantasyActor
CONFIG.Actor.dataModels = {
character: models.LethalFantasyCharacter,
opponent: models.LethalFantasyOpponent,
}
CONFIG.Item.documentClass = documents.LethalFantasyItem
CONFIG.Item.dataModels = {
path: models.LethalFantasyPath,
talent: models.LethalFantasyTalent,
weapon: models.LethalFantasyWeapon,
armor: models.LethalFantasyArmor,
spell: models.LethalFantasySpell,
attack: models.LethalFantasyAttack,
}
// Register sheet application classes
Actors.unregisterSheet("core", ActorSheet)
Actors.registerSheet("lethalFantasy", applications.LethalFantasyCharacterSheet, { types: ["character"], makeDefault: true })
Actors.registerSheet("lethalFantasy", applications.LethalFantasyOpponentSheet, { types: ["opponent"], makeDefault: true })
Items.unregisterSheet("core", ItemSheet)
Items.registerSheet("lethalFantasy", applications.LethalFantasyTalentSheet, { types: ["talent"], makeDefault: true })
Items.registerSheet("lethalFantasy", applications.LethalFantasyPathSheet, { types: ["path"], makeDefault: true })
Items.registerSheet("lethalFantasy", applications.LethalFantasyWeaponSheet, { types: ["weapon"], makeDefault: true })
Items.registerSheet("lethalFantasy", applications.LethalFantasySpellSheet, { types: ["spell"], makeDefault: true })
Items.registerSheet("lethalFantasy", applications.LethalFantasyArmorSheet, { types: ["armor"], makeDefault: true })
Items.registerSheet("lethalFantasy", applications.LethalFantasyAttackSheet, { types: ["attack"], makeDefault: true })
// Other Document Configuration
CONFIG.ChatMessage.documentClass = documents.LethalFantasyChatMessage
// Dice system configuration
CONFIG.Dice.rolls.push(documents.LethalFantasyRoll)
// Register system settings
game.settings.register("lethalFantasy", "displayOpponentMalus", {
name: "TENEBRIS.Setting.displayOpponentMalus",
hint: "TENEBRIS.Setting.displayOpponentMalusHint",
scope: "world",
config: true,
type: Boolean,
default: true,
})
game.settings.register("lethalFantasy", "fortune", {
name: "TENEBRIS.Setting.fortune",
hint: "TENEBRIS.Setting.fortuneHint",
scope: "world",
config: true,
type: Number,
default: 0,
requiresReload: true,
})
game.settings.register("lethalFantasy", "worldKey", {
name: "Unique world key",
scope: "world",
config: false,
type: String,
default: "",
})
// Activate socket handler
game.socket.on(`system.${SYSTEM.id}`, handleSocketEvent)
// Pre-localize configuration objects
// TODO : encore d'actualité ?
// preLocalizeConfig()
initControlButtons()
setupTextEnrichers()
// Gestion des jets de dés depuis les journaux
document.addEventListener("click", (event) => {
const anchor = event.target.closest("a.ask-roll-journal")
if (!anchor) return
event.preventDefault()
event.stopPropagation()
const type = anchor.dataset.rollType
const target = anchor.dataset.rollTarget
const title = anchor.dataset.rollTitle
const avantage = anchor.dataset.rollAvantage
applications.LethalFantasyManager.askRollForAll(type, target, title, avantage)
})
console.info("CTHULHU TENEBRIS | System Initialized")
})
/**
* Perform one-time configuration of system configuration objects.
*/
function preLocalizeConfig() {
const localizeConfigObject = (obj, keys) => {
for (let o of Object.values(obj)) {
for (let k of keys) {
o[k] = game.i18n.localize(o[k])
}
}
}
// CONFIG.Dice.rollModes = Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
// localizeConfigObject(SYSTEM.ACTION.TAG_CATEGORIES, ["label"])
// localizeConfigObject(CONFIG.Dice.rollModes, ["label"])
}
Hooks.once("ready", function () {
console.info("CTHULHU TENEBRIS | Ready")
game.system.applicationFortune = new applications.LethalFantasyFortune()
game.system.applicationFortune.render(true)
game.system.applicationManager = new applications.LethalFantasyManager()
if (game.user.isGM) {
game.system.applicationManager.render(true)
}
if (!SYSTEM.DEV_MODE) {
registerWorldCount("lethalFantasy")
}
_showUserGuide()
/**
*
*/
async function _showUserGuide() {
if (game.user.isGM) {
const newVer = game.system.version
const userGuideJournalName = "Guide du système"
const userGuideCompendiumLabel = "userguide"
let currentVer = "0"
let oldUserGuide = game.journal.getName(userGuideJournalName)
if (oldUserGuide !== undefined && oldUserGuide !== null && oldUserGuide.getFlag("lethalFantasy", "UserGuideVersion") !== undefined) {
currentVer = oldUserGuide.getFlag("lethalFantasy", "UserGuideVersion")
}
if (newVer === currentVer) {
// Up to date
return
}
let newReleasePack = game.packs.find((p) => p.metadata.name === userGuideCompendiumLabel)
if (newReleasePack === null || newReleasePack === undefined) {
console.info("CTHULHU TENEBRIS | No compendium found for the system guide")
return
}
await newReleasePack.getIndex()
let newUserGuide = newReleasePack.index.find((j) => j.name === userGuideJournalName)
if (newUserGuide === undefined || newUserGuide === null) {
console.info("CTHULHU TENEBRIS | No system guide found in the compendium")
return
}
// Don't delete until we have new release Pack
if (oldUserGuide !== null && oldUserGuide !== undefined) {
await oldUserGuide.delete()
}
await game.journal.importFromCompendium(newReleasePack, newUserGuide._id)
let newReleaseJournal = game.journal.getName(newUserGuide.name)
await newReleaseJournal.setFlag("lethalFantasy", "UserGuideVersion", newVer)
// Show journal
await newReleaseJournal.sheet.render(true, { sheetMode: "text" })
}
}
})
Hooks.on("renderChatMessage", (message, html, data) => {
const typeMessage = data.message.flags.lethalFantasy?.typeMessage
// Message de fortune
if (typeMessage === "fortune") {
if (game.user.isGM && !data.message.flags.lethalFantasy?.accepted) {
html.find(".button").click((event) => applications.LethalFantasyFortune.acceptRequest(event, html, data))
} else {
html.find(".button").each((i, btn) => {
btn.style.display = "none"
})
if (game.user.isGM) {
html.find(".fortune-accepted").each((i, btn) => (btn.style.display = "flex"))
}
}
}
// Message de demande de jet de dés
else if (typeMessage === "askRoll") {
// Affichage des boutons de jet de dés uniquement pour les joueurs
if (game.user.isGM) {
html.find(".ask-roll-dice").each((i, btn) => {
btn.style.display = "none"
})
} else {
html.find(".ask-roll-dice").click((event) => {
const btn = $(event.currentTarget)
const type = btn.data("type")
const value = btn.data("value")
const avantage = btn.data("avantage") ?? "="
const character = game.user.character
if (type === SYSTEM.ROLL_TYPE.RESOURCE) character.rollResource(value)
else if (type === SYSTEM.ROLL_TYPE.SAVE) character.rollSave(value, avantage)
})
}
}
})
Hooks.on("updateSetting", async (setting, update, options, id) => {
if (setting.key === "lethalFantasy.fortune") {
game.system.applicationFortune.render(true)
}
})
// Dice-so-nice Ready
Hooks.once("diceSoNiceReady", (dice3d) => {
configureDiceSoNice(dice3d)
})
/**
* Create a macro when dropping an entity on the hotbar
* Item - open roll dialog
* Actor - open actor sheet
* Journal - open journal sheet
*/
Hooks.on("hotbarDrop", (bar, data, slot) => {
if (["Actor", "Item", "JournalEntry", "roll", "rollDamage", "rollAttack"].includes(data.type)) {
Macros.createLethalFantasyMacro(data, slot)
return false
}
})
/**
* Register world usage statistics
* @param {string} registerKey
*/
function registerWorldCount(registerKey) {
if (game.user.isGM) {
let worldKey = game.settings.get(registerKey, "worldKey")
if (worldKey === undefined || worldKey === "") {
worldKey = foundry.utils.randomID(32)
game.settings.set(registerKey, "worldKey", worldKey)
}
// Simple API counter
const worldData = {
register_key: registerKey,
world_key: worldKey,
foundry_version: `${game.release.generation}.${game.release.build}`,
system_name: game.system.id,
system_version: game.system.version,
}
let apiURL = "https://worlds.qawstats.info/worlds-counter"
$.ajax({
url: apiURL,
type: "POST",
data: JSON.stringify(worldData),
contentType: "application/json; charset=utf-8",
dataType: "json",
async: false,
})
}
}