289 lines
11 KiB
JavaScript
289 lines
11 KiB
JavaScript
import { ACTOR_TYPES, ITEM_TYPES } from "../../constants.js"
|
|
import { Grammar } from "../../grammar.js"
|
|
import { RdDItemArme } from "../../item/arme.js"
|
|
import { CATEGORIES_COMPETENCE_COMBAT } from "../../item/base-items.js"
|
|
import { Misc } from "../../misc.js"
|
|
import { RdDTimestamp } from "../../time/rdd-timestamp.js"
|
|
import { PDFDocument } from "./pdf-lib/pdf-lib.esm.js"
|
|
|
|
|
|
|
|
const copyProperty = (actor, path) => foundry.utils.getProperty(actor, path)
|
|
|
|
// const findItem = (actor, itemType, itemName) => actor.itemTypes[itemType].find(it => Grammar.equalsInsensitive(formCompName(it.name), formCompName(itemName)))
|
|
// const findItemPos = (actor, itemType, pos) => actor.itemTypes[itemType].length <= pos ? actor.itemTypes[itemType][pos] : length
|
|
// const findProperty = (it, path) => it ? foundry.utils.getProperty(it, path) : undefined
|
|
|
|
// const findItemProperty = (actor, itemType, itemName, path) => findProperty(findItem(actor, itemType, itemName), path)
|
|
// const findItemPosProperty = (actor, itemType, pos, path) => findProperty(findItemPos(actor, itemType, pos), path)
|
|
|
|
// const findArmeProperty = (actor, pos, path) => findProperty(findItemPos(actor, ITEM_TYPES.arme, pos), path)
|
|
// const findSortProperty = (actor, pos, path) => findProperty(findItemPos(actor, ITEM_TYPES.sort, pos), path)
|
|
|
|
// const itemFormPath = (type, pos, property) => `${type}s.${pos}.${property}`
|
|
|
|
|
|
const ACTOR_TO_FORM_MAPPING = [
|
|
{ path: 'name' },
|
|
{ path: 'system.carac.taille.value' },
|
|
{ path: 'system.carac.apparence.value' },
|
|
{ path: 'system.carac.apparence.xp' },
|
|
{ path: 'system.carac.constitution.value' },
|
|
{ path: 'system.carac.constitution.xp' },
|
|
{ path: 'system.carac.force.value' },
|
|
{ path: 'system.carac.force.value' },
|
|
{ path: 'system.carac.force.xp' },
|
|
{ path: 'system.carac.agilite.value' },
|
|
{ path: 'system.carac.agilite.xp' },
|
|
{ path: 'system.carac.dexterite.value' },
|
|
{ path: 'system.carac.dexterite.xp' },
|
|
{ path: 'system.carac.vue.value' },
|
|
{ path: 'system.carac.vue.xp' },
|
|
{ path: 'system.carac.ouie.value' },
|
|
{ path: 'system.carac.ouie.xp' },
|
|
{ path: 'system.carac.odoratgout.value' },
|
|
{ path: 'system.carac.odoratgout.xp' },
|
|
{ path: 'system.carac.volonte.value' },
|
|
{ path: 'system.carac.volonte.xp' },
|
|
{ path: 'system.carac.empathie.value' },
|
|
{ path: 'system.carac.empathie.xp' },
|
|
{ path: 'system.carac.intellect.value' },
|
|
{ path: 'system.carac.intellect.xp' },
|
|
{ path: 'system.carac.reve.value' },
|
|
{ path: 'system.carac.reve.xp' },
|
|
{ path: 'system.carac.chance.value' },
|
|
{ path: 'system.carac.chance.xp' },
|
|
|
|
{ path: 'system.age' },
|
|
{ path: 'system.sexe' },
|
|
{ path: 'system.taille' },
|
|
{ path: 'system.poids' },
|
|
{ path: 'system.cheveux' },
|
|
{ path: 'system.yeux' },
|
|
{ path: 'system.beaute' },
|
|
{ path: 'system.main' },
|
|
{ path: 'system.heure' },
|
|
{ path: 'computed.hn.heure', getter: actor => (RdDTimestamp.definition(actor.system.heure)?.heure ?? 0) + 1 },
|
|
{ path: 'computed.hn.label', getter: actor => RdDTimestamp.definition(actor.system.heure)?.avecArticle },
|
|
|
|
{ path: 'system.carac.melee.value' },
|
|
{ path: 'system.carac.tir.value' },
|
|
{ path: 'system.carac.lancer.value' },
|
|
{ path: 'system.carac.derobee.value' },
|
|
{ path: 'system.sante.vie.value' },
|
|
{ path: 'system.sante.endurance.value' },
|
|
|
|
{ path: 'system.attributs.sust.value' },
|
|
{ path: 'system.attributs.sconst.value' },
|
|
{ path: 'system.attributs.encombrement.value' },
|
|
{ path: 'system.attributs.plusdom.value', getter: actor => Misc.toSignedString(actor.system.attributs.plusdom.value) },
|
|
|
|
// , getter: actor => actor.get
|
|
]
|
|
|
|
export default class ExportPdf {
|
|
|
|
static init() {
|
|
Hooks.on("getActorContextOptions", (actorDirectory, menus) => { ExportPdf.onActorDirectoryMenu(actorDirectory, menus) })
|
|
}
|
|
|
|
static onActorDirectoryMenu(actorDirectory, menus) {
|
|
menus.push({
|
|
name: 'Export PDF',
|
|
icon: '<i class="fa-regular fa-file-pdf"></i>',
|
|
condition: target => actorDirectory.id == 'actors' && ExportPdf.$isActorPersonnage(this.$getActor(target)),
|
|
callback: async target => await ExportPdf.exportActor(target)
|
|
})
|
|
}
|
|
|
|
static $getActor(target) {
|
|
const entryId = $(target).closest(".directory-item")?.data("entryId")
|
|
return game.actors.get(entryId)
|
|
}
|
|
|
|
static $isActorPersonnage(actor) {
|
|
return actor?.type == ACTOR_TYPES.personnage
|
|
}
|
|
|
|
static async exportActor(target) {
|
|
const actor = ExportPdf.$getActor(target)
|
|
if (!ExportPdf.$isActorPersonnage(actor)) {
|
|
ui.notifications.error("Pas de personnage sélectionné")
|
|
return
|
|
}
|
|
const templatePdf = '/systems/foundryvtt-reve-de-dragon/assets/feuille-personnage.pdf';
|
|
|
|
const pdfBytes = await fetch(templatePdf).then(res => res.arrayBuffer())
|
|
const pdfDoc = await PDFDocument.load(pdfBytes)
|
|
|
|
const exporter = new ExportPdf(actor, pdfDoc)
|
|
exporter.generateFeuillePersonnage()
|
|
}
|
|
|
|
constructor(actor, pdfDoc) {
|
|
this.pdfDoc = pdfDoc
|
|
this.form = this.pdfDoc.getForm()
|
|
this.actor = actor
|
|
this.allComps = this.actor.itemTypes[ITEM_TYPES.competence]
|
|
this.comps = this.allComps
|
|
.filter(it => !it.isNiveauBase())
|
|
.sort(Misc.ascending(it => it.name))
|
|
|
|
this.compsNonArmes = this.comps.filter(it => !ExportPdf.isCompCombat(it))
|
|
this.compsArmes = this.comps.filter(it => ExportPdf.isCompCombat(it))
|
|
this.addedComps = new Set([])
|
|
}
|
|
|
|
static isCompCombat(comp) {
|
|
return CATEGORIES_COMPETENCE_COMBAT.includes(comp.system.categorie) && !Grammar.includesLowerCaseNoAccent(comp.name, "corps à corps") && !Grammar.includesLowerCaseNoAccent(comp.name, "esquive")
|
|
}
|
|
|
|
async generateFeuillePersonnage() {
|
|
|
|
this.$exportActorFields()
|
|
this.$exportCompetences()
|
|
this.$exportArchetype()
|
|
this.$exportArmes()
|
|
this.$exportSorts()
|
|
|
|
const pdfBytes = await this.pdfDoc.save();
|
|
const filename = `rdd-${this.actor.name.slugify()}.pdf`;
|
|
foundry.utils.saveDataToFile(pdfBytes, "application/pdf", filename);
|
|
}
|
|
|
|
$exportActorFields() {
|
|
ACTOR_TO_FORM_MAPPING.forEach(async (mapping) => {
|
|
const path = mapping.path
|
|
const value = mapping.getter ? mapping.getter(this.actor) : copyProperty(this.actor, path)
|
|
this.$setFormValue(path, value)
|
|
})
|
|
}
|
|
|
|
$exportCompetences() {
|
|
this.compsNonArmes
|
|
.filter(it => !this.addedComps.has(it.id))
|
|
.forEach(comp => {
|
|
const formCompName = Grammar.toLowerCaseNoAccent(comp.name.replaceAll(/(\s|-|\')/g, '_'))
|
|
this.$setFormCompetence(formCompName, comp)
|
|
})
|
|
const musique = this.compsNonArmes.filter(it => Grammar.includesLowerCaseNoAccent(it.name, 'musique'))
|
|
.sort(Misc.descending(it => it.system.niveau))
|
|
.filter(it => !this.addedComps.has(it.id))
|
|
.find(it => true)
|
|
if (musique) {
|
|
this.$setFormCompetence('musique', musique)
|
|
}
|
|
}
|
|
|
|
$exportArchetype() {
|
|
this.allComps.sort(Misc.ascending(it => it.system.niveau_archetype))
|
|
.forEach(comp => {
|
|
let formCompName = Grammar.toLowerCaseNoAccent(comp.name.replaceAll(/(\s|-|\')/g, '_'))
|
|
if (formCompName.includes('musique')) {
|
|
formCompName = 'musique'
|
|
}
|
|
this.$setFormValue(`competences.${formCompName}.niveau_archetype`, comp.system.niveau_archetype)
|
|
})
|
|
}
|
|
|
|
$setFormCompetenceArchetype(formCompName, comp, baseFormName = 'competences') {
|
|
}
|
|
|
|
$exportArmes() {
|
|
const uniques = new Set([])
|
|
const armes = this.actor.itemTypes[ITEM_TYPES.arme].map(arme => arme.getTypeAttaques()
|
|
.map(main => {
|
|
const compName = arme.getCompetenceAction(main)
|
|
const dommages = RdDItemArme.valeurMain(arme.system.dommages, main)
|
|
const forceRequise = RdDItemArme.valeurMain(arme.system.force ?? 0, main)
|
|
const comp = this.compsArmes.find(it => Grammar.equalsInsensitive(it.name, compName)) ?? this.actor.findItemLike(compName, ITEM_TYPES.competence)
|
|
const unique = [comp.id, arme.name, dommages, forceRequise].join('|');
|
|
if (uniques.has(unique)) {
|
|
return undefined
|
|
}
|
|
uniques.add(unique)
|
|
return { arme: arme, comp: comp, main: main }
|
|
}))
|
|
.reduce((a, b) => a.concat(b))
|
|
.filter(it => it != undefined && !it.comp.isNiveauBase())
|
|
.sort(Misc.descending(it => it.comp.niveau))
|
|
|
|
for (let pos = 0; pos < armes.length; pos++) {
|
|
const it = armes[pos]
|
|
this.$setFormArmeCompetence(pos, it.comp, it.arme, it.main, it.main)
|
|
}
|
|
|
|
// TODO: list comps without weapons
|
|
// TODO: list other comps not in the standard list -- use an instance of ExportPdf to hold state/built list
|
|
const otherComps = this.comps.filter(it => !this.addedComps.has(it.id))
|
|
for (let pos = 0; pos < otherComps.length; pos++) {
|
|
const comp = otherComps[pos]
|
|
this.$setFormCompetence(pos, comp, 'competences')
|
|
this.$setFormValue(`competences.${pos}.name`, comp.name)
|
|
}
|
|
}
|
|
|
|
$setFormCompetence(formCompName, comp, baseFormName = 'competences') {
|
|
|
|
if (this.form.getFieldMaybe(`${baseFormName}.${formCompName}.niveau`)) {
|
|
this.addedComps.add(comp.id)
|
|
}
|
|
if (comp.system.niveau != comp.system.base) {
|
|
this.$setFormValue(`${baseFormName}.${formCompName}.niveau`, comp.system.niveau)
|
|
}
|
|
if (comp.system.xp > 0) {
|
|
this.$setFormValue(`${baseFormName}.${formCompName}.xp`, comp.system.xp)
|
|
}
|
|
if (comp.system.xp_sort > 0) {
|
|
this.$setFormValue(`${baseFormName}.${formCompName}.xp_sort`, comp.system.xp_sort)
|
|
}
|
|
if (CATEGORIES_COMPETENCE_COMBAT.includes(comp.system.categorie)) {
|
|
this.$setFormValue(`${baseFormName}.${formCompName}.init`, comp.getBaseInit())
|
|
}
|
|
}
|
|
|
|
$setFormArmeCompetence(pos, comp, arme, main) {
|
|
this.$setFormCompetence(pos, comp, 'armes')
|
|
this.$setFormValue(`armes.${pos}.name`, arme.name)
|
|
this.$setFormValue(`armes.${pos}.main`, main)
|
|
this.$setFormValue(`armes.${pos}.plusdom`, RdDItemArme.valeurMain(arme.system.dommages, main))
|
|
}
|
|
|
|
$exportSorts() {
|
|
const sorts = this.actor.itemTypes[ITEM_TYPES.sort].sort(Misc.ascending(s => ExportPdf.$orderDraconic(s) + s.name))
|
|
|
|
for (let pos = 0; pos < sorts.length; pos++) {
|
|
const sort = sorts[pos]
|
|
this.$setFormSort(pos, sort)
|
|
}
|
|
}
|
|
|
|
$setFormSort(pos, sort) {
|
|
this.$setFormValue(`sorts.${pos}.name`, sort.name)
|
|
this.$setFormValue(`sorts.${pos}.voie`, sort.system.draconic)
|
|
this.$setFormValue(`sorts.${pos}.tmr`, sort.system.caseTMRSpeciale ?? sort.system.caseTMR)
|
|
this.$setFormValue(`sorts.${pos}.diff`, sort.system.difficulte)
|
|
this.$setFormValue(`sorts.${pos}.reve`, sort.system.ptreve)
|
|
this.$setFormValue(`sorts.${pos}.bonuscase`, sort.system.bonuscase)
|
|
}
|
|
|
|
static $orderDraconic(s) {
|
|
switch (s.system.draconic.substring(0, 1)) {
|
|
case 'O': return 1
|
|
case 'H': return 2
|
|
case 'N': return 3
|
|
case 'T': return 4
|
|
}
|
|
return 5
|
|
}
|
|
|
|
$setFormValue(path, value) {
|
|
const hasField = this.form.getFieldMaybe(path)
|
|
if (hasField && value != undefined) {
|
|
const field = this.form.getTextField(path)
|
|
field.setText(value.toString())
|
|
}
|
|
}
|
|
}
|
|
|