diff --git a/changelog.md b/changelog.md index 9256a26f..66c097cf 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,6 @@ # 12.0 ## 12.0.11 - Le scriptorium d'Astrobazzarh +- ajout d'un bouton pour générer les éléments de description d'un personnage - ajout du logo en background dans la liste des systèmes Foundry - ajout d'un champ pour le métier - export scriptarium diff --git a/module/actor-sheet.js b/module/actor-sheet.js index 4ff63dd7..cba5cf81 100644 --- a/module/actor-sheet.js +++ b/module/actor-sheet.js @@ -17,6 +17,7 @@ import { RdDItemBlessure } from "./item/blessure.js"; import { RdDEmpoignade } from "./rdd-empoignade.js"; import { RdDBaseActorSangSheet } from "./actor/base-actor-sang-sheet.js"; import { RdDCoeur } from "./coeur/rdd-coeur.js"; +import { AppPersonnageAleatoire } from "./actor/random/app-personnage-aleatoire.js"; /* -------------------------------------------- */ /** @@ -198,6 +199,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet { this.html.find('.jeu-label a').click(async event => this.actor.rollJeu(RdDSheetUtility.getItemId(event))) this.html.find('.recettecuisine-label a').click(async event => this.actor.rollRecetteCuisine(RdDSheetUtility.getItemId(event))) + this.html.find('.description-aleatoire').click(async event => new AppPersonnageAleatoire(this.actor).render(true)) if (game.user.isGM) { // experience log this.html.find('.experiencelog-delete').click(async event => { diff --git a/module/actor.js b/module/actor.js index 50666844..ee0569d2 100644 --- a/module/actor.js +++ b/module/actor.js @@ -3067,7 +3067,7 @@ export class RdDActor extends RdDBaseActorSang { incarnation.name = 'Réincarnation de ' + incarnation.name incarnation.system = { carac: foundry.utils.duplicate(this.system.carac), - heure: RdDTimestamp.defHeure(await RdDDice.rollTotal("1dh", { rollMode: "selfroll", showDice: SHOW_DICE })).key, + heure: RdDTimestamp.defHeure(await RdDDice.rollHeure( { rollMode: "selfroll", showDice: SHOW_DICE })).key, age: 18, biographie: '', notes: '', diff --git a/module/actor/random/app-personnage-aleatoire.js b/module/actor/random/app-personnage-aleatoire.js new file mode 100644 index 00000000..79d01576 --- /dev/null +++ b/module/actor/random/app-personnage-aleatoire.js @@ -0,0 +1,183 @@ +import { SHOW_DICE } from "../../constants.js"; +import { Misc } from "../../misc.js"; +import { RdDCarac } from "../../rdd-carac.js"; +import { RdDDice } from "../../rdd-dice.js"; +import { RdDNameGen } from "../../rdd-namegen.js"; +import { RdDTimestamp } from "../../time/rdd-timestamp.js"; + +const PATHS = [ + 'name', + 'system.sexe', + 'system.age', + 'system.taille', + 'system.poids', + 'system.main', + 'system.heure', + 'system.cheveux', + 'system.yeux' +] + +const RANDOM_VALUES = { + 'system.sexe': { 'masculin': 1, 'féminin': 1 }, + 'system.main': { 'droitier': 51, 'gaucher': 15, 'ambidectre': 6 }, + 'system.cheveux': { 'noirs': 2, 'bruns': 5, 'châtains clair': 5, 'blonds': 4, 'blonds très clair': 1, 'roux carotte': 1, 'roux cuivré': 3 }, + 'system.yeux': { 'noirs': 2, 'noisettes': 3, 'bruns vert': 4, 'verts': 3, 'bleus clair': 3, 'bleus gris': 2, 'gris': 1, 'mauves': 1, 'indigos': 1 }, +} + +export class AppPersonnageAleatoire extends FormApplication { + + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + template: "systems/foundryvtt-reve-de-dragon/templates/actor/random/app-personnage-aleatoire.hbs", + title: "Génération aléatoire", + width: 'fit-content', + height: 'fit-content', + classes: ['app-personnage-aleatoire'], + popOut: true, + resizable: true + }, { inplace: false }) + } + + constructor(actor) { + super({}) + this.actor = actor + this.current = foundry.utils.duplicate(actor) + this.checked = { + 'name': false, + 'system.sexe': true, + 'system.age': true, + 'system.taille': true, + 'system.poids': true, + 'system.main': true, + 'system.heure': true, + 'system.cheveux': true, + 'system.yeux': true + } + } + + async getData(options) { + return foundry.utils.mergeObject(await super.getData(options), { + actor: this.actor, + current: this.current, + checked: this.checked, + options: { isGM: game.user.isGM } + }) + } + + activateListeners(html) { + super.activateListeners(html) + this.html = html + this.html.find("button.button-cancel").click(async event => await this.close()) + this.html.find("button.button-apply").click(async event => await this.onApply()) + this.html.find("input.current-value").change(async event => await this.onChange(event)) + this.html.find("div.random-field[data-path='system.heure'] select.current-value").change(async event => await this.onChange(event)) + this.html.find("a.random").click(async event => await this.onRandom(event)) + this.html.find("a.reset").click(async event => await this.onReset(event)) + this.html.find("a.randomize-selected").click(async event => await this.onRandomizeSelected()) + this.html.find("input.check-for-random").click(async event => await this.onCheckForRandom(event)) + } + async _updateObject(event, formData) { } + + async onApply() { + const updates = Object.fromEntries( + PATHS.filter(path => game.user.isGM || path != 'name') + .map(path => [path, this.current[path]]) + ) + await this.actor.update(updates) + await this.close() + } + + getPath(selector) { + const fields = this.html.find(selector).parents("div.random-field:first") + return fields[0].attributes['data-path'].value + } + + async onChange(event) { + const path = this.getPath(event.currentTarget) + this.current[path] = event.currentTarget.value + } + + async onRandom(event) { + const path = this.getPath(event.currentTarget) + await this.setRandom(path); + this.render() + } + + async onReset(event) { + const path = this.getPath(event.currentTarget) + this.current[path] = this.actor[path] + await this.render() + } + + async onCheckForRandom(event) { + const path = this.getPath(event.currentTarget) + this.checked[path] = event.currentTarget.checked + this.render() + } + + async onRandomizeSelected() { + const paths = this.html.find("input.check-for-random:checked") + .parents("div.random-field") + .toArray() + .map(it => it.attributes['data-path'].value) + await Promise.all(paths.map(path => this.setRandom(path))) + this.render() + } + + async setRandom(path) { + this.current[path] = await this.random(path); + } + + async random(path) { + switch (path) { + case 'name': + return await RdDNameGen.generate() + case 'system.sexe': + case 'system.main': + case 'system.cheveux': + case 'system.yeux': + return await this.randomFromMap(RANDOM_VALUES[path]) + case 'system.poids': + return await this.randomPoids() + case 'system.taille': + return await this.randomTaille() + case 'system.age': + return await RdDDice.rollTotal('(2d4kl)*10 + 1d7xo + 2d20kl') + case 'system.heure': + return RdDTimestamp.defHeure(await RdDDice.rollHeure({ rollMode: "selfroll", showDice: SHOW_DICE })).key + } + return 'unknown' + } + + async randomFromMap(valuesMap) { + const max = Object.values(valuesMap).reduce(Misc.sum(), 0) + const total = await RdDDice.rollTotal(`1d${max}`) + let sum = 0 + for (let entry of Object.entries(valuesMap)) { + sum = sum + entry[1] + if (sum >= total) { + return entry[0] + } + } + return Object.keys(valuesMap)[0] + } + + async randomPoids() { + const caracTaille = RdDCarac.getCaracDerivee(this.current.system.carac.taille.value) + const range = caracTaille.poidsMax - caracTaille.poidsMin + 1 + const total = await RdDDice.rollTotal(`1d${range} + ${caracTaille.poidsMin}`) + return total + ' kg' + } + + async randomTaille() { + const caracTaille = RdDCarac.getCaracDerivee(this.current.system.carac.taille.value) + const base = this.current.system.carac.taille.value * 2 + 60 + caracTaille.poidsMin + const variation = Math.floor((caracTaille.poidsMax - caracTaille.poidsMin + base / 5) / 2) + const total = await RdDDice.rollTotal(`2d${variation} + ${base}`) + const cm = total % 100 + const m = (total - cm) / 100 + return `${m}m${cm}` + } + + +} diff --git a/module/rdd-carac.js b/module/rdd-carac.js index 1defe187..8668d9f0 100644 --- a/module/rdd-carac.js +++ b/module/rdd-carac.js @@ -3,38 +3,38 @@ import { Misc } from "./misc.js"; const TABLE_CARACTERISTIQUES_DERIVEES = { // xp: coût pour passer du niveau inférieur à ce niveau - 1: { xp: 3, poids: "moins de 1kg", plusdom: -5, sconst: 0.5, sust: 0.1 }, - 2: { xp: 3, poids: "1-5", plusdom: -4, sconst: 0.5, sust: 0.3 }, - 3: { xp: 4, poids: "6-10", plusdom: -3, sconst: 1, sust: 0.5, beaute: 'hideux' }, - 4: { xp: 4, poids: "11-20", plusdom: -3, sconst: 1, sust: 1, beaute: 'repoussant' }, - 5: { xp: 5, poids: "21-30", plusdom: -2, sconst: 1, sust: 1, beaute: 'franchement très laid' }, - 6: { xp: 5, poids: "31-40", plusdom: -1, sconst: 2, sust: 2, beaute: 'laid' }, - 7: { xp: 6, poids: "41-50", plusdom: -1, sconst: 2, sust: 2, beaute: 'très désavantagé' }, - 8: { xp: 6, poids: "51-60", plusdom: 0, sconst: 2, sust: 2, beaute: 'désavantagé' }, - 9: { xp: 7, poids: "61-65", plusdom: 0, sconst: 3, sust: 2, beaute: 'pas terrible' }, - 10: { xp: 7, poids: "66-70", plusdom: 0, sconst: 3, sust: 3, beaute: 'commun' }, - 11: { xp: 8, poids: "71-75", plusdom: 0, sconst: 3, sust: 3, beaute: 'pas mal' }, - 12: { xp: 8, poids: "76-80", plusdom: +1, sconst: 4, sust: 3, beaute: 'avantagé' }, - 13: { xp: 9, poids: "81-90", plusdom: +1, sconst: 4, sust: 3, beaute: 'mignon' }, - 14: { xp: 9, poids: "91-100", plusdom: +2, sconst: 4, sust: 4, beaute: 'beau' }, - 15: { xp: 10, poids: "101-110", plusdom: +2, sconst: 5, sust: 4, beaute: 'très beau' }, - 16: { xp: 20, poids: "111-120", plusdom: +3, sconst: 5, sust: 4, beaute: 'éblouissant' }, - 17: { xp: 30, poids: "121-131", plusdom: +3, sconst: 5, sust: 5 }, - 18: { xp: 40, poids: "131-141", plusdom: +4, sconst: 6, sust: 5 }, - 19: { xp: 50, poids: "141-150", plusdom: +4, sconst: 6, sust: 5 }, - 20: { xp: 60, poids: "151-160", plusdom: +4, sconst: 6, sust: 6 }, - 21: { xp: 70, poids: "161-180", plusdom: +5, sconst: 7, sust: 6 }, - 22: { xp: 80, poids: "181-200", plusdom: +5, sconst: 7, sust: 7 }, - 23: { xp: 90, poids: "201-300", plusdom: +6, sconst: 7, sust: 8 }, - 24: { xp: 100, poids: "301-400", plusdom: +6, sconst: 8, sust: 9 }, - 25: { xp: 110, poids: "401-500", plusdom: +7, sconst: 8, sust: 10 }, - 26: { xp: 120, poids: "501-600", plusdom: +7, sconst: 8, sust: 11 }, - 27: { xp: 130, poids: "601-700", plusdom: +8, sconst: 9, sust: 12 }, - 28: { xp: 140, poids: "701-800", plusdom: +8, sconst: 9, sust: 13 }, - 29: { xp: 150, poids: "801-900", plusdom: +9, sconst: 9, sust: 14 }, - 30: { xp: 160, poids: "901-1000", plusdom: +9, sconst: 10, sust: 15 }, - 31: { xp: 170, poids: "1001-1500", plusdom: +10, sconst: 10, sust: 16 }, - 32: { xp: 180, poids: "1501-2000", plusdom: +11, sconst: 10, sust: 17 } + 1: { xp: 3, poids: "moins de 1kg", poidsMin: 0, poidsMax: 1, plusdom: -5, sconst: 0.5, sust: 0.1 }, + 2: { xp: 3, poids: "1-5", poidsMin: 1, poidsMax: 5, plusdom: -4, sconst: 0.5, sust: 0.3 }, + 3: { xp: 4, poids: "6-10", poidsMin: 6, poidsMax: 10, plusdom: -3, sconst: 1, sust: 0.5, beaute: 'hideux' }, + 4: { xp: 4, poids: "11-20", poidsMin: 11, poidsMax: 20, plusdom: -3, sconst: 1, sust: 1, beaute: 'repoussant' }, + 5: { xp: 5, poids: "21-30", poidsMin: 21, poidsMax: 30, plusdom: -2, sconst: 1, sust: 1, beaute: 'franchement très laid' }, + 6: { xp: 5, poids: "31-40", poidsMin: 31, poidsMax: 40, plusdom: -1, sconst: 2, sust: 2, beaute: 'laid' }, + 7: { xp: 6, poids: "41-50", poidsMin: 41, poidsMax: 50, plusdom: -1, sconst: 2, sust: 2, beaute: 'très désavantagé' }, + 8: { xp: 6, poids: "51-60", poidsMin: 51, poidsMax: 60, plusdom: 0, sconst: 2, sust: 2, beaute: 'désavantagé' }, + 9: { xp: 7, poids: "61-65", poidsMin: 61, poidsMax: 65, plusdom: 0, sconst: 3, sust: 2, beaute: 'pas terrible' }, + 10: { xp: 7, poids: "66-70", poidsMin: 66, poidsMax: 70, plusdom: 0, sconst: 3, sust: 3, beaute: 'commun' }, + 11: { xp: 8, poids: "71-75", poidsMin: 71, poidsMax: 75, plusdom: 0, sconst: 3, sust: 3, beaute: 'pas mal' }, + 12: { xp: 8, poids: "76-80", poidsMin: 76, poidsMax: 80, plusdom: +1, sconst: 4, sust: 3, beaute: 'avantagé' }, + 13: { xp: 9, poids: "81-90", poidsMin: 81, poidsMax: 90, plusdom: +1, sconst: 4, sust: 3, beaute: 'mignon' }, + 14: { xp: 9, poids: "91-100", poidsMin: 91, poidsMax: 100, plusdom: +2, sconst: 4, sust: 4, beaute: 'beau' }, + 15: { xp: 10, poids: "101-110", poidsMin: 101, poidsMax: 110, plusdom: +2, sconst: 5, sust: 4, beaute: 'très beau' }, + 16: { xp: 20, poids: "111-120", poidsMin: 111, poidsMax: 120, plusdom: +3, sconst: 5, sust: 4, beaute: 'éblouissant' }, + 17: { xp: 30, poids: "121-131", poidsMin: 121, poidsMax: 131, plusdom: +3, sconst: 5, sust: 5 }, + 18: { xp: 40, poids: "131-141", poidsMin: 131, poidsMax: 141, plusdom: +4, sconst: 6, sust: 5 }, + 19: { xp: 50, poids: "141-150", poidsMin: 141, poidsMax: 150, plusdom: +4, sconst: 6, sust: 5 }, + 20: { xp: 60, poids: "151-160", poidsMin: 151, poidsMax: 160, plusdom: +4, sconst: 6, sust: 6 }, + 21: { xp: 70, poids: "161-180", poidsMin: 161, poidsMax: 180, plusdom: +5, sconst: 7, sust: 6 }, + 22: { xp: 80, poids: "181-200", poidsMin: 181, poidsMax: 200, plusdom: +5, sconst: 7, sust: 7 }, + 23: { xp: 90, poids: "201-300", poidsMin: 201, poidsMax: 300, plusdom: +6, sconst: 7, sust: 8 }, + 24: { xp: 100, poids: "301-400", poidsMin: 301, poidsMax: 400, plusdom: +6, sconst: 8, sust: 9 }, + 25: { xp: 110, poids: "401-500", poidsMin: 401, poidsMax: 500, plusdom: +7, sconst: 8, sust: 10 }, + 26: { xp: 120, poids: "501-600", poidsMin: 501, poidsMax: 600, plusdom: +7, sconst: 8, sust: 11 }, + 27: { xp: 130, poids: "601-700", poidsMin: 601, poidsMax: 700, plusdom: +8, sconst: 9, sust: 12 }, + 28: { xp: 140, poids: "701-800", poidsMin: 701, poidsMax: 800, plusdom: +8, sconst: 9, sust: 13 }, + 29: { xp: 150, poids: "801-900", poidsMin: 801, poidsMax: 900, plusdom: +9, sconst: 9, sust: 14 }, + 30: { xp: 160, poids: "901-1000", poidsMin: 901, poidsMax: 1000, plusdom: +9, sconst: 10, sust: 15 }, + 31: { xp: 170, poids: "1001-1500", poidsMin: 1001, poidsMax: 1500, plusdom: +10, sconst: 10, sust: 16 }, + 32: { xp: 180, poids: "1501-2000", poidsMin: 1501, poidsMax: 2000, plusdom: +11, sconst: 10, sust: 17 } }; export class RdDCarac { diff --git a/module/rdd-commands.js b/module/rdd-commands.js index 88b08dc3..03b0ba8e 100644 --- a/module/rdd-commands.js +++ b/module/rdd-commands.js @@ -81,7 +81,7 @@ export class RdDCommands { this.registerCommand({ path: ["/sommeil"], func: (content, msg, params) => this.sommeil(msg, params), descr: "Prépare le passage de journée pour chateau dormant" }); this.registerCommand({ path: ["/meteo"], func: (content, msg, params) => this.getMeteo(msg, params), descr: "Propose une météo marine" }); - this.registerCommand({ path: ["/nom"], func: (content, msg, params) => RdDNameGen.getName(msg, params), descr: "Génère un nom aléatoire" }); + this.registerCommand({ path: ["/nom"], func: (content, msg, params) => RdDNameGen.proposeName(msg, params), descr: "Génère un nom aléatoire" }); this.registerCommand({ path: ["/tmr"], func: (content, msg, params) => this.findTMR(msg, params), diff --git a/module/rdd-dice.js b/module/rdd-dice.js index 2f9dc10f..cdc5c74f 100644 --- a/module/rdd-dice.js +++ b/module/rdd-dice.js @@ -132,6 +132,10 @@ export class RdDDice { } } + static async rollHeure(options = { showDice: HIDE_DICE }) { + return await RdDDice.rollTotal("1dh", options) - 1 + } + static async rollTotal(formula, options = { showDice: HIDE_DICE }) { return (await RdDDice.roll(formula, options)).total; } diff --git a/module/rdd-namegen.js b/module/rdd-namegen.js index b27ea5bc..80a47450 100644 --- a/module/rdd-namegen.js +++ b/module/rdd-namegen.js @@ -11,13 +11,17 @@ const words = ['pore', 'pre', 'flor', 'lane', 'turlu', 'pin', 'a', 'alph', 'i', /* -------------------------------------------- */ export class RdDNameGen { - static async getName(msg, params) { + static async proposeName(msg, params) { const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-command-nom.html`, { - nom: Misc.upperFirst(await RdDDice.rollOneOf(words) + await RdDDice.rollOneOf(words)) + nom: await RdDNameGen.generate() }); ChatMessage.create({ content: html, whisper: ChatMessage.getWhisperRecipients("GM") }); } + static async generate() { + return Misc.upperFirst(await RdDDice.rollOneOf(words) + await RdDDice.rollOneOf(words)); + } + static async onCreerActeur(event) { const button = event.currentTarget; await RdDBaseActor.create({ diff --git a/module/rdd-utility.js b/module/rdd-utility.js index 661d9cf8..086c0fff 100644 --- a/module/rdd-utility.js +++ b/module/rdd-utility.js @@ -169,6 +169,7 @@ export class RdDUtility { 'systems/foundryvtt-reve-de-dragon/templates/actor/liens-vehicules.hbs', 'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire.html', 'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire-item.html', + 'systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs', //Items 'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete-script.hbs', 'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete.hbs', diff --git a/module/time/rdd-calendrier.js b/module/time/rdd-calendrier.js index a56df1ce..b5b41088 100644 --- a/module/time/rdd-calendrier.js +++ b/module/time/rdd-calendrier.js @@ -227,7 +227,7 @@ export class RdDCalendrier extends Application { /* -------------------------------------------- */ async ajouterNombreAstral(indexDate) { - const nombreAstral = await RdDDice.rollTotal("1dh", { showDice: HIDE_DICE, rollMode: "selfroll" }); + const nombreAstral = await RdDDice.rollHeure( { showDice: HIDE_DICE, rollMode: "selfroll" }); return { nombreAstral: nombreAstral, lectures: [], diff --git a/templates/actor-sheet.html b/templates/actor-sheet.html index 39dddd59..58bf0955 100644 --- a/templates/actor-sheet.html +++ b/templates/actor-sheet.html @@ -205,6 +205,7 @@ + Description diff --git a/templates/actor/random/app-personnage-aleatoire.hbs b/templates/actor/random/app-personnage-aleatoire.hbs new file mode 100644 index 00000000..4aa71b0b --- /dev/null +++ b/templates/actor/random/app-personnage-aleatoire.hbs @@ -0,0 +1,53 @@ +
+

Génération aléatoire pour {{actor.name}}

+
+ {{#if options.isGM}} + {{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs" + label="Nom" path="name" type="text" value=current.name checked=checked.name + }} + {{/if}} + {{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs" + label="Sexe" path="system.sexe" type="text" + value=current.system.sexe checked=checked.system.sexe + }} + {{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs" + label="Age" path="system.age" type="entier" min=10 max=100 + value=current.system.age checked=checked.system.age + }} + {{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs" + label="Taille" path="system.taille" type="text" + value=current.system.taille checked=checked.system.taille + }} + {{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs" + label="Poids" path="system.poids" type="text" + value=current.system.poids checked=checked.system.poids + }} + {{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs" + label="Main directrice" path="system.main" type="text" + value=current.system.main checked=checked.system.main + }} + {{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs" + label="Heure de naissance" path="system.heure" type="heure" + value=current.system.heure checked=checked.system.heure + }} + {{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs" + label="Cheveux" path="system.cheveux" type="text" + value=current.system.cheveux checked=checked.system.cheveux + }} + {{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs" + label="Yeux" path="system.yeux" type="text" + value=current.system.yeux checked=checked.system.yeux + }} +
+
+
+ Générer les valeurs sélectionnés +
+
+
+ + +
+
+
+ diff --git a/templates/actor/random/champ-aleatoire.hbs b/templates/actor/random/champ-aleatoire.hbs new file mode 100644 index 00000000..7535d2c4 --- /dev/null +++ b/templates/actor/random/champ-aleatoire.hbs @@ -0,0 +1,19 @@ +
+ + {{#if (eq type 'entier')}} + + {{else if (eq type 'heure')}} + + {{else}} + + {{/if}} +
+ + + +
+
\ No newline at end of file