commit 45273e5e43e52849ddf9aaabf4291894196db92c Author: sladecraven Date: Sat Oct 22 11:09:48 2022 +0200 Initial push diff --git a/README.md b/README.md new file mode 100644 index 0000000..eda248a --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# Système Foundry pour Mournblade (French RPG, Titam France/Sombres Projets) + +## EN + +Unofficial system for Mournblade (French version from Titam France). + +Books are mandatory to play and are available at : http://www.titam-france.fr + +## FR + +Système non-officiel pour le JDR Mournblade (Titam France). + +Ce système a été autorisé par Ludospherik ( http://www.ludospherik.fr/ ), merci à eux ! + +Les livres du jeu sont nécessaires pour jouer, et sont disponibles ici : http://www.titam-france.fr + +# Credits + +Mournblade, le jeu de rôle de Sword & Sorcery, is a property of Titam France/Sombres Projets. + +# Developmement + +LeRatierBretonnien diff --git a/assets/fonts/CentaurMT.otf b/assets/fonts/CentaurMT.otf new file mode 100644 index 0000000..9e5e7c1 Binary files /dev/null and b/assets/fonts/CentaurMT.otf differ diff --git a/assets/fonts/Chaparral Pro Regular.ttf b/assets/fonts/Chaparral Pro Regular.ttf new file mode 100644 index 0000000..a6bf4b7 Binary files /dev/null and b/assets/fonts/Chaparral Pro Regular.ttf differ diff --git a/assets/fonts/CharlemagneStd-Bold.otf b/assets/fonts/CharlemagneStd-Bold.otf new file mode 100644 index 0000000..b012835 Binary files /dev/null and b/assets/fonts/CharlemagneStd-Bold.otf differ diff --git a/assets/icons/adresse.webp b/assets/icons/adresse.webp new file mode 100644 index 0000000..e36b964 Binary files /dev/null and b/assets/icons/adresse.webp differ diff --git a/assets/icons/arme.webp b/assets/icons/arme.webp new file mode 100644 index 0000000..4f49f86 Binary files /dev/null and b/assets/icons/arme.webp differ diff --git a/assets/icons/bonneaventure.webp b/assets/icons/bonneaventure.webp new file mode 100644 index 0000000..a32ad80 Binary files /dev/null and b/assets/icons/bonneaventure.webp differ diff --git a/assets/icons/capacite.webp b/assets/icons/capacite.webp new file mode 100644 index 0000000..9df61b0 Binary files /dev/null and b/assets/icons/capacite.webp differ diff --git a/assets/icons/clairvoyance.webp b/assets/icons/clairvoyance.webp new file mode 100644 index 0000000..196810e Binary files /dev/null and b/assets/icons/clairvoyance.webp differ diff --git a/assets/icons/competence.webp b/assets/icons/competence.webp new file mode 100644 index 0000000..b0d564c Binary files /dev/null and b/assets/icons/competence.webp differ diff --git a/assets/icons/don.webp b/assets/icons/don.webp new file mode 100644 index 0000000..ea6a07d Binary files /dev/null and b/assets/icons/don.webp differ diff --git a/assets/icons/eclat.webp b/assets/icons/eclat.webp new file mode 100644 index 0000000..fdb1c0b Binary files /dev/null and b/assets/icons/eclat.webp differ diff --git a/assets/icons/equipement.webp b/assets/icons/equipement.webp new file mode 100644 index 0000000..a019529 Binary files /dev/null and b/assets/icons/equipement.webp differ diff --git a/assets/icons/heritage.webp b/assets/icons/heritage.webp new file mode 100644 index 0000000..b35e788 Binary files /dev/null and b/assets/icons/heritage.webp differ diff --git a/assets/icons/monnaie.webp b/assets/icons/monnaie.webp new file mode 100644 index 0000000..96cc153 Binary files /dev/null and b/assets/icons/monnaie.webp differ diff --git a/assets/icons/origine.webp b/assets/icons/origine.webp new file mode 100644 index 0000000..5401af9 Binary files /dev/null and b/assets/icons/origine.webp differ diff --git a/assets/icons/pacte.webp b/assets/icons/pacte.webp new file mode 100644 index 0000000..2c63612 Binary files /dev/null and b/assets/icons/pacte.webp differ diff --git a/assets/icons/predilection.webp b/assets/icons/predilection.webp new file mode 100644 index 0000000..ebba71b Binary files /dev/null and b/assets/icons/predilection.webp differ diff --git a/assets/icons/presence.webp b/assets/icons/presence.webp new file mode 100644 index 0000000..4f2a4ad Binary files /dev/null and b/assets/icons/presence.webp differ diff --git a/assets/icons/profession.webp b/assets/icons/profession.webp new file mode 100644 index 0000000..499f3c8 Binary files /dev/null and b/assets/icons/profession.webp differ diff --git a/assets/icons/protection.webp b/assets/icons/protection.webp new file mode 100644 index 0000000..bb88b96 Binary files /dev/null and b/assets/icons/protection.webp differ diff --git a/assets/icons/puissance.webp b/assets/icons/puissance.webp new file mode 100644 index 0000000..e537971 Binary files /dev/null and b/assets/icons/puissance.webp differ diff --git a/assets/icons/rune.webp b/assets/icons/rune.webp new file mode 100644 index 0000000..6c8ff34 Binary files /dev/null and b/assets/icons/rune.webp differ diff --git a/assets/icons/tendance.webp b/assets/icons/tendance.webp new file mode 100644 index 0000000..fc9ebad Binary files /dev/null and b/assets/icons/tendance.webp differ diff --git a/assets/icons/traitchaotique.webp b/assets/icons/traitchaotique.webp new file mode 100644 index 0000000..6052371 Binary files /dev/null and b/assets/icons/traitchaotique.webp differ diff --git a/assets/icons/trempe.webp b/assets/icons/trempe.webp new file mode 100644 index 0000000..e2371df Binary files /dev/null and b/assets/icons/trempe.webp differ diff --git a/assets/logos/mournblade_logo_chaos.webp b/assets/logos/mournblade_logo_chaos.webp new file mode 100644 index 0000000..a5ae37a Binary files /dev/null and b/assets/logos/mournblade_logo_chaos.webp differ diff --git a/assets/logos/mournblade_logo_texte.webp b/assets/logos/mournblade_logo_texte.webp new file mode 100644 index 0000000..a2aaac4 Binary files /dev/null and b/assets/logos/mournblade_logo_texte.webp differ diff --git a/assets/tokens/token_chaos.webp b/assets/tokens/token_chaos.webp new file mode 100644 index 0000000..5906817 Binary files /dev/null and b/assets/tokens/token_chaos.webp differ diff --git a/assets/tokens/token_chaos2.webp b/assets/tokens/token_chaos2.webp new file mode 100644 index 0000000..77980c1 Binary files /dev/null and b/assets/tokens/token_chaos2.webp differ diff --git a/assets/tokens/token_chaos3.webp b/assets/tokens/token_chaos3.webp new file mode 100644 index 0000000..bbdf74b Binary files /dev/null and b/assets/tokens/token_chaos3.webp differ diff --git a/assets/tokens/token_loi.webp b/assets/tokens/token_loi.webp new file mode 100644 index 0000000..2ed7924 Binary files /dev/null and b/assets/tokens/token_loi.webp differ diff --git a/assets/tokens/token_loi2.webp b/assets/tokens/token_loi2.webp new file mode 100644 index 0000000..24c523b Binary files /dev/null and b/assets/tokens/token_loi2.webp differ diff --git a/assets/tokens/token_loi3.webp b/assets/tokens/token_loi3.webp new file mode 100644 index 0000000..613303a Binary files /dev/null and b/assets/tokens/token_loi3.webp differ diff --git a/assets/tokens/token_loi4.webp b/assets/tokens/token_loi4.webp new file mode 100644 index 0000000..ec64aed Binary files /dev/null and b/assets/tokens/token_loi4.webp differ diff --git a/assets/ui/fond_mournblade.webp b/assets/ui/fond_mournblade.webp new file mode 100644 index 0000000..b066a93 Binary files /dev/null and b/assets/ui/fond_mournblade.webp differ diff --git a/assets/ui/pc_sheet_bg.webp b/assets/ui/pc_sheet_bg.webp new file mode 100644 index 0000000..53d6304 Binary files /dev/null and b/assets/ui/pc_sheet_bg.webp differ diff --git a/modules/hawkmoon-actor-sheet.js b/modules/hawkmoon-actor-sheet.js new file mode 100644 index 0000000..3b80393 --- /dev/null +++ b/modules/hawkmoon-actor-sheet.js @@ -0,0 +1,170 @@ +/** + * Extend the basic ActorSheet with some very simple modifications + * @extends {ActorSheet} + */ + +import { HawkmoonUtility } from "./hawkmoon-utility.js"; +import { HawkmoonRollDialog } from "./hawkmoon-roll-dialog.js"; + +/* -------------------------------------------- */ +export class HawkmoonActorSheet extends ActorSheet { + + /** @override */ + static get defaultOptions() { + + return mergeObject(super.defaultOptions, { + classes: ["fvtt-hawkmoon", "sheet", "actor"], + template: "systems/fvtt-hawkmoon-cyd/templates/actor-sheet.html", + width: 640, + height: 720, + tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "stats" }], + dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }], + editScore: false + }); + } + + /* -------------------------------------------- */ + async getData() { + const objectData = duplicate(this.object) + + let formData = { + title: this.title, + id: objectData.id, + type: objectData.type, + img: objectData.img, + name: objectData.name, + editable: this.isEditable, + cssClass: this.isEditable ? "editable" : "locked", + system: objectData.system, + effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)), + limited: this.object.limited, + skills: this.actor.getSkills(), + armes: duplicate(this.actor.getWeapons()), + protections: duplicate(this.actor.getArmors()), + dons: duplicate(this.actor.getDons()), + alignement: this.actor.getAlignement(), + aspect: this.actor.getAspect(), + marge: this.actor.getMarge(), + tendances:duplicate(this.actor.getTendances()), + runes:duplicate(this.actor.getRunes()), + origine: duplicate(this.actor.getOrigine() || {}), + heritage: duplicate(this.actor.getHeritage() || {}), + metier: duplicate(this.actor.getMetier() || {}), + combat: this.actor.getCombatValues(), + equipements: duplicate(this.actor.getEquipments()), + description: await TextEditor.enrichHTML(this.object.system.biodata.description, {async: true}), + options: this.options, + owner: this.document.isOwner, + editScore: this.options.editScore, + isGM: game.user.isGM + } + this.formData = formData; + + console.log("PC : ", formData, this.object); + return formData; + } + + + /* -------------------------------------------- */ + /** @override */ + activateListeners(html) { + super.activateListeners(html); + + // Everything below here is only needed if the sheet is editable + if (!this.options.editable) return; + + // Update Inventory Item + html.find('.item-edit').click(ev => { + const li = $(ev.currentTarget).parents(".item") + let itemId = li.data("item-id") + const item = this.actor.items.get( itemId ) + item.sheet.render(true) + }) + // Delete Inventory Item + html.find('.item-delete').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + HawkmoonUtility.confirmDelete(this, li); + }) + html.find('.edit-item-data').change(ev => { + const li = $(ev.currentTarget).parents(".item") + let itemId = li.data("item-id") + let itemType = li.data("item-type") + let itemField = $(ev.currentTarget).data("item-field") + let dataType = $(ev.currentTarget).data("dtype") + let value = ev.currentTarget.value + this.actor.editItemField(itemId, itemType, itemField, dataType, value) + }) + + html.find('.quantity-minus').click(event => { + const li = $(event.currentTarget).parents(".item"); + this.actor.incDecQuantity( li.data("item-id"), -1 ); + } ); + html.find('.quantity-plus').click(event => { + const li = $(event.currentTarget).parents(".item"); + this.actor.incDecQuantity( li.data("item-id"), +1 ); + } ); + + html.find('.roll-attribut').click((event) => { + const li = $(event.currentTarget).parents(".item") + let attrKey = li.data("attr-key") + this.actor.rollAttribut(attrKey) + }) + html.find('.roll-competence').click((event) => { + const li = $(event.currentTarget).parents(".item") + let attrKey = $(event.currentTarget).data("attr-key") + let compId = li.data("item-id") + this.actor.rollCompetence(attrKey, compId) + }) + html.find('.roll-rune').click((event) => { + const li = $(event.currentTarget).parents(".item") + let runeId = li.data("item-id") + this.actor.rollRune(runeId) + }) + html.find('.roll-arme-offensif').click((event) => { + const li = $(event.currentTarget).parents(".item") + let armeId = li.data("item-id") + this.actor.rollArmeOffensif(armeId) + }) + html.find('.roll-arme-degats').click((event) => { + const li = $(event.currentTarget).parents(".item") + let armeId = li.data("item-id") + this.actor.rollArmeDegats(armeId) + }) + + + html.find('.lock-unlock-sheet').click((event) => { + this.options.editScore = !this.options.editScore; + this.render(true); + }); + html.find('.item-equip').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + this.actor.equipItem( li.data("item-id") ); + this.render(true); + }); + + } + + /* -------------------------------------------- */ + /** @override */ + setPosition(options = {}) { + const position = super.setPosition(options); + const sheetBody = this.element.find(".sheet-body"); + const bodyHeight = position.height - 192; + sheetBody.css("height", bodyHeight); + return position; + } + + /* -------------------------------------------- */ + /*async _onDropItem(event, dragData) { + let item = await HawkmoonUtility.searchItem( dragData) + this.actor.preprocessItem( event, item, true ) + super._onDropItem(event, dragData) + }*/ + + /* -------------------------------------------- */ + /** @override */ + _updateObject(event, formData) { + // Update the Actor + return this.object.update(formData); + } +} diff --git a/modules/hawkmoon-actor.js b/modules/hawkmoon-actor.js new file mode 100644 index 0000000..1a78c33 --- /dev/null +++ b/modules/hawkmoon-actor.js @@ -0,0 +1,505 @@ +/* -------------------------------------------- */ +import { HawkmoonUtility } from "./hawkmoon-utility.js"; +import { HawkmoonRollDialog } from "./hawkmoon-roll-dialog.js"; + +/* -------------------------------------------- */ +const __degatsBonus = [-2, -2, -1, -1, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 8, 8, 9, 9, 10, 10] +const __vitesseBonus = [-2, -2, -1, -1, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8] + +/* -------------------------------------------- */ +/** + * Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system. + * @extends {Actor} + */ +export class HawkmoonActor extends Actor { + + /* -------------------------------------------- */ + /** + * Override the create() function to provide additional SoS functionality. + * + * This overrided create() function adds initial items + * Namely: Basic skills, money, + * + * @param {Object} data Barebones actor data which this function adds onto. + * @param {Object} options (Unused) Additional options which customize the creation workflow. + * + */ + + static async create(data, options) { + + // Case of compendium global import + if (data instanceof Array) { + return super.create(data, options); + } + // If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic + if (data.items) { + let actor = super.create(data, options); + return actor; + } + + if (data.type == 'personnage') { + const skills = await HawkmoonUtility.loadCompendium("fvtt-hawkmoon-cyd.skills") + data.items = skills.map(i => i.toObject()) + } + if (data.type == 'pnj') { + } + + return super.create(data, options); + } + + /* -------------------------------------------- */ + prepareArme(arme) { + arme = duplicate(arme) + let combat = this.getCombatValues() + if (arme.system.typearme == "contact" || arme.system.typearme == "contactjet") { + arme.system.competence = duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "mêlée")) + arme.system.attrKey = "pui" + arme.system.totalDegats = arme.system.degats + "+" + combat.bonusDegatsTotal + arme.system.totalOffensif = this.system.attributs.pui.value + arme.system.competence.system.niveau + arme.system.bonusmaniementoff + if (arme.system.isdefense) { + arme.system.totalDefensif = combat.defenseTotal + arme.system.competence.system.niveau + arme.system.bonusmaniementdef + } + } + if (arme.system.typearme == "jet" || arme.system.typearme == "tir") { + arme.system.competence = duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "armes à distance")) + arme.system.attrKey = "adr" + arme.system.totalOffensif = this.system.attributs.adr.value + arme.system.competence.system.niveau + arme.system.bonusmaniementoff + arme.system.totalDegats = arme.system.degats + if (arme.system.isdefense) { + arme.system.totalDefensif = combat.defenseTotal + arme.system.competence.system.niveau + arme.system.bonusmaniementdef + } + } + return arme + } + /* -------------------------------------------- */ + prepareBouclier(bouclier) { + bouclier = duplicate(bouclier) + let combat = this.getCombatValues() + bouclier.system.competence = duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "mêlée")) + bouclier.system.attrKey = "pui" + bouclier.system.totalDegats = bouclier.system.degats + "+" + combat.bonusDegatsTotal + bouclier.system.totalOffensif = this.system.attributs.pui.value + bouclier.system.competence.system.niveau + bouclier.system.isdefense = true + bouclier.system.bonusmaniementoff = 0 + bouclier.system.totalDefensif = combat.defenseTotal + bouclier.system.competence.system.niveau + bouclier.system.bonusdefense + return bouclier + } + + /* -------------------------------------------- */ + getWeapons() { + let armes = [] + for (let arme of this.items) { + if (arme.type == "arme") { + armes.push(this.prepareArme(arme)) + } + if (arme.type == "bouclier") { + armes.push(this.prepareBouclier(arme)) + } + } + return armes + } + + /* -------------------------------------------- */ + getDons() { + return this.items.filter(item => item.type == "don") + } + /* -------------------------------------------- */ + getTendances() { + return this.items.filter(item => item.type == "tendance") + } + getRunes() { + return this.items.filter(item => item.type == "rune") + } + /* -------------------------------------------- */ + getEquipments() { + return this.items.filter(item => item.type == "equipement") + } + /* -------------------------------------------- */ + getArmors() { + return this.items.filter(item => item.type == "protection") + } + getOrigine() { + return this.items.find(item => item.type == "origine") + } + getMetier() { + return this.items.find(item => item.type == "metier") + } + getHeritage() { + return this.items.find(item => item.type == "heritage") + } + /* -------------------------------------------- */ + getSkills() { + let comp = [] + for (let item of this.items) { + item = duplicate(item) + if (item.type == "competence") { + item.system.attribut1total = item.system.niveau + (this.system.attributs[item.system.attribut1]?.value || 0) + item.system.attribut2total = item.system.niveau + (this.system.attributs[item.system.attribut2]?.value || 0) + item.system.attribut3total = item.system.niveau + (this.system.attributs[item.system.attribut3]?.value || 0) + if (item.system.niveau == 0) { + item.system.attribut1total -= 3 + item.system.attribut2total -= 3 + item.system.attribut3total -= 3 + } + item.system.attribut1label = this.system.attributs[item.system.attribut1]?.label || "" + item.system.attribut2label = this.system.attributs[item.system.attribut2]?.label || "" + item.system.attribut3label = this.system.attributs[item.system.attribut3]?.label || "" + comp.push(item) + } + } + return comp.sort(function (a, b) { + let fa = a.name.toLowerCase(), + fb = b.name.toLowerCase(); + if (fa < fb) { + return -1; + } + if (fa > fb) { + return 1; + } + return 0; + }) + } + + /* -------------------------------------------- */ + getAspect() { + return (this.system.balance.loi > this.system.balance.chaos) ? this.system.balance.loi : this.system.balance.chaos + } + getMarge() { + return Math.abs( this.system.balance.loi - this.system.balance.chaos) + } + getAlignement() { + return (this.system.balance.loi > this.system.balance.chaos) ? "loyal" : "chaotique" + } + + /* -------------------------------------------- */ + getDefenseBase() { + return this.system.attributs.tre.value + 5 + } + + /* -------------------------------------------- */ + getVitesseBase() { + return 5 + __vitesseBonus[this.system.attributs.adr.value] + } + + /* -------------------------------------------- */ + getCombatValues() { + let combat = { + initBase: this.system.attributs.adr.value, + initTotal: this.system.attributs.adr.value + this.system.combat.initbonus, + bonusDegats: this.getBonusDegats(), + bonusDegatsTotal: this.getBonusDegats() + this.system.combat.bonusdegats, + vitesseBase: this.getVitesseBase(), + vitesseTotal: this.getVitesseBase() + this.system.combat.vitessebonus, + defenseBase: this.getDefenseBase(), + defenseTotal: this.getDefenseBase() + this.system.combat.defensebonus + } + return combat + } + + /* -------------------------------------------- */ + prepareBaseData() { + } + + /* -------------------------------------------- */ + async prepareData() { + super.prepareData(); + } + + /* -------------------------------------------- */ + prepareDerivedData() { + + if (this.type == 'personnage') { + let newSante = this.system.sante.bonus + (this.system.attributs.pui.value + this.system.attributs.tre.value) * 2 + 5 + if (this.system.sante.base != newSante) { + this.update({ 'system.sante.base': newSante }) + } + let newAme = (this.system.attributs.cla.value + this.system.attributs.tre.value) * this.system.biodata.amemultiplier + 5 + if (this.system.ame.fullmax != newAme) { + this.update({ 'system.ame.fullmax': newAme }) + } + } + + super.prepareDerivedData() + } + + /* -------------------------------------------- */ + _preUpdate(changed, options, user) { + + super._preUpdate(changed, options, user); + } + + /* -------------------------------------------- */ + getItemById(id) { + let item = this.items.find(item => item.id == id); + if (item) { + item = duplicate(item) + } + return item; + } + + /* -------------------------------------------- */ + async equipItem(itemId) { + let item = this.items.find(item => item.id == itemId) + if (item && item.system) { + let update = { _id: item.id, "system.equipped": !item.system.equipped } + await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity + } + } + + /* -------------------------------------------- */ + editItemField(itemId, itemType, itemField, dataType, value) { + let item = this.items.find(item => item.id == itemId) + if (item) { + console.log("Item ", item, itemField, dataType, value) + if (dataType.toLowerCase() == "number") { + value = Number(value) + } else { + value = String(value) + } + let update = { _id: item.id, [`system.${itemField}`]: value }; + this.updateEmbeddedDocuments("Item", [update]) + } + } + + /* -------------------------------------------- */ + getBonneAventure() { + return this.system.bonneaventure.actuelle + } + + /* -------------------------------------------- */ + changeBonneAventure(value) { + let newBA = this.system.bonneaventure.actuelle + newBA += value + this.update({ 'system.bonneaventure.actuelle': newBA }) + } + + /* -------------------------------------------- */ + getEclat() { + return this.system.eclat.value + } + + /* -------------------------------------------- */ + changeEclat(value) { + let newE = this.system.eclat.value + newE += value + this.update({ 'system.eclat.value': newE }) + } + + /* -------------------------------------------- */ + canEclatDoubleD20() { + return (this.getAlignement() == "loyal" && this.system.eclat.value > 0) + } + /* -------------------------------------------- */ + subPointsAme(runeMode, value) { + let ame = duplicate(this.system.ame) + if(runeMode == "prononcer") { + ame.value -= value + } else { + ame.currentmax -= value + } + this.update( {'system.ame': ame}) + } + + /* -------------------------------------------- */ + compareName(a, b) { + if (a.name < b.name) { + return -1; + } + if (a.name > b.name) { + return 1; + } + return 0; + } + + /* -------------------------------------------- */ + getAttribute(attrKey) { + return this.system.attributes[attrKey] + } + + /* -------------------------------------------- */ + getBonusDegats() { + return __degatsBonus[this.system.attributs.pui.value] + } + + /* -------------------------------------------- */ + async equipGear(equipmentId) { + let item = this.items.find(item => item.id == equipmentId); + if (item && item.system.data) { + let update = { _id: item.id, "system.equipped": !item.system.equipped }; + await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity + } + } + + /* -------------------------------------------- */ + getSubActors() { + let subActors = []; + for (let id of this.system.subactors) { + subActors.push(duplicate(game.actors.get(id))); + } + return subActors; + } + /* -------------------------------------------- */ + async addSubActor(subActorId) { + let subActors = duplicate(this.system.subactors); + subActors.push(subActorId); + await this.update({ 'system.subactors': subActors }); + } + /* -------------------------------------------- */ + async delSubActor(subActorId) { + let newArray = []; + for (let id of this.system.subactors) { + if (id != subActorId) { + newArray.push(id); + } + } + await this.update({ 'system.subactors': newArray }); + } + + /* -------------------------------------------- */ + async incDecQuantity(objetId, incDec = 0) { + let objetQ = this.items.get(objetId) + if (objetQ) { + let newQ = objetQ.system.quantity + incDec; + const updated = await this.updateEmbeddedDocuments('Item', [{ _id: objetQ.id, 'system.quantity': newQ }]); // pdates one EmbeddedEntity + } + } + + /* -------------------------------------------- */ + getCompetence(compId) { + return this.items.get(compId) + } + + /* -------------------------------------------- */ + async setPredilectionUsed(compId, predIdx) { + let comp = this.items.get(compId) + let pred = duplicate(comp.system.predilections) + pred[predIdx].used = true + await this.updateEmbeddedDocuments('Item', [{ _id: compId, 'system.predilections': pred }]) + } + + /* -------------------------------------------- */ + getInitiativeScore( ) { + return Number(this.system.attributs.adr.value) + Number(this.system.combat.initbonus) + } + /* -------------------------------------------- */ + getBestDefenseValue() { + let defenseList = this.items.filter(item => (item.type =="arme" || item.type == "bouclier") && item.system.equipped) + let maxDef = 0 + let bestArme + for(let arme of defenseList) { + if (arme.type == "arme" && arme.system.isdefense) { + arme = this.prepareArme(arme) + } + if (arme.type == "bouclier" ) { + arme = this.prepareBouclier(arme) + } + if ( arme.system.totalDefensif > maxDef) { + maxDef = arme.system.totalDefensif + bestArme = duplicate(arme) + } + } + return bestArme + } + + /* -------------------------------------------- */ + getCommonRollData(attrKey = undefined, compId = undefined, compName = undefined) { + let rollData = HawkmoonUtility.getBasicRollData() + rollData.alias = this.name + rollData.actorImg = this.img + rollData.actorId = this.id + rollData.img = this.img + rollData.canEclatDoubleD20 = this.canEclatDoubleD20() + rollData.doubleD20 = false + rollData.attributs = HawkmoonUtility.getAttributs() + + if (attrKey) { + rollData.attrKey = attrKey + if (attrKey != "tochoose") { + rollData.actionImg = "systems/fvtt-mournblade/assets/icons/" + this.system.attributs[attrKey].labelnorm + ".webp" + rollData.attr = duplicate(this.system.attributs[attrKey]) + } + } + if (compId) { + rollData.competence = duplicate(this.items.get(compId) || {}) + rollData.actionImg = rollData.competence?.img + } + if (compName) { + rollData.competence = duplicate(this.items.find( item => item.name.toLowerCase() == compName.toLowerCase()) || {}) + rollData.actionImg = rollData.competence?.img + } + return rollData + } + + /* -------------------------------------------- */ + async rollAttribut(attrKey) { + let rollData = this.getCommonRollData(attrKey) + let rollDialog = await HawkmoonRollDialog.create(this, rollData) + rollDialog.render(true) + } + + /* -------------------------------------------- */ + async rollCompetence(attrKey, compId) { + let rollData = this.getCommonRollData(attrKey, compId) + console.log("RollDatra", rollData) + let rollDialog = await HawkmoonRollDialog.create(this, rollData) + rollDialog.render(true) + } + + /* -------------------------------------------- */ + async rollRune(runeId) { + let comp = this.items.find(comp => comp.type == "competence" && comp.name.toLowerCase() == "savoir : runes") + if ( !comp) { + ui.notifications.warn("La compétence Savoirs : Runes n'a pas été trouvée, abandon.") + return + } + let rollData = this.getCommonRollData("cla", undefined, "Savoir : Runes") + rollData.rune = duplicate(this.items.get(runeId) || {}) + rollData.difficulte = rollData.rune?.system?.seuil || 0 + rollData.runemode = "prononcer" + rollData.runeame = 1 + console.log("runeData", rollData) + let rollDialog = await HawkmoonRollDialog.create(this, rollData) + rollDialog.render(true) + } + + /* -------------------------------------------- */ + async rollArmeOffensif(armeId) { + let arme = this.items.get(armeId) + if (arme.type == "arme") { + arme = this.prepareArme(arme) + } + if (arme.type == "bouclier") { + arme = this.prepareBouclier(arme) + } + let rollData = this.getCommonRollData(arme.system.attrKey, arme.system.competence._id) + rollData.arme = arme + console.log("ARME!", rollData) + let rollDialog = await HawkmoonRollDialog.create(this, rollData) + rollDialog.render(true) + } + + /* -------------------------------------------- */ + async rollArmeDegats(armeId) { + let arme = this.items.get(armeId) + if (arme.type == "arme") { + arme = this.prepareArme(arme) + } + if (arme.type == "bouclier") { + arme = this.prepareBouclier(arme) + } + let roll = new Roll(arme.system.totalDegats).roll({ async: false }) + await HawkmoonUtility.showDiceSoNice(roll, game.settings.get("core", "rollMode")); + let rollData = { + arme: arme, + finalResult: roll.total, + alias: this.name, + actorImg: this.img, + actorId: this.id, + actionImg: arme.img, + } + HawkmoonUtility.createChatWithRollMode(rollData.alias, { + content: await renderTemplate(`systems/fvtt-mournblade/templates/chat-degats-result.html`, rollData) + }) + + } +} diff --git a/modules/hawkmoon-combat.js b/modules/hawkmoon-combat.js new file mode 100644 index 0000000..5ec1a42 --- /dev/null +++ b/modules/hawkmoon-combat.js @@ -0,0 +1,27 @@ +import { HawkmoonUtility } from "./hawkmoon-utility.js"; + +/* -------------------------------------------- */ +export class HawkmoonCombat extends Combat { + + /* -------------------------------------------- */ + async rollInitiative(ids, formula = undefined, messageOptions = {} ) { + ids = typeof ids === "string" ? [ids] : ids; + for (let cId = 0; cId < ids.length; cId++) { + const c = this.combatants.get(ids[cId]); + let id = c._id || c.id; + let initBonus = c.actor ? c.actor.getInitiativeScore() : 0 + let roll = new Roll("1d10 + "+initBonus).roll({ async: false}) + await HawkmoonUtility.showDiceSoNice(roll, game.settings.get("core", "rollMode")) + //console.log("Init bonus", initBonus, roll.total) + await this.updateEmbeddedDocuments("Combatant", [ { _id: id, initiative: roll.total } ]); + } + + return this; + } + + /* -------------------------------------------- */ + _onUpdate(changed, options, userId) { + } + + +} diff --git a/modules/hawkmoon-commands.js b/modules/hawkmoon-commands.js new file mode 100644 index 0000000..bbccd9b --- /dev/null +++ b/modules/hawkmoon-commands.js @@ -0,0 +1,123 @@ +/* -------------------------------------------- */ + +import { HawkmoonUtility } from "./hawkmoon-utility.js"; +import { HawkmoonRollDialog } from "./hawkmoon-roll-dialog.js"; + +/* -------------------------------------------- */ +export class HawkmoonCommands { + + static init() { + if (!game.system.mournblade.commands) { + //const HawkmoonCommands = new HawkmoonCommands() + //HawkmoonCommands.registerCommand({ path: ["/char"], func: (content, msg, params) => HawkmoonCommands.createChar(msg), descr: "Create a new character" }); + //game.system.mournblade.commands = HawkmoonCommands + } + } + + constructor() { + this.commandsTable = {} + } + + /* -------------------------------------------- */ + registerCommand(command) { + this._addCommand(this.commandsTable, command.path, '', command); + } + + /* -------------------------------------------- */ + _addCommand(targetTable, path, fullPath, command) { + if (!this._validateCommand(targetTable, path, command)) { + return; + } + const term = path[0]; + fullPath = fullPath + term + ' ' + if (path.length == 1) { + command.descr = `${fullPath}: ${command.descr}`; + targetTable[term] = command; + } + else { + if (!targetTable[term]) { + targetTable[term] = { subTable: {} }; + } + this._addCommand(targetTable[term].subTable, path.slice(1), fullPath, command) + } + } + + /* -------------------------------------------- */ + _validateCommand(targetTable, path, command) { + if (path.length > 0 && path[0] && command.descr && (path.length != 1 || targetTable[path[0]] == undefined)) { + return true; + } + console.warn("HawkmoonCommands._validateCommand failed ", targetTable, path, command); + return false; + } + + + /* -------------------------------------------- */ + /* Manage chat commands */ + processChatCommand(commandLine, content = '', msg = {}) { + // Setup new message's visibility + let rollMode = game.settings.get("core", "rollMode"); + if (["gmroll", "blindroll"].includes(rollMode)) msg["whisper"] = ChatMessage.getWhisperRecipients("GM"); + if (rollMode === "blindroll") msg["blind"] = true; + msg["type"] = 0; + + let command = commandLine[0].toLowerCase(); + let params = commandLine.slice(1); + + return this.process(command, params, content, msg); + } + + /* -------------------------------------------- */ + process(command, params, content, msg) { + return this._processCommand(this.commandsTable, command, params, content, msg); + } + + /* -------------------------------------------- */ + _processCommand(commandsTable, name, params, content = '', msg = {}, path = "") { + console.log("===> Processing command") + let command = commandsTable[name]; + path = path + name + " "; + if (command && command.subTable) { + if (params[0]) { + return this._processCommand(command.subTable, params[0], params.slice(1), content, msg, path) + } + else { + this.help(msg, command.subTable); + return true; + } + } + if (command && command.func) { + const result = command.func(content, msg, params); + if (result == false) { + RdDCommands._chatAnswer(msg, command.descr); + } + return true; + } + return false; + } + + /* -------------------------------------------- */ + async createChar(msg) { + game.system.Hawkmoon.creator = new HawkmoonActorCreate(); + game.system.Hawkmoon.creator.start(); + } + + /* -------------------------------------------- */ + static _chatAnswer(msg, content) { + msg.whisper = [game.user.id]; + msg.content = content; + ChatMessage.create(msg); + } + + /* -------------------------------------------- */ + async poolRoll( msg) { + let rollData = HawkmoonUtility.getBasicRollData() + rollData.alias = "Dice Pool Roll", + rollData.mode = "generic" + rollData.title = `Dice Pool Roll`; + + let rollDialog = await HawkmoonRollDialog.create( this, rollData); + rollDialog.render( true ); + } + +} \ No newline at end of file diff --git a/modules/hawkmoon-item-sheet.js b/modules/hawkmoon-item-sheet.js new file mode 100644 index 0000000..e292cba --- /dev/null +++ b/modules/hawkmoon-item-sheet.js @@ -0,0 +1,178 @@ +import { HawkmoonUtility } from "./hawkmoon-utility.js"; + +/** + * Extend the basic ItemSheet with some very simple modifications + * @extends {ItemSheet} + */ +export class HawkmoonItemSheet extends ItemSheet { + + /** @override */ + static get defaultOptions() { + + return mergeObject(super.defaultOptions, { + classes: ["fvtt-hawkmoon", "sheet", "item"], + template: "systems/fvtt-hawkmoon-cyd/templates/item-sheet.html", + dragDrop: [{ dragSelector: null, dropSelector: null }], + width: 620, + height: 550 + //tabs: [{navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description"}] + }); + } + + /* -------------------------------------------- */ + _getHeaderButtons() { + let buttons = super._getHeaderButtons(); + // Add "Post to chat" button + // We previously restricted this to GM and editable items only. If you ever find this comment because it broke something: eh, sorry! + buttons.unshift( + { + class: "post", + icon: "fas fa-comment", + onclick: ev => { } + }) + return buttons + } + + /* -------------------------------------------- */ + /** @override */ + setPosition(options = {}) { + const position = super.setPosition(options); + const sheetBody = this.element.find(".sheet-body"); + const bodyHeight = position.height - 192; + sheetBody.css("height", bodyHeight); + if (this.item.type.includes('weapon')) { + position.width = 640; + } + return position; + } + + /* -------------------------------------------- */ + async getData() { + const objectData = duplicate(this.object) + let formData = { + title: this.title, + id: this.id, + type: objectData.type, + img: objectData.img, + name: objectData.name, + editable: this.isEditable, + cssClass: this.isEditable ? "editable" : "locked", + attributs: HawkmoonUtility.getAttributs(), + system: objectData.system, + limited: this.object.limited, + options: this.options, + owner: this.document.isOwner, + description: await TextEditor.enrichHTML(this.object.system.description, {async: true}), + mr: (this.object.type == 'specialisation'), + isGM: game.user.isGM + } + + if ( objectData.type == "don") { + formData.sacrifice = await TextEditor.enrichHTML(this.object.system.sacrifice, {async: true}) + } + //this.options.editable = !(this.object.origin == "embeddedItem"); + console.log("ITEM DATA", formData, this); + return formData; + } + + + /* -------------------------------------------- */ + _getHeaderButtons() { + let buttons = super._getHeaderButtons(); + buttons.unshift({ + class: "post", + icon: "fas fa-comment", + onclick: ev => this.postItem() + }); + return buttons + } + + /* -------------------------------------------- */ + postItem() { + let chatData = duplicate(HawkmoonUtility.data(this.item)); + if (this.actor) { + chatData.actor = { id: this.actor.id }; + } + // Don't post any image for the item (which would leave a large gap) if the default image is used + if (chatData.img.includes("/blank.png")) { + chatData.img = null; + } + // JSON object for easy creation + chatData.jsondata = JSON.stringify( + { + compendium: "postedItem", + payload: chatData, + }); + + renderTemplate('systems/fvtt-Hawkmoon-rpg/templates/post-item.html', chatData).then(html => { + let chatOptions = HawkmoonUtility.chatDataSetup(html); + ChatMessage.create(chatOptions) + }); + } + + /* -------------------------------------------- */ + /** @override */ + activateListeners(html) { + super.activateListeners(html); + + // Everything below here is only needed if the sheet is editable + if (!this.options.editable) return; + + + // Update Inventory Item + html.find('.item-edit').click(ev => { + const li = $(ev.currentTarget).parents(".item") + const item = this.object.options.actor.getOwnedItem(li.data("item-id")) + item.sheet.render(true); + }); + + html.find('.delete-subitem').click(ev => { + this.deleteSubitem(ev); + }) + html.find('.edit-prediction').change(ev => { + const li = $(ev.currentTarget).parents(".prediction-item") + let index = li.data("prediction-index") + let pred = duplicate(this.object.system.predilections) + pred[index].name = ev.currentTarget.value + this.object.update( { 'data.predilections': pred }) + }) + html.find('.delete-prediction').click(ev => { + const li = $(ev.currentTarget).parents(".prediction-item") + let index = li.data("prediction-index") + let pred = duplicate(this.object.system.predilections) + pred.splice(index,1) + this.object.update( { 'data.predilections': pred }) + }) + html.find('.use-prediction').change(ev => { + const li = $(ev.currentTarget).parents(".prediction-item") + let index = li.data("prediction-index") + let pred = duplicate(this.object.system.predilections) + pred[index].used = ev.currentTarget.checked + this.object.update( { 'data.predilections': pred }) + }) + html.find('#add-predilection').click(ev => { + let pred = duplicate(this.object.system.predilections) + pred.push( { name: "Nouvelle prédilection", used: false }) + this.object.update( { 'data.predilections': pred }) + }) + // Update Inventory Item + html.find('.item-delete').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + let itemType = li.data("item-type"); + }); + + } + + /* -------------------------------------------- */ + get template() { + let type = this.item.type; + return `systems/fvtt-mournblade/templates/item-${type}-sheet.html`; + } + + /* -------------------------------------------- */ + /** @override */ + _updateObject(event, formData) { + return this.object.update(formData); + } +} diff --git a/modules/hawkmoon-item.js b/modules/hawkmoon-item.js new file mode 100644 index 0000000..b210614 --- /dev/null +++ b/modules/hawkmoon-item.js @@ -0,0 +1,31 @@ +import { HawkmoonUtility } from "./hawkmoon-utility.js"; + +export const defaultItemImg = { + competence: "systems/fvtt-hawkmoon-cyd/assets/icons/competence.webp", + arme: "systems/fvtt-hawkmoon-cyd/assets/icons/arme.webp", + capacite: "systems/fvtt-hawkmoon-cyd/assets/icons/capacite.webp", + don: "systems/fvtt-hawkmoon-cyd/assets/icons/don.webp", + equipement: "systems/fvtt-hawkmoon-cyd/assets/icons/equipement.webp", + monnaie: "systems/fvtt-hawkmoon-cyd/assets/icons/monnaie.webp", + pacte: "systems/fvtt-hawkmoon-cyd/assets/icons/pacte.webp", + predilection: "systems/fvtt-hawkmoon-cyd/assets/icons/predilection.webp", + protection: "systems/fvtt-hawkmoon-cyd/assets/icons/protection.webp", + rune: "systems/fvtt-hawkmoon-cyd/assets/icons/rune.webp", + tendance: "systems/fvtt-hawkmoon-cyd/assets/icons/tendance.webp", + traitchaotique: "systems/fvtt-hawkmoon-cyd/assets/icons/traitchaotique.webp", +} + +/** + * Extend the basic ItemSheet with some very simple modifications + * @extends {ItemSheet} + */ +export class HawkmoonItem extends Item { + + constructor(data, context) { + if (!data.img) { + data.img = defaultItemImg[data.type]; + } + super(data, context); + } + +} diff --git a/modules/hawkmoon-main.js b/modules/hawkmoon-main.js new file mode 100644 index 0000000..41279a7 --- /dev/null +++ b/modules/hawkmoon-main.js @@ -0,0 +1,134 @@ +/** + * Hawkmoon system + * Author: Uberwald + * Software License: Prop + */ + +/* -------------------------------------------- */ + +/* -------------------------------------------- */ +// Import Modules +import { HawkmoonActor } from "./hawkmoon-actor.js"; +import { HawkmoonItemSheet } from "./hawkmoon-item-sheet.js"; +import { HawkmoonActorSheet } from "./hawkmoon-actor-sheet.js"; +import { HawkmoonUtility } from "./hawkmoon-utility.js"; +import { HawkmoonCombat } from "./hawkmoon-combat.js"; +import { HawkmoonItem } from "./hawkmoon-item.js"; + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ + +/************************************************************************************/ +Hooks.once("init", async function () { + console.log(`Initializing Hawkmoon RPG`); + + /* -------------------------------------------- */ + // preload handlebars templates + HawkmoonUtility.preloadHandlebarsTemplates(); + + /* -------------------------------------------- */ + // Set an initiative formula for the system + CONFIG.Combat.initiative = { + formula: "1d6", + decimals: 1 + }; + + /* -------------------------------------------- */ + game.socket.on("system.fvtt-hawkmoon-cyd", data => { + HawkmoonUtility.onSocketMesssage(data); + }); + + /* -------------------------------------------- */ + // Define custom Entity classes + CONFIG.Combat.documentClass = HawkmoonCombat + CONFIG.Actor.documentClass = HawkmoonActor + CONFIG.Item.documentClass = HawkmoonItem + game.system.hawkmon = { + HawkmoonUtility + } + + /* -------------------------------------------- */ + // Register sheet application classes + Actors.unregisterSheet("core", ActorSheet); + Actors.registerSheet("fvtt-hawkmoon-cyd", HawkmoonActorSheet, { types: ["personnage"], makeDefault: true }) + //Actors.registerSheet("fvtt-hawkmoon-cyd", HawkmoonNPCSheet, { types: ["npc"], makeDefault: false }); + + Items.unregisterSheet("core", ItemSheet); + Items.registerSheet("fvtt-hawkmoon-cyd", HawkmoonItemSheet, { makeDefault: true }) + + HawkmoonUtility.init(); + +}); + +/* -------------------------------------------- */ +function welcomeMessage() { + ChatMessage.create({ + user: game.user.id, + whisper: [game.user.id], + content: `
+ Bienvenue dans Hawkmoon ! +

Les livres de Hawkmoon sont nécessaires pour jouer : https://www.titam-france.fr

+

Hawkmoon est jeude rôle publié par Titam France/Sombres projets, tout les droits leur appartiennent.

+ ` }); +} + +/* -------------------------------------------- */ +// Register world usage statistics +function registerUsageCount( registerKey ) { + if ( game.user.isGM ) { + game.settings.register(registerKey, "world-key", { + name: "Unique world key", + scope: "world", + config: false, + default: "", + type: String + }); + + let worldKey = game.settings.get(registerKey, "world-key") + if ( worldKey == undefined || worldKey == "" ) { + worldKey = randomID(32) + game.settings.set(registerKey, "world-key", worldKey ) + } + // Simple API counter + let regURL = `https://www.uberwald.me/fvtt_appcount/count.php?name="${registerKey}"&worldKey="${worldKey}"&version="${game.release.generation}.${game.release.build}"&system="${game.system.id}"&systemversion="${game.system.version}"` + //$.ajaxSetup({ + //headers: { 'Access-Control-Allow-Origin': '*' } + //}) + $.ajax(regURL) + } +} + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ +Hooks.once("ready", function () { + + HawkmoonUtility.ready(); + // User warning + if (!game.user.isGM && game.user.character == undefined) { + ui.notifications.info("Attention ! Aucun personnage n'est relié au joueur !"); + ChatMessage.create({ + content: "ATTENTION Le joueur " + game.user.name + " n'est relié à aucun personnage !", + user: game.user._id + }); + } + + registerUsageCount('fvtt-hawkmoon-cyd') + welcomeMessage() +}); + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ +Hooks.on("chatMessage", (html, content, msg) => { + if (content[0] == '/') { + let regExp = /(\S+)/g; + let commands = content.match(regExp); + if (game.system.mournblade.commands.processChatCommand(commands, content, msg)) { + return false; + } + } + return true; +}); + diff --git a/modules/hawkmoon-roll-dialog.js b/modules/hawkmoon-roll-dialog.js new file mode 100644 index 0000000..5892b9d --- /dev/null +++ b/modules/hawkmoon-roll-dialog.js @@ -0,0 +1,79 @@ +import { HawkmoonUtility } from "./hawkmoon-utility.js"; + +export class HawkmoonRollDialog extends Dialog { + + /* -------------------------------------------- */ + static async create(actor, rollData ) { + + let options = { classes: ["HawkmoonDialog"], width: 340, height: 420, 'z-index': 99999 }; + let html = await renderTemplate('systems/fvtt-hawkmoon-cyd/templates/roll-dialog-generic.html', rollData); + + return new HawkmoonRollDialog(actor, rollData, html, options ); + } + + /* -------------------------------------------- */ + constructor(actor, rollData, html, options, close = undefined) { + let conf = { + title: "Test de Capacité", + content: html, + buttons: { + rolld10: { + icon: '', + label: "Lancer 1d10", + callback: () => { this.roll("1d10") } + }, + rolld20: { + icon: '', + label: "Lancer 1d20", + callback: () => { this.roll("1d20") } + }, + cancel: { + icon: '', + label: "Annuler", + callback: () => { this.close() } + } }, + close: close + } + + super(conf, options); + + this.actor = actor + this.rollData = rollData + } + + /* -------------------------------------------- */ + roll ( dice) { + this.rollData.mainDice = dice + HawkmoonUtility.rollHawkmoon( this.rollData ) + } + + + /* -------------------------------------------- */ + activateListeners(html) { + super.activateListeners(html); + + var dialog = this; + function onLoad() { + } + $(function () { onLoad(); }); + + html.find('#modificateur').change(async (event) => { + this.rollData.modificateur = Number(event.currentTarget.value) + }) + html.find('#difficulte').change(async (event) => { + this.rollData.difficulte = Number(event.currentTarget.value) + }) + html.find('#attrKey').change(async (event) => { + this.rollData.attrKey = String(event.currentTarget.value) + }) + html.find('#runemode').change(async (event) => { + this.rollData.runemode = String(event.currentTarget.value) + }) + html.find('#runeame').change(async (event) => { + this.rollData.runeame = Number(event.currentTarget.value) + }) + html.find('#doubleD20').change(async (event) => { + this.rollData.doubleD20 = event.currentTarget.checked + }) + } +} \ No newline at end of file diff --git a/modules/hawkmoon-utility.js b/modules/hawkmoon-utility.js new file mode 100644 index 0000000..7bf67bb --- /dev/null +++ b/modules/hawkmoon-utility.js @@ -0,0 +1,662 @@ +/* -------------------------------------------- */ +import { HawkmoonCombat } from "./hawkmoon-combat.js"; +import { HawkmoonCommands } from "./hawkmoon-commands.js"; + +/* -------------------------------------------- */ +export class HawkmoonUtility { + + + /* -------------------------------------------- */ + static async init() { + Hooks.on('renderChatLog', (log, html, data) => HawkmoonUtility.chatListeners(html)) + Hooks.on("getChatLogEntryContext", (html, options) => HawkmoonUtility.chatRollMenu(html, options)) + + Hooks.on("getCombatTrackerEntryContext", (html, options) => { + HawkmoonUtility.pushInitiativeOptions(html, options); + }) + Hooks.on("dropCanvasData", (canvas, data) => { + HawkmoonUtility.dropItemOnToken(canvas, data) + }); + + this.rollDataStore = {} + this.defenderStore = {} + HawkmoonCommands.init(); + + Handlebars.registerHelper('count', function (list) { + return list.length; + }) + Handlebars.registerHelper('includes', function (array, val) { + return array.includes(val); + }) + Handlebars.registerHelper('upper', function (text) { + return text.toUpperCase(); + }) + Handlebars.registerHelper('lower', function (text) { + return text.toLowerCase() + }) + Handlebars.registerHelper('upperFirst', function (text) { + if (typeof text !== 'string') return text + return text.charAt(0).toUpperCase() + text.slice(1) + }) + Handlebars.registerHelper('notEmpty', function (list) { + return list.length > 0; + }) + Handlebars.registerHelper('mul', function (a, b) { + return parseInt(a) * parseInt(b); + }) + + } + + /* -------------------------------------------- */ + static getModificateurOptions() { + let opt = [] + for (let i = -15; i <= 15; i++) { + opt.push(``) + } + return opt.concat("\n") + } + + /* -------------------------------------------- */ + static getPointAmeOptions() { + let opt = [] + for (let i = 1; i <= 20; i++) { + opt.push(``) + } + return opt.concat("\n") + } + + /* -------------------------------------------- */ + static getAttributs() { + return { adr: "Adresse", pui: "Puissance", cla: "Clairvoyance", pre: "Présence", tre: "Trempe" } + } + /* -------------------------------------------- */ + static pushInitiativeOptions(html, options) { + } + + /* -------------------------------------------- */ + static getSkills() { + return this.skills + } + + /* -------------------------------------------- */ + static async ready() { + const skills = await HawkmoonUtility.loadCompendium("fvtt-hawkmoon-cyd.skills") + this.skills = skills.map(i => i.toObject()) + } + + /* -------------------------------------------- */ + static async loadCompendiumData(compendium) { + const pack = game.packs.get(compendium); + return await pack?.getDocuments() ?? []; + } + + /* -------------------------------------------- */ + static async loadCompendium(compendium, filter = item => true) { + let compendiumData = await HawkmoonUtility.loadCompendiumData(compendium); + return compendiumData.filter(filter); + } + + /* -------------------------------------------- */ + static getOptionsStatusList() { + return this.optionsStatusList; + } + /* -------------------------------------------- */ + static async chatListeners(html) { + + html.on("click", '.predilection-reroll', async event => { + let predIdx = $(event.currentTarget).data("predilection-index") + let messageId = HawkmoonUtility.findChatMessageId(event.currentTarget) + let message = game.messages.get(messageId) + let rollData = message.getFlag("world", "mournblade-roll") + let actor = game.actors.get(rollData.actorId) + await actor.setPredilectionUsed(rollData.competence._id, predIdx) + rollData.competence = duplicate(actor.getCompetence(rollData.competence._id)) + HawkmoonUtility.rollHawkmoon(rollData) + }) + } + + /* -------------------------------------------- */ + static async preloadHandlebarsTemplates() { + + const templatePaths = [ + 'systems/fvtt-hawkmoon-cyd/templates/editor-notes-gm.html', + 'systems/fvtt-hawkmoon-cyd/templates/partial-item-description.html', + 'systems/fvtt-hawkmoon-cyd/templates/partial-list-niveau.html' + ] + return loadTemplates(templatePaths); + } + + /* -------------------------------------------- */ + static removeChatMessageId(messageId) { + if (messageId) { + game.messages.get(messageId)?.delete(); + } + } + + static findChatMessageId(current) { + return HawkmoonUtility.getChatMessageId(HawkmoonUtility.findChatMessage(current)); + } + + static getChatMessageId(node) { + return node?.attributes.getNamedItem('data-message-id')?.value; + } + + static findChatMessage(current) { + return HawkmoonUtility.findNodeMatching(current, it => it.classList.contains('chat-message') && it.attributes.getNamedItem('data-message-id')) + } + + static findNodeMatching(current, predicate) { + if (current) { + if (predicate(current)) { + return current; + } + return HawkmoonUtility.findNodeMatching(current.parentElement, predicate); + } + return undefined; + } + + /* -------------------------------------------- */ + static createDirectOptionList(min, max) { + let options = {}; + for (let i = min; i <= max; i++) { + options[`${i}`] = `${i}`; + } + return options; + } + + /* -------------------------------------------- */ + static buildListOptions(min, max) { + let options = "" + for (let i = min; i <= max; i++) { + options += `` + } + return options; + } + + /* -------------------------------------------- */ + static getTarget() { + if (game.user.targets && game.user.targets.size == 1) { + for (let target of game.user.targets) { + return target; + } + } + return undefined; + } + + /* -------------------------------------------- */ + static getDefenseState(actorId) { + return this.defenderStore[actorId]; + } + + /* -------------------------------------------- */ + static updateRollData(rollData) { + + let id = rollData.rollId; + let oldRollData = this.rollDataStore[id] || {}; + let newRollData = mergeObject(oldRollData, rollData); + this.rollDataStore[id] = newRollData; + } + /* -------------------------------------------- */ + static saveRollData(rollData) { + game.socket.emit("system.fvtt-hawkmoon-cyd", { + name: "msg_update_roll", data: rollData + }); // Notify all other clients of the roll + this.updateRollData(rollData); + } + + /* -------------------------------------------- */ + static getRollData(id) { + return this.rollDataStore[id]; + } + + /* -------------------------------------------- */ + static onSocketMesssage(msg) { + //console.log("SOCKET MESSAGE", msg.name, game.user.character.id, msg.data.defenderId); + if (msg.name == "msg_update_defense_state") { + this.updateDefenseState(msg.data.defenderId, msg.data.rollId); + } + if (msg.name == "msg_update_roll") { + this.updateRollData(msg.data); + } + } + + /* -------------------------------------------- */ + static chatDataSetup(content, modeOverride, isRoll = false, forceWhisper) { + let chatData = { + user: game.user.id, + rollMode: modeOverride || game.settings.get("core", "rollMode"), + content: content + }; + + if (["gmroll", "blindroll"].includes(chatData.rollMode)) chatData["whisper"] = ChatMessage.getWhisperRecipients("GM").map(u => u.id); + if (chatData.rollMode === "blindroll") chatData["blind"] = true; + else if (chatData.rollMode === "selfroll") chatData["whisper"] = [game.user]; + + if (forceWhisper) { // Final force ! + chatData["speaker"] = ChatMessage.getSpeaker(); + chatData["whisper"] = ChatMessage.getWhisperRecipients(forceWhisper); + } + + return chatData; + } + + /* -------------------------------------------- */ + static async showDiceSoNice(roll, rollMode) { + if (game.modules.get("dice-so-nice")?.active) { + if (game.dice3d) { + let whisper = null; + let blind = false; + rollMode = rollMode ?? game.settings.get("core", "rollMode"); + switch (rollMode) { + case "blindroll": //GM only + blind = true; + case "gmroll": //GM + rolling player + whisper = this.getUsers(user => user.isGM); + break; + case "roll": //everybody + whisper = this.getUsers(user => user.active); + break; + case "selfroll": + whisper = [game.user.id]; + break; + } + await game.dice3d.showForRoll(roll, game.user, true, whisper, blind); + } + } + } + + /* -------------------------------------------- */ + static computeResult(rollData) { + if (rollData.mainDice == "1d20") { + let diceValue = rollData.roll.terms[0].results[0].result + diceValue *= (rollData.doubleD20) ? 2 : 1 + //console.log("PAIR/IMP", diceValue) + if (diceValue % 2 == 1) { + //console.log("PAIR/IMP2", diceValue) + rollData.finalResult -= rollData.roll.terms[0].results[0].result // Substract value + if (diceValue == 1 || diceValue == 11) { + rollData.isDramatique = true + rollData.isSuccess = false + } + } + } + + //console.log("Result : ", rollData) + if (rollData.difficulte > 0 && !rollData.isDramatique) { + rollData.isSuccess = (rollData.finalResult >= rollData.difficulte) + rollData.isHeroique = ((rollData.finalResult - rollData.difficulte) >= 10) + rollData.isDramatique = ((rollData.finalResult - rollData.difficulte) <= -10) + } + } + + /* -------------------------------------------- */ + static async rollHawkmoon(rollData) { + + let actor = game.actors.get(rollData.actorId) + if (rollData.attrKey == "tochoose") { // No attr selected, force address + rollData.attrKey = "adr" + } + if (!rollData.attr) { + rollData.actionImg = "systems/fvtt-hawkmoon-cyd/assets/icons/" + actor.system.attributs[rollData.attrKey].labelnorm + ".webp" + rollData.attr = duplicate(actor.system.attributs[rollData.attrKey]) + } + + rollData.diceFormula = rollData.mainDice + if (rollData.doubleD20) { // Multiply result ! + rollData.diceFormula += "*2" + if (!rollData.isReroll) { + actor.changeEclat(-1) + } + } + //console.log("BEFORE COMP", rollData) + if (rollData.competence) { + rollData.predilections = duplicate(rollData.competence.system.predilections.filter(pred => !pred.used) || []) + let compmod = (rollData.competence.system.niveau == 0) ? -3 : 0 + rollData.diceFormula += `+${rollData.attr.value}+${rollData.competence.system.niveau}+${rollData.modificateur}+${compmod}` + } else { + rollData.diceFormula += `+${rollData.attr.value}*2+${rollData.modificateur}` + } + + if (rollData.arme && rollData.arme.type == "arme") { + rollData.diceFormula += `+${rollData.arme.system.bonusmaniementoff}` + } + + if (rollData.rune) { + rollData.runeduree = Math.ceil((rollData.runeame + 3) / 3) + if (rollData.runemode == "inscrire") { + rollData.runeduree *= 2 + } + if (rollData.runemode == "prononcer") { + rollData.runeduree = 1 + } + } + + let myRoll = new Roll(rollData.diceFormula).roll({ async: false }) + await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode")) + rollData.roll = myRoll + console.log(">>>> ", myRoll) + + rollData.finalResult = myRoll.total + this.computeResult(rollData) + + if (rollData.rune) { + let subAme = rollData.runeame + if (rollData.isEchec && !rollData.isDramatique) { + subAme = Math.ceil((subAme + 1) / 2) + } + actor.subPointsAme(rollData.runemode, subAme) + } + + this.createChatWithRollMode(rollData.alias, { + content: await renderTemplate(`systems/fvtt-hawkmoon-cyd/templates/chat-generic-result.html`, rollData) + }, rollData) + + } + + /* -------------------------------------------- */ + static async bonusRollHawkmoon(rollData) { + rollData.bonusFormula = rollData.addedBonus + + let bonusRoll = new Roll(rollData.bonusFormula).roll({ async: false }) + await this.showDiceSoNice(bonusRoll, game.settings.get("core", "rollMode")); + rollData.bonusRoll = bonusRoll + + rollData.finalResult += rollData.bonusRoll.total + + this.computeResult(rollData) + + this.createChatWithRollMode(rollData.alias, { + content: await renderTemplate(`systems/fvtt-hawkmoon-cyd/templates/chat-generic-result.html`, rollData) + }, rollData) + + } + + /* -------------------------------------------- */ + static getUsers(filter) { + return game.users.filter(filter).map(user => user.data._id); + } + + /* -------------------------------------------- */ + static getWhisperRecipients(rollMode, name) { + switch (rollMode) { + case "blindroll": return this.getUsers(user => user.isGM); + case "gmroll": return this.getWhisperRecipientsAndGMs(name); + case "selfroll": return [game.user.id]; + } + return undefined; + } + /* -------------------------------------------- */ + static getWhisperRecipientsAndGMs(name) { + let recep1 = ChatMessage.getWhisperRecipients(name) || []; + return recep1.concat(ChatMessage.getWhisperRecipients('GM')); + } + + /* -------------------------------------------- */ + static blindMessageToGM(chatOptions) { + let chatGM = duplicate(chatOptions); + chatGM.whisper = this.getUsers(user => user.isGM); + chatGM.content = "Blinde message of " + game.user.name + "
" + chatOptions.content; + console.log("blindMessageToGM", chatGM); + game.socket.emit("system.fvtt-weapons-of-the-gods", { msg: "msg_gm_chat_message", data: chatGM }); + } + + /* -------------------------------------------- */ + static async searchItem(dataItem) { + let item; + if (dataItem.pack) { + item = await fromUuid("Compendium." + dataItem.pack + "." + dataItem.id); + } else { + item = game.items.get(dataItem.id) + } + return item + } + + /* -------------------------------------------- */ + static split3Columns(data) { + + let array = [[], [], []]; + if (data == undefined) return array; + + let col = 0; + for (let key in data) { + let keyword = data[key]; + keyword.key = key; // Self-reference + array[col].push(keyword); + col++; + if (col == 3) col = 0; + } + return array; + } + + /* -------------------------------------------- */ + static async createChatMessage(name, rollMode, chatOptions, rollData = undefined) { + switch (rollMode) { + case "blindroll": // GM only + if (!game.user.isGM) { + this.blindMessageToGM(chatOptions); + + chatOptions.whisper = [game.user.id]; + chatOptions.content = "Message only to the GM"; + } + else { + chatOptions.whisper = this.getUsers(user => user.isGM); + } + break; + default: + chatOptions.whisper = this.getWhisperRecipients(rollMode, name); + break; + } + chatOptions.alias = chatOptions.alias || name + let msg = await ChatMessage.create(chatOptions) + console.log("=======>", rollData) + msg.setFlag("world", "mournblade-roll", rollData) + } + + /* -------------------------------------------- */ + static getBasicRollData() { + let rollData = { + rollId: randomID(16), + rollMode: game.settings.get("core", "rollMode"), + modificateursOptions: this.getModificateurOptions(), + pointAmeOptions: this.getPointAmeOptions(), + difficulte: 0, + modificateur: 0, + } + HawkmoonUtility.updateWithTarget(rollData) + return rollData + } + + /* -------------------------------------------- */ + static updateWithTarget(rollData) { + let target = HawkmoonUtility.getTarget() + if (target) { + rollData.defenderTokenId = target.id + let defender = game.canvas.tokens.get(rollData.defenderTokenId).actor + rollData.armeDefense = defender.getBestDefenseValue() + if ( rollData.armeDefense) { + rollData.difficulte = rollData.armeDefense.system.totalDefensif + } else { + ui.notifications.warn("Aucune arme de défense équipée, difficulté manuelle à positionner.") + } + } + } + + /* -------------------------------------------- */ + static createChatWithRollMode(name, chatOptions, rollData = undefined) { + this.createChatMessage(name, game.settings.get("core", "rollMode"), chatOptions, rollData) + } + + /* -------------------------------------------- */ + static applyBonneAventureRoll(li, changed, addedBonus) { + let msgId = li.data("message-id") + let msg = game.messages.get(msgId) + if (msg) { + let rollData = msg.getFlag("world", "mournblade-roll") + let actor = game.actors.get(rollData.actorId) + actor.changeBonneAventure(changed) + rollData.isReroll = true + rollData.textBonus = "Bonus de Points d'Aventure" + if (addedBonus == "reroll") { + HawkmoonUtility.rollHawkmoon(rollData) + } else { + rollData.addedBonus = addedBonus + HawkmoonUtility.bonusRollHawkmoon(rollData) + } + } + } + + /* -------------------------------------------- */ + static applyEclatRoll(li, changed, addedBonus) { + let msgId = li.data("message-id") + let msg = game.messages.get(msgId) + if (msg) { + let rollData = msg.getFlag("world", "mournblade-roll") + let actor = game.actors.get(rollData.actorId) + actor.changeEclat(changed) + rollData.isReroll = true + rollData.textBonus = "Bonus d'Eclat" + rollData.addedBonus = addedBonus + HawkmoonUtility.bonusRollHawkmoon(rollData) + } + } + + /* -------------------------------------------- */ + static chatRollMenu(html, options) { + let canApply = li => canvas.tokens.controlled.length && li.find(".mournblade-roll").length + let canApplyBALoyal = function (li) { + let message = game.messages.get(li.attr("data-message-id")) + let rollData = message.getFlag("world", "mournblade-roll") + let actor = game.actors.get(rollData.actorId) + return (!rollData.isReroll && actor.getBonneAventure() > 0 && actor.getAlignement() == "loyal") + } + let canApplyPELoyal = function (li) { + let message = game.messages.get(li.attr("data-message-id")) + let rollData = message.getFlag("world", "mournblade-roll") + let actor = game.actors.get(rollData.actorId) + return (!rollData.isReroll && actor.getEclat() > 0 && actor.getAlignement() == "loyal") + } + let canApplyBAChaotique = function (li) { + let message = game.messages.get(li.attr("data-message-id")) + let rollData = message.getFlag("world", "mournblade-roll") + let actor = game.actors.get(rollData.actorId) + return (!rollData.isReroll && actor.getBonneAventure() > 0 && actor.getAlignement() == "chaotique") + } + let canApplyBAChaotique3 = function (li) { + let message = game.messages.get(li.attr("data-message-id")) + let rollData = message.getFlag("world", "mournblade-roll") + let actor = game.actors.get(rollData.actorId) + return (!rollData.isReroll && actor.getBonneAventure() > 2 && actor.getAlignement() == "chaotique") + } + let canApplyPEChaotique = function (li) { + let message = game.messages.get(li.attr("data-message-id")) + let rollData = message.getFlag("world", "mournblade-roll") + let actor = game.actors.get(rollData.actorId) + return (!rollData.isReroll && actor.getEclat() > 0 && actor.getAlignement() == "chaotique") + } + let hasPredilection = function (li) { + let message = game.messages.get(li.attr("data-message-id")) + let rollData = message.getFlag("world", "mournblade-roll") + let actor = game.actors.get(rollData.actorId) + if (rollData.competence) { + let nbPred = rollData.competence.data.predilections.filter(pred => !pred.used).length + return (!rollData.isReroll && rollData.competence && nbPred > 0) + } + return false + } + let canCompetenceDouble = function (li) { + let message = game.messages.get(li.attr("data-message-id")) + let rollData = message.getFlag("world", "mournblade-roll") + let actor = game.actors.get(rollData.actorId) + if (rollData.competence) { + return rollData.competence.data.doublebonus + } + return false + } + options.push( + { + name: "Ajouer +3 (1 point de Bonne Aventure)", + icon: "", + condition: canApply && canApplyBALoyal, + callback: li => HawkmoonUtility.applyBonneAventureRoll(li, -1, "+3") + } + ) + options.push( + { + name: "Ajouer +6 (1 point de Bonne Aventure)", + icon: "", + condition: canApply && canApplyBALoyal && canCompetenceDouble, + callback: li => HawkmoonUtility.applyBonneAventureRoll(li, -1, "+6") + } + ) + options.push( + { + name: "Ajouer +1d6 (1 point de Bonne Aventure)", + icon: "", + condition: canApply && canApplyBAChaotique, + callback: li => HawkmoonUtility.applyBonneAventureRoll(li, -1, "+1d6") + } + ) + options.push( + { + name: "Ajouer +2d6 (1 point de Bonne Aventure)", + icon: "", + condition: canApply && canApplyBAChaotique && canCompetenceDouble, + callback: li => HawkmoonUtility.applyBonneAventureRoll(li, -1, "+2d6") + } + ) + options.push( + { + name: "Relancer le dé (3 points de Bonne Aventure)", + icon: "", + condition: canApply && canApplyBAChaotique3, + callback: li => HawkmoonUtility.applyBonneAventureRoll(li, -3, "reroll") + } + ) + options.push( + { + name: "Ajouter +10 (1 Point d'Eclat)", + icon: "", + condition: canApply && canApplyPELoyal, + callback: li => HawkmoonUtility.applyEclatRoll(li, -1, "+10") + } + ) + options.push( + { + name: "Ajouter +20 (1 Point d'Eclat)", + icon: "", + condition: canApply && canApplyPELoyal && canCompetenceDouble, + callback: li => HawkmoonUtility.applyEclatRoll(li, -1, "+20") + } + ) + return options + } + + /* -------------------------------------------- */ + static async confirmDelete(actorSheet, li) { + let itemId = li.data("item-id"); + let msgTxt = "

Are you sure to remove this Item ?"; + let buttons = { + delete: { + icon: '', + label: "Yes, remove it", + callback: () => { + actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]); + li.slideUp(200, () => actorSheet.render(false)); + } + }, + cancel: { + icon: '', + label: "Cancel" + } + } + msgTxt += "

"; + let d = new Dialog({ + title: "Confirm removal", + content: msgTxt, + buttons: buttons, + default: "cancel" + }); + d.render(true); + } + +} \ No newline at end of file diff --git a/styles/simple.css b/styles/simple.css new file mode 100644 index 0000000..ce25bb4 --- /dev/null +++ b/styles/simple.css @@ -0,0 +1,1372 @@ + /* ==================== (A) Fonts ==================== */ + /* ==================== (A) Fonts ==================== */ + @font-face { + font-family: "Charlemagne"; + src: url('../assets/fonts/CharlemagneStd-Bold.otf') format("otf"); + } + + :root { + /* =================== 1. ACTOR SHEET FONT STYLES =========== */ + --window-header-font-family: Charlemagne; + --window-header-title-font-size: 1.1rem; + --window-header-title-font-weight: normal; + --window-header-title-color: #f5f5f5; + + --major-button-font-family: Charlemagne; + --major-button-font-size: 1.05rem; + --major-button-font-weight: normal; + --major-button-color: #dadada; + + --tab-header-font-family: Charlemagne; + --tab-header-font-size: 1.0rem; + --tab-header-font-weight: 700; + --tab-header-color: #403f3e; + --tab-header-color-active: #4a0404; + + --actor-input-font-size: 0.8rem; + --actor-input-font-weight: 500; + --actor-input-color: black; + + --actor-label-font-size: 0.8rem; + --actor-label-font-weight: 700; + --actor-label-color: #464331c4; + + /* =================== 2. DEBUGGING HIGHLIGHTERS ============ */ + --debug-background-color-red: #ff000054; + --debug-background-color-blue: #1d00ff54; + --debug-background-color-green: #54ff0054; + + --debug-box-shadow-red: inset 0 0 2px red; + --debug-box-shadow-blue: inset 0 0 2px blue; + --debug-box-shadow-green: inset 0 0 2px green; + } + +/*@import url("https://fonts.googleapis.com/css2?family=Martel:wght@400;800&family=Roboto:wght@300;400;500&display=swap");*/ +/* Global styles & Font */ +.window-app { + font-family: Charlemagne; + text-align: justify; + font-size: 12px; + letter-spacing: 1px; +} + +/* Fonts */ +.sheet header.sheet-header h1 input, .window-app .window-header, #actors .directory-list, #navigation #scene-list .scene.nav-item { + font-family: "Charlemagne"; + font-size: 0.8rem; +} /* For title, sidebar character and scene */ +.sheet nav.sheet-tabs { + font-family: "Charlemagne"; + font-size: 0.8rem; +} /* For nav and title */ +.window-app input, .fvtt-hawkmoon-cyd .item-form, .sheet header.sheet-header .flex-group-center.flex-compteurs, .sheet header.sheet-header .flex-group-center.flex-fatigue, select, button, .item-checkbox, #sidebar, #players, #navigation #nav-toggle { + font-size: 0.8rem; +} + +.window-header{ + background: rgba(0,0,0,0.75); +} + +.window-app.sheet .window-content { + margin: 0; + padding: 0; +} +.strong-text{ + font-weight: bold; +} + +.tabs .item.active, .blessures-list li ul li:first-child:hover, a:hover { + text-shadow: 1px 0px 0px #ff6600; +} + +.rollable:hover, .rollable:focus { + color: #000; + text-shadow: 0 0 10px red; + cursor: pointer; +} +input:disabled { + color:#1c2058; +} +select:disabled { + color:#1c2058; +} +table {border: 1px solid #7a7971;} + +.grid, .grid-2col { + display: grid; + grid-column: span 2 / span 2; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + margin: 10px 0; + padding: 0; +} + +.grid-3col { + grid-column: span 3 / span 3; + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.grid-4col { + grid-column: span 4 / span 4; + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.grid-5col { + grid-column: span 5 / span 5; + grid-template-columns: repeat(5, minmax(0, 1fr)); +} + +.grid-6col { + grid-column: span 5 / span 5; + grid-template-columns: repeat(5, minmax(0, 1fr)); +} + +.grid-7col { + grid-column: span 7 / span 7; + grid-template-columns: repeat(7, minmax(0, 1fr)); +} + +.grid-8col { + grid-column: span 8 / span 8; + grid-template-columns: repeat(8, minmax(0, 1fr)); +} + +.grid-9col { + grid-column: span 9 / span 9; + grid-template-columns: repeat(9, minmax(0, 1fr)); +} + +.grid-10col { + grid-column: span 10 / span 10; + grid-template-columns: repeat(10, minmax(0, 1fr)); +} + +.grid-11col { + grid-column: span 11 / span 11; + grid-template-columns: repeat(11, minmax(0, 1fr)); +} + +.grid-12col { + grid-column: span 12 / span 12; + grid-template-columns: repeat(12, minmax(0, 1fr)); +} + +.flex-group-center, +.flex-group-left, +.flex-group-right { + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + text-align: center; + padding: 5px; +} + +.flex-group-left { + -webkit-box-pack: start; + -ms-flex-pack: start; + justify-content: flex-start; + text-align: left; +} + +.flex-group-right { + -webkit-box-pack: end; + -ms-flex-pack: end; + justify-content: flex-end; + text-align: right; +} + +.flex-center { + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + text-align: center; +} + +.table-create-actor { + font-size: 0.8rem; +} + +.flex-between { + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +.flex-shrink { + flex: 'flex-shrink' ; +} + +/* Styles limited to foundryvtt-vadentis sheets */ + +.fvtt-hawkmoon-cyd .sheet-header { + -webkit-box-flex: 0; + -ms-flex: 0 0 210px; + flex: 0 0 210px; + overflow: hidden; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -webkit-box-pack: start; + -ms-flex-pack: start; + justify-content: flex-start; + margin-bottom: 10px; +} + +.fvtt-hawkmoon-cyd .sheet-header .profile-img { + -webkit-box-flex: 0; + -ms-flex: 0 0 128px; + flex: 0 0 128px; + height: 128px; + width: 128px; + margin-right: 10px; + object-fit: cover; + object-position: 50% 0; +} + +.button-img { + vertical-align: baseline; + width: 8%; + height: 8%; + max-height: 48px; + border-width: 0; + border: 1px solid rgba(0, 0, 0, 0); +} +.button-img:hover { + color: rgba(255, 255, 128, 0.7); + border: 1px solid rgba(255, 128, 0, 0.8); + cursor: pointer; +} + +.button-effect-img { + vertical-align: baseline; + width: 16px; + max-height: 16px; + height: 16; + border-width: 0; +} + +.small-button-container { + height: 16px; + width: 16px; + border: 0; + vertical-align: bottom; +} + +.fvtt-hawkmoon-cyd .sheet-header .header-fields { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.fvtt-hawkmoon-cyd .sheet-header h1.charname { + height: 50px; + padding: 0px; + margin: 5px 0; + border-bottom: 0; + font-weight: bold; + font-size: 2rem; + font-family: "Charlemagne"; +} + +.fvtt-hawkmoon-cyd .sheet-header h1.charname input { + width: 100%; + height: 100%; + margin: 0; + font-weight: bold; + font-family: "Charlemagne"; + font-size: 2rem; +} + +.fvtt-hawkmoon-cyd .sheet-tabs { + -webkit-box-flex: 0; + -ms-flex: 0; + flex: 0; +} + +.fvtt-hawkmoon-cyd .sheet-body, +.fvtt-hawkmoon-cyd .sheet-body .tab, +.fvtt-hawkmoon-cyd .sheet-body .tab .editor { + height: 100%; + font-size: 0.8rem; +} + +.editor { + border: 2; + height: 300px; + padding: 0 3px; +} + +.medium-editor { + border: 2; + height: 240px; + padding: 0 3px; +} + +.small-editor { + border: 2; + height: 120px; + padding: 0 3px; +} + +.fvtt-hawkmoon-cyd .tox .tox-editor-container { + background: #fff; +} + +.fvtt-hawkmoon-cyd .tox .tox-edit-area { + padding: 0 8px; +} + +.fvtt-hawkmoon-cyd .resource-label { + font-weight: bold; + text-transform: uppercase; +} + +.fvtt-hawkmoon-cyd .tabs { + height: 40px; + border-top: 1px solid #AAA; + border-bottom: 1px solid #AAA; + color: #000000; +} + +.fvtt-hawkmoon-cyd .tabs .item { + line-height: 40px; + font-weight: bold; +} + +.fvtt-hawkmoon-cyd .tabs .item.active { + text-decoration: underline; + text-shadow: none; +} + +.fvtt-hawkmoon-cyd .items-list { + list-style: none; + margin: 1px 0; + padding: 0; + overflow-y: auto; +} + +.fvtt-hawkmoon-cyd .items-list .item-header { + font-weight: bold; +} + +.fvtt-hawkmoon-cyd .items-list .item { + height: 30px; + line-height: 24px; + padding: 1px 0; + border-bottom: 1px solid #BBB; +} + +.fvtt-hawkmoon-cyd .items-list .item .item-image { + -webkit-box-flex: 0; + -ms-flex: 0 0 24px; + flex: 0 0 24px; + margin-right: 5px; +} + +.fvtt-hawkmoon-cyd .items-list .item img { + display: block; +} + +.fvtt-hawkmoon-cyd .items-list .item-name { + margin: 0; +} + +.fvtt-hawkmoon-cyd .items-list .item-controls { + -webkit-box-flex: 0; + -ms-flex: 0 0 86px; + flex: 0 0 86px; + text-align: right; +} + + +/* ======================================== */ +/* Sheet */ +.window-app.sheet .window-content .sheet-header{ + /*background: #011d33 url("../images/ui/fond1.webp") repeat left top;*/ + background: url("../assets/ui/pc_sheet_bg.webp") repeat left top; + /*color: rgba(168, 139, 139, 0.5);*/ +} + +.window-app.sheet .window-content .sheet-header input[type="text"], .window-app.sheet .window-content .sheet-header input[type="number"], .window-app.sheet .window-content .sheet-header input[type="password"], .window-app.sheet .window-content .sheet-header input[type="date"], .window-app.sheet .window-content .sheet-header input[type="time"] { + color: rgba(36, 37, 37, 0.75); + background: rgba(255, 255, 255, 0.05); + border: 0 none; + margin-bottom: 0.25rem; +} + +.window-app .window-content, .window-app.sheet .window-content .sheet-body{ + font-size: 0.8rem; + background: url("../assets/ui/pc_sheet_bg.webp") repeat left top; +} + +/* background: rgba(245,245,240,0.6) url("../images/ui/sheet_background.webp") left top;*/ + +section.sheet-body{padding: 0.25rem 0.5rem;} + +.sheet header.sheet-header .profile-img { + object-fit: cover; + object-position: 50% 0; + margin: 0.5rem 0 0.5rem 0.5rem; + padding: 0; +} + +.sheet nav.sheet-tabs { + font-size: 0.70rem; + font-weight: bold; + height: 3rem; + flex: 0 0 3rem; + margin: 0; + padding: 0 0 0 0.25rem; + text-align: center; + text-transform: uppercase; + line-height: 1.5rem; + border-top: 0 none; + border-bottom: 0 none; + background-color:black; + color:beige; +} + +/* background: rgb(245,245,240) url("../images/ui/fond4.webp") repeat left top;*/ + +nav.sheet-tabs .item { + position: relative; + padding: 0 0.25rem; +} + +nav.sheet-tabs .item:after { + content: ""; + position: absolute; + top: 0; + right: 0; + height: 2rem; + width: 1px; + border-right: 1px dashed rgba(52, 52, 52, 0.25); +} + +.sheet .tab[data-tab] { + padding: 0; +} + +section.sheet-body:after { + content: ""; + display: block; + clear: both; +} + +.sheet header.sheet-header .flex-compteurs {text-align: right;} +.sheet header.sheet-header .resource-content {width: 2rem;} + +.select-diff { + display: inline-block; + text-align: left; + width: 50px; +} + +.window-app.sheet .window-content .tooltip:hover .tooltiptext { + top: 2rem; + left: 2rem; + margin: 0; + padding: 0.25rem; +} + +.window-app.sheet .window-content .carac-value, .window-app.sheet .window-content .competence-xp { + margin: 0.05rem; + flex-basis: 3rem; + text-align: center; +} + +/* ======================================== */ +/* Global UI elements */ + +/* ======================================== */ + +h1, h2, h3, h4 { + font-weight: bold; +} + +ul, ol { + margin: 0; + padding: 0; +} +ul, li { + list-style-type: none; +} + +.sheet li { + margin: 0.010rem; + padding: 0.25rem; +} +.header-fields li { + margin: 0; + padding: 0; +} + +.alterne-list > .list-item:hover { + background: rgba(100, 100, 50, 0.25); +} +.alterne-list > .list-item:nth-child(even) { + background: rgba(80, 60, 0, 0.10); +} +.alterne-list > .list-item:nth-child(odd) { + background: rgb(160, 130, 100, 0.05); +} + +.specialisation-label { + font-size: 0.8rem; +} + +.carac-label, +.attr-label { + font-weight: bold; +} + +.list-item { + margin: 0.125rem; + box-shadow: inset 0px 0px 1px #00000096; + border-radius: 0.25rem; + padding: 0.125rem; + flex: 1 1 5rem; +} +.item-display-show { + display: block; +} +.item-display-hide { + display: none; +} +.conteneur-type { + background: rgb(200, 10, 100, 0.25); +} + +.item-quantite { + margin-left: 0.5rem; +} + +.list-item-margin1 { + margin-left: 1rem; +} +.list-item-margin2 { + margin-left: 2rem; +} +.list-item-margin3 { + margin-left: 3rem; +} +.list-item-margin4 { + margin-left: 4rem; +} + +.sheet-competence-img { + width: 24px; + height: 24px; + flex-grow: 0; + margin-right: 0.25rem; +} +.competence-column { + flex-direction: column; + align-content: flex-start; + justify-content: flex-start; + flex-grow: 0; + flex-basis: 1; +} +.competence-header { + align-content: flex-start; + justify-content: flex-start; + font-weight: bold; + flex-grow: 0; +} +.secondaire-label, +.arme-label, +.generic-label, +.competence-label, +.devotion-label, +.sort-label, +.technique-label, +.stat-label, +.arme-label, +.armure-label, +.equipement-label, +.description-label { + flex-grow: 2; + margin-left: 4px; +} +.roll-dialog-label { + margin: 4px 0; +} + +.short-label { + flex-grow: 1; +} +.keyword-label { + font-size: 0.85rem; +} + +.item-sheet-label { + flex-grow: 1; +} + +.item-text-long-line { + flex-grow: 3; +} + +.score-label { + flex-grow: 2; + align-content: center; +} + +.attribut-value, +.carac-value { + flex-grow: 0; + flex-basis: 64px; + margin-right: 4px; + margin-left: 4px; +} +.sante-value, +.competence-value { + flex-grow: 0; + flex-basis: 2rem; + margin-right: 0.25rem; + margin-left: 0.25rem; +} +.description-value { + flex-grow: 0; + flex-basis: 4rem; + margin-right: 0.25rem; + margin-left: 0.25rem; +} +.competence-xp { + flex-grow: 0; + flex-basis: 2rem; + margin-right: 0.25rem; + margin-left: 0.25rem; +} +.blessures-title { + font-weight: bold; +} +.alchimie-title { + font-weight: bold; +} +.blessure-data { + flex-direction: row; + align-content: flex-start; + justify-content: flex-start; +} +.blessures-soins { + flex-grow: 0; + flex-basis: 32px; + margin-right: 4px; + margin-left: 4px; +} +.blessures-loc { + flex-grow: 0; + flex-basis: 96px; + margin-right: 4px; + margin-left: 4px; +} +.pointsreve-value { + flex-grow: 0; + flex-basis: 64px; + margin-right: 4px; + margin-left: 4px; +} + +.input-sante-header, +.stress-style { + flex-grow: 0; + flex-basis: 64px; + margin-right: 4px; + margin-left: 4px; +} + +.small-label { + margin-top: 5px; +} + +.padd-right { + margin-right: 8px; +} +.padd-left { + margin-left: 8px; +} + +.stack-left { + align-items:center; + flex-shrink: 1; + flex-grow: 0; +} +.npc-stat-label { + flex-grow: 2; +} + +.packed-left { + white-space: nowrap; + flex-grow: 0; +} + +.input-numeric-short { + width: 40px; + max-width: 40px; + flex-grow: 0; + flex-shrink: 0; + flex-basis: 40px; + margin-right: 0.25rem; + margin-left: 0.25rem; +} + +.stats-table { + align-content: flex-start; +} + +/* ======================================== */ +.tokenhudext { + display: flex; + flex: 0 !important; + font-weight: 600; +} +.tokenhudext.left { + justify-content: flex-start; + flex-direction: column; + position: absolute; + top: 2.75rem; + right: 4rem; +} +.tokenhudext.right { + justify-content: flex-start; + flex-direction: column; + position: absolute; + top: 2.75rem; + left: 4rem; +} +.control-icon.tokenhudicon { + width: fit-content; + height: fit-content; + min-width: 6rem; + flex-basis: auto; + padding: 0; + line-height: 1rem; + margin: 0.25rem; +} +.control-icon.tokenhudicon.right { + margin-left: 8px; +} +#token-hud .status-effects.active{ + z-index: 2; +} +/* ======================================== */ +.item-checkbox { + height: 25px; + border: 1px solid #736953a6; + border-left: none; + font-weight: 500; + font-size: 1rem; + color: black; + padding-top: 5px; + margin-right: 0px; + width: 45px; + position: relative; + left: 0px; + text-align: center; +} + + +.flex-actions-bar { + flex-grow: 2; +} + +/* ======================================== */ +/* Sidebar CSS */ +#sidebar { + font-size: 1rem; + background-position: 100%; + color: rgba(220,220,220,0.75); +} + +/* background: rgb(105,85,65) url("../images/ui/texture_feuille_perso_onglets.webp") no-repeat right bottom;*/ + +#sidebar.collapsed { + height: 470px !important; +} + +#sidebar-tabs > .collapsed, #chat-controls .chat-control-icon { + color: rgba(220,220,220,0.75); + text-shadow: 1px 1px 0 rgba(0,0,0,0.75); +} + +.sidebar-tab .directory-list .entity { + border-top: 1px dashed rgba(0,0,0,0.25); + border-bottom: 0 none; + padding: 0.25rem 0; +} + +.sidebar-tab .directory-list .entity:hover { + background: rgba(0,0,0,0.05); + cursor: pointer; +} +.chat-message-header { + background: rgba(220,220,210,0.5); + font-size: 1.1rem; + height: 48px; + text-align: center; + vertical-align: middle; + display: flex; + align-items: center; +} + +.chat-message .message-header .flavor-text, .chat-message .message-header .whisper-to { + font-size: 0.9rem; +} +.chat-actor-name { + padding: 4px; +} + +.chat-img { + width: 64px; + height: 64px; +} + +.roll-dialog-header { + height: 52px; +} + +.actor-icon { + float: left; + width: 48px; + height: 48px; + padding: 2px 6px 2px 2px; +} + +.padding-dice { + padding-top: .2rem; + padding-bottom: .2rem; +} + +.dice-image { + box-sizing: border-box; + border: none; + border-radius: 0; + max-width: 100%; +} + +.dice-image-reroll { + background-color:rgba(115, 224, 115, 0.25); + border-color: #011d33; + box-sizing: border-box; + border: 1px; + border-radius: 0%; + max-width: 100%; +} + +.chat-dice { + width: 15%; + height: 15%; + font-size: 15px; + padding: 10px; + padding-bottom: 20px; + padding-top: .2rem; + padding-bottom: .2rem; +} + +.div-river-full { + height: 5rem; + align-items: flex-start; +} + +.div-river { + align-content: center; + margin-left: 8px; + align-content:space-around; + justify-content: space-around; +} + +.div-center { + align-self: center; +} + +.chat-message { + background: rgba(220,220,210,0.5); + font-size: 0.9rem; +} + +.chat-message.whisper { + background: rgba(220,220,210,0.75); + border: 2px solid #545469; +} +.chat-message .chat-icon { + border: 0; + padding: 2px 6px 2px 2px; + float: left; + width: 64px; + height: 64px; +} + +#sidebar-tabs { + flex: 0 0 32px; + box-sizing: border-box; + margin: 0 0 5px; + border-bottom: 1px solid rgba(0,0,0,0); + box-shadow: inset 0 0 2rem rgba(0,0,0,0.5); +} + +#sidebar-tabs > .item.active { + border: 1px solid rgba(114,98,72,1); + background: rgba(30, 25, 20, 0.75); + box-shadow: 0 0 6px inset rgba(114,98,72,1); +} + +#sidebar #sidebar-tabs i{ + width: 25px; + height: 25px; + display: inline-block; + background-position:center; + background-size:cover; + text-shadow: 1px 1px 0 rgba(0,0,0,0.75); + +} + +/*#sidebar #sidebar-tabs i.fa-comments:before, #sidebar #sidebar-tabs i.fa-fist-raised:before, #sidebar #sidebar-tabs i.fa-users:before, #sidebar #sidebar-tabs i.fa-map:before, #sidebar #sidebar-tabs i.fa-suitcase:before, #sidebar #sidebar-tabs i.fa-book-open:before, #sidebar #sidebar-tabs i.fa-th-list:before, #sidebar #sidebar-tabs i.fa-music:before, #sidebar #sidebar-tabs i.fa-atlas:before, #sidebar #sidebar-tabs i.fa-cogs:before {content: "";} +#sidebar #sidebar-tabs i.fa-comments {background: url("img/ui/icon_sidebar_chat.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-fist-raised {background: url("img/ui/icon_sidebar_fight.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-users {background: url("img/ui/icon_sidebar_actor.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-map {background: url("img/ui/icon_sidebar_scene.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-suitcase {background: url("img/ui/icon_sidebar_item.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-book-open {background: url("img/ui/icon_sidebar_journal.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-th-list {background: url("img/ui/icon_sidebar_rolltable.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-music {background: url("img/ui/icon_sidebar_music.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-atlas {background: url("img/ui/icon_sidebar_compendium.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-cogs {background: url("img/ui/icon_sidebar_settings.svg") no-repeat;} + +#combat #combat-controls { + box-shadow: inset 0 0 2rem rgba(0,0,0,0.5); +} +*/ + +/*--------------------------------------------------------------------------*/ +/* Control, Tool, hotbar & navigation */ + +#controls .scene-control, #controls .control-tool { + box-shadow: 0 0 3px #000; + margin: 0 0 8px; + border-radius: 0; + background: rgba(30, 25, 20, 1); + background-origin: padding-box; + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; +} + +#controls .scene-control.active, #controls .control-tool.active, #controls .scene-control:hover, #controls .control-tool:hover { + background: rgba(72, 46, 28, 1); + background-origin: padding-box; + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; + box-shadow: 0 0 3px #ff6400; +} + +#hotbar #action-bar #macro-list { + border: 1px solid rgba(72, 46, 28, 1); + box-shadow: 2px 2px 5px #000000; +} + +#hotbar #action-bar .macro { + border-image: url(img/ui/bg_control.jpg) 21 repeat; + border-image-slice: 6 6 6 6 fill; + border-image-width: 6px 6px 6px 6px; + border-image-outset: 0px 0px 0px 0px; + border-radius: 0px; +} + +#hotbar .bar-controls { + background: rgba(30, 25, 20, 1); + border: 1px solid rgba(72, 46, 28, 1); +} + +#players { + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; + background: rgba(30, 25, 20, 1); +} + +#navigation #scene-list .scene.nav-item.active { + background: rgba(72, 46, 28, 1); +} + +#navigation #scene-list .scene.nav-item { + background: rgba(30, 25, 20, 1); + background-origin: padding-box; + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; +} + +#navigation #scene-list .scene.view, #navigation #scene-list .scene.context { + background: rgba(72, 46, 28, 1); + background-origin: padding-box; + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; + box-shadow: 0 0 3px #ff6400; +} + +#navigation #nav-toggle { + background: rgba(30, 25, 20, 1); + background-origin: padding-box; + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; +} + +/* Tooltip container */ +.tooltip { + position: relative; + display: inline-block; + /*border-bottom: 1px dotted black; /* If you want dots under the hoverable text */ +} + +/* Tooltip text */ +.tooltip .tooltiptext { + text-align: left; + background: rgba(231, 229, 226, 0.9); + width: 150px; + padding: 3px 0; + font-size: 0.9rem; + + /* Position the tooltip text */ + top: 1px; + position: absolute; + z-index: 1; + + /* Fade in tooltip */ + visibility: hidden; + opacity: 0; + transition: opacity 0.3s; +} + +.tooltip .ttt-fatigue{ + width: 360px; + + background: rgba(30, 25, 20, 0.9); + border-image: url(img/ui/bg_control.jpg) 21 repeat; + border-image-slice: 6 6 6 6 fill; + border-image-width: 6px 6px 6px 6px; + border-image-outset: 0px 0px 0px 0px; + border-radius: 0px; + + font-size: 0.8rem; + padding: 3px 0; +} + +.tooltip .ttt-ajustements { + width: 150px; + background: rgba(220,220,210,0.95); + border-radius: 6px; + font-size: 0.9rem; + padding: 3px 0; +} + +.tooltip-nobottom { + border-bottom: unset; /* If you want dots under the hoverable text */ +} +.tooltip .ttt-xp { + width: 250px; + background: rgba(220,220,210,0.95); + border-radius: 6px; + font-size: 0.9rem; + padding: 3px 0; +} + +/* Show the tooltip text when you mouse over the tooltip container */ +.tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; +} + +.river-button { + box-shadow: inset 0px 1px 0px 0px #a6827e; + background: linear-gradient(to bottom, #21374afc 5%, #152833ab 100%); + background-color: #7d5d3b00; + border-radius: 3px; + border: 2px ridge #846109; + display: inline-block; + cursor: pointer; + color: #ffffff; + font-size: 0.8rem; + padding: 2px 4px 0px 4px; + text-decoration: none; + text-shadow: 0px 1px 0px #4d3534; + position: relative; + margin:4px; +} + +.chat-card-button { + box-shadow: inset 0px 1px 0px 0px #a6827e; + background: linear-gradient(to bottom, #21374afc 5%, #152833ab 100%); + background-color: #7d5d3b00; + border-radius: 3px; + border: 2px ridge #846109; + display: inline-block; + cursor: pointer; + color: #ffffff; + font-size: 0.8rem; + padding: 4px 4px 0px 4px; + text-decoration: none; + text-shadow: 0px 1px 0px #4d3534; + position: relative; + margin:2px; +} + +.chat-card-button:hover { + background: linear-gradient(to bottom, #800000 5%, #3e0101 100%); + background-color: red; +} +.chat-card-button:active { + position:relative; + top:1px; +} + +.button-sheet-roll { + box-shadow: inset 0px 1px 0px 0px #a6827e; + background: linear-gradient(to bottom, #21374afc 5%, #152833ab 60%); + background-color: #7d5d3b00; + border-radius: 4px; + border: 1px ridge #846109; + display: inline-block; + cursor: pointer; + color: #ffffff; + font-size: 0.8rem; + padding: 1px 1px 0px 1px; + text-decoration: none; + text-shadow: 0px 1px 0px #4d3534; + position: relative; + max-height:1.8rem; + max-width: 4rem; + margin-left:4px; +} + +.button-sheet-roll:hover { + background: linear-gradient(to bottom, #800000 5%, #3e0101 100%); + background-color: red; +} +.button-sheet-roll:active { + position:relative; + top:1px; +} + +.plus-minus-button { + box-shadow: inset 0px 1px 0px 0px #a6827e; + background: linear-gradient(to bottom, #21374afc 5%, #152833ab 100%); + background-color: #7d5d3b00; + border-radius: 3px; + border: 2px ridge #846109; + display: inline-block; + cursor: pointer; + color: #ffffff; + padding: 2px 6px 0px 6px; + text-decoration: none; + text-shadow: 0px 1px 0px #4d3534; + position: relative; + margin:3px; +} + +.river-button:hover, +.plus-minus-button:hover, +.chat-card-button:hover { + background: linear-gradient(to bottom, #800000 5%, #3e0101 100%); + background-color: red; +} + +.plus-minus-button:active, +.chat-card-button:active { + position:relative; + top:1px; +} + +.plus-minus { + font-size: 0.9rem; + font-weight: bold; +} + +.ul-level1 { + padding-left: 2rem; +} + +.drop-equipment-effect, +.drop-power-effect, +.drop-perk-effect, +.drop-ability-effect, +.drop-effect-specaffected, +.drop-effect-spec, +.drop-ability-weapon, +.drop-ability-armor, +.drop-race-perk, +.drop-spec-perk, +.drop-ability-power, +.drop-ability-spec, +.drop-spec-power, +.drop-abilities, +.drop-optionnal-abilities, +.drop-specialperk1, +.drop-perk2, +.drop-spec1 , +.drop-spec2 { + background: linear-gradient(to bottom, #6c95b9fc 5%, #105177ab 100%); + background-color: #7d5d3b00; + border-radius: 3px; + border: 2px ridge #846109; +} + +.label-name { + padding-top: 7px; + padding-left: 4px; + margin-left: 4px; + min-width: 5rem; + max-width: 5rem; +} + +/*************************************************************/ +.competence-name { + padding-top: 7px; + padding-left: 4px; + margin-left: 4px; + flex-grow: 2; +} + +/*************************************************************/ +.competence-niveau { + flex-grow: 1; + min-width: 64px; + max-width: 64px; +} + +/*************************************************************/ +.arme-defensif { + padding-top: 7px; + padding-left: 4px; + margin-left: 4px; + text-align: center; + flex-grow: 2; +} + +/*************************************************************/ +.button-roll-competence { + min-width: 64px; + max-width: 64px; + background-color: rgba(211, 221, 187, 100); + padding-top: 7px; + padding-left: 4px; + margin-left: 4px; + margin-right: 4px; + border-radius: 12px; +} + +/*************************************************************/ +.button-roll-competence-empty { + min-width: 64px; + max-width: 64px; + padding-top: 7px; + padding-left: 4px; + margin-left: 4px; + margin-right: 4px; + border-radius: 12px; +} + +/*************************************************************/ +.item-name-img { + flex-grow:1; + max-width: 2rem; + min-width: 2rem; + max-height: 2rem; +} + +/*************************************************************/ +#pause +{ + font-size: 2rem; +} +#pause > h3 +{ + color: #CCC +} +#pause > img { + content: url(../assets/logos/mournblade_logo_chaos.webp); + height: 256px; + width: 256px; + top: -80px; + left: calc(50% - 132px); +} + +#logo { + content : url(../assets/logos/mournblade_logo_texte.webp); + width: 120px; + height: 40px; +} + +.dice-cell { + padding-left: 12px; + padding-right: 12px; + width: 60px; + text-align: center; +} + +.dice-formula, +.dice-total { + height: 54px; + position:relative; +} + +.item-name-label-header { + flex-grow:2; + max-width: 12rem; + min-width: 12rem; +} +.item-name-label { + flex-grow:2; + max-width: 10rem; + min-width: 10rem; +} +.item-name-label-level2 { + flex-grow:2; + max-width: 9rem; + min-width: 9rem; +} +.item-field-label-short { + flex-grow:1; + max-width: 4rem; + min-width: 4rem; +} +.item-field-label-medium { + flex-grow:1; + max-width: 6rem; + min-width: 6rem; +} +.item-field-label-long { + flex-grow:1; + max-width: 8rem; + min-width: 8rem; +} +.item-control-end { + align-self: flex-end; +} +.alternate-list { + margin-top: 4px; + flex-wrap: nowrap; +} +.item-filler { + flex-grow: 6; + flex-shrink: 7; +} +.item-controls-fixed { + min-width:3.2rem; + max-width: 3.2rem; +} \ No newline at end of file diff --git a/system.json b/system.json new file mode 100644 index 0000000..4c54fa7 --- /dev/null +++ b/system.json @@ -0,0 +1,134 @@ +{ + "id": "fvtt-hawkmoon-cyd", + "description": "Hawmoon RPG for FoundryVTT (CYD system - French)", + "version": "10.0.0", + "authors": [ + { + "name": "Uberwald/LeRatierBretonnien", + "flags": {} + } + ], + "esmodules": [ + "modules/hawkmon-main.js" + ], + "gridDistance": 5, + "gridUnits": "m", + "license": "LICENSE.txt", + "manifest": "https://www.uberwald.me/gitea/public/fvtt-hawkmoon-cyd/raw/branch/v10/system.json", + "download": "https://www.uberwald.me/gitea/public/fvtt-hawkmoon-cyd/archive/fvtt-hawkmoon-cyd-10.0.0.zip", + "packs": [ + { + "type": "Item", + "label": "Compétences", + "name": "skills", + "path": "packs/skills.db", + "system": "fvtt-hawkmoon-cyd", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Armes & Boucliers", + "name": "armes", + "path": "packs/armes.db", + "system": "fvtt-hawkmoon-cyd", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Protections", + "name": "protection", + "path": "packs/protection.db", + "system": "fvtt-hawkmoon-cyd", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Equipement", + "name": "equipement", + "path": "packs/equipement.db", + "system": "fvtt-hawkmoon-cyd", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Origines", + "name": "origines", + "path": "packs/origines.db", + "system": "fvtt-hawkmoon-cyd", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Héritages", + "name": "heritages", + "path": "packs/heritages.db", + "system": "fvtt-hawkmoon-cyd", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Métiers", + "name": "metiers", + "path": "packs/metiers.db", + "system": "fvtt-hawkmoon-cyd", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Tendances", + "name": "tendances", + "path": "packs/tendances.db", + "system": "fvtt-hawkmoon-cyd", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Traits chaotiques", + "name": "traits-chaotiques", + "path": "packs/traits-chaotiques.db", + "system": "fvtt-hawkmoon-cyd", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Runes", + "name": "runes", + "path": "packs/runes.db", + "system": "fvtt-hawkmoon-cyd", + "private": false, + "flags": {} + }, + { + "type": "RollTable", + "label": "Tables", + "name": "tables", + "path": "packs/tables.db", + "system": "fvtt-hawkmoon-cyd", + "private": false, + "flags": {} + } + ], + "primaryTokenAttribute": "secondary.health", + "secondaryTokenAttribute": "secondary.delirium", + "socket": true, + "styles": [ + "styles/simple.css" + ], + "title": "Mournblade", + "url": "https://www.uberwald.me/gitea/public/fvtt-hawkmoon-cyd", + "background": "systems/fvtt-hawkmoon-cyd/assets/ui/fond_hawkmoon.webp", + "compatibility": { + "minimum": "10", + "verified": "10", + "maximum": "10" + } +} \ No newline at end of file diff --git a/template.json b/template.json new file mode 100644 index 0000000..53fe431 --- /dev/null +++ b/template.json @@ -0,0 +1,264 @@ +{ + "Actor": { + "types": [ + "personnage" + ], + "templates": { + "biodata": { + "biodata": { + "name": "", + "age": 0, + "alignement": "", + "poids": "", + "taille": "", + "cheveux": "", + "sexe": "", + "yeux": "", + "description": "", + "amemultiplier": 2, + "notes": "", + "gmnotes": "" + } + }, + "core": { + "subactors": [], + "attributs": { + "adr": { + "label": "Adresse", + "labelnorm": "adresse", + "abbrev": "adr", + "value": 1 + }, + "pui": { + "label": "Puissance", + "labelnorm": "puissance", + "abbrev": "pui", + "value": 1 + }, + "cla": { + "label": "Clairvoyance", + "labelnorm": "clairvoyance", + "abbrev": "cla", + "value": 1 + }, + "pre": { + "label": "Présence", + "labelnorm": "presence", + "abbrev": "pre", + "value": 0 + }, + "tre": { + "label": "Trempe", + "labelnorm": "trempe", + "abbrev": "tre", + "value": 0 + } + }, + "bonneaventure": { + "base": 0, + "actuelle": 0 + }, + "experience": { + "value": 0 + }, + "eclat": { + "value": 0 + }, + "sante": { + "base": 0, + "bonus": 0, + "nonletaux": 0, + "letaux": 0, + "sequelles": "" + }, + "ame": { + "fullmax": 0, + "currentmax": 0, + "value": 0, + "traumatismes": "" + }, + "combat": { + "initbonus": 0, + "vitessebonus": 0, + "bonusdegats": 0, + "defensebonus": 0 + }, + "balance": { + "loi": 0, + "chaos": 0, + "aspect": 0, + "marge": 0, + "pointschaos": 0, + "pointsloi": 0 + } + }, + "npccore": { + "npctype": "", + "description": "" + } + }, + "personnage": { + "templates": [ + "biodata", + "core" + ] + }, + "pnj": { + "templates": [ + "npccore" + ] + } + }, + "Item": { + "templates": { + "base": { + "description": "" + } + }, + "types": [ + "arme", + "competence", + "protection", + "pacte", + "traitchaotique", + "monnaie", + "don", + "tendance", + "rune", + "equipement", + "capacite", + "origine", + "heritage", + "metier", + "runeeffect", + "bouclier" + ], + "runeeffect": { + "rune": "", + "mode": "", + "duree": "", + "pointame": 0, + "templates": [ + "base" + ] + }, + "origine": { + "templates": [ + "base" + ] + }, + "heritage": { + "templates": [ + "base" + ] + }, + "metier": { + "templates": [ + "base" + ] + }, + "capacite": { + "templates": [ + "base" + ] + }, + "equipement": { + "rarete": 0, + "prix": 0, + "templates": [ + "base" + ] + }, + "arme": { + "typearme": "", + "isdefense": false, + "bonusmaniementoff": 0, + "bonusmaniementdef": 0, + "degats": "", + "nonletaux": false, + "deuxmains": false, + "courte": 0, + "moyenne": 0, + "longue": 0, + "tr": 0, + "rarete": 0, + "prix": 0, + "equipped": false, + "templates": [ + "base" + ] + }, + "competence": { + "niveau": 0, + "attribut1": "", + "attribut2": "", + "attribut3": "", + "doublebonus": false, + "predilections": [], + "templates": [ + "base" + ] + }, + "protection": { + "typeprotection": "", + "protection": 0, + "degats": "", + "rarete": 0, + "prix": 0, + "equipped": false, + "templates": [ + "base" + ] + }, + "bouclier": { + "bonusdefense": 0, + "degats": "", + "nonletaux": false, + "rarete": 0, + "prix": 0, + "equipped": false, + "templates": [ + "base" + ] + }, + "pacte": { + "allegeance": "", + "templates": [ + "base" + ] + }, + "traitchaotique": { + "templates": [ + "base" + ] + }, + "monnaie": { + "templates": [ + "base" + ] + }, + "don": { + "allegeance": "", + "prerequis": "", + "sacrifice": "", + "templates": [ + "base" + ] + }, + "tendance": { + "allegeance": "", + "templates": [ + "base" + ] + }, + "rune": { + "formule": "", + "seuil": 0, + "prononcee": "", + "tracee": "", + "templates": [ + "base" + ] + } + } +} \ No newline at end of file diff --git a/templates/actor-sheet.html b/templates/actor-sheet.html new file mode 100644 index 0000000..b64a962 --- /dev/null +++ b/templates/actor-sheet.html @@ -0,0 +1,554 @@ +
+ + {{!-- Sheet Header --}} +
+
+
+ +
+

+
+ +
    +
  • + + + + + + + + +
  • + +
  • + + + + + + +
  • + +
  • +

    Bonne Aventure

    + + + + +
  • + +
  • +

    Alignement {{alignement}}

    +

    Eclat

    + + +

    Expérience

    + + +
  • +
+
+ +
+
+
+ + {{!-- Sheet Tab Navigation --}} + + + {{!-- Sheet Body --}} +
+ + {{!-- Main Tab --}} +
+ +
+ +
+
    + {{#each data.attributs as |attr key|}} +
  • + + {{attr.label}} + +
  • + {{/each}} +
+
+ +
+

Santé

+
    +
  • + + + + +
  • +
  • + + + + +
  • +
+ +

Ame

+
    +
  • + + + + + + +
  • +
+ +

Combat

+
    +
  • + + + + +
  • +
  • + + + + +
  • +
  • + + + + +
  • +
  • + + + + +
  • +
+
+ +
+ +
+ + {{!-- Competence Tab --}} +
+ +
+ +
+
    +
  • + +

    +
    + + + +
     
    +
  • + {{#each skills as |skill key|}} +
  • + + {{skill.name}} + + + {{#if (ne skill.system.attribut1 "none")}} + + + + {{/if}} + {{#if (ne skill.system.attribut2 "none")}} + + + + {{/if}} + {{#if (ne skill.system.attribut3 "none")}} + + + + {{/if}} + +
     
    +
    + + +
    +
  • + {{/each}} +
+
+ +
+ +
+ + {{!-- Dons/Pactes Tab --}} +
+ +
+ +
+
    +
  • + +

    +
    + + + +
     
    +
    +
    +
  • + {{#each dons as |don key|}} +
  • + + {{don.name}} + {{don.system.allegeance}} +
     
    +
    + + +
    +
  • + {{/each}} +
+
+ +
+
    +
  • + +

    +
    + + + + + + +
     
    +
    +
    +
  • + {{#each runes as |rune key|}} +
  • + + {{rune.name}} + {{rune.system.formule}} + {{rune.system.seuil}} +
     
    +
    + + +
    +
  • + {{/each}} +
+
+ + +
+
    +
  • + +

    +
    + + + +
     
    +
    +
    +
  • + {{#each tendances as |tendance key|}} +
  • + + {{tendance.name}} + {{tendance.system.allegeance}} +
     
    +
    + + +
    +
  • + {{/each}} +
+
+ +
+ +
+ + + {{!-- Equipement Tab --}} +
+ +
+ +
+
    +
  • + +

    +
    + + + + + + + + + +
     
    +
    + +
    +
  • + {{#each armes as |arme key|}} +
  • + + {{arme.name}} + + + {{#if arme.system.equipped}} + + {{else}} + + {{/if}} + + + {{#if arme.system.isdefense}} + + {{else}} + + {{/if}} + + + {{#if arme.system.equipped}} + + {{else}} + + {{/if}} + + +
     
    + +
  • + {{/each}} +
+
+ +
+
    +
  • + +

    +
    + + + +
     
    +
    + +
    +
  • + {{#each protections as |protection key|}} +
  • + + {{protection.name}} + + +
     
    +
    + + +
    +
  • + {{/each}} +
+
+ +
+
    +
  • + +

    +
    +
     
    +
    + +
    +
  • + {{#each equipements as |equipement key|}} +
  • + + {{equipement.name}} +
     
    +
    + + +
    +
  • + {{/each}} +
+
+ +
+ +
+ + + {{!-- Biography Tab --}} +
+
+
+
    +
  • + + +
    + + +
    +
  • +
  • + + +
    + + +
    +
  • +
  • + + +
    + + +
    +
  • +
  • + + +
  • +
+
+ +
+
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+
+ +
+
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
    +
    + + +

    Description

    +
    +
    + {{editor description target="system.biodata.description" button=true owner=owner editable=editable}} +
    + +
    + +
    +
    \ No newline at end of file diff --git a/templates/chat-degats-result.html b/templates/chat-degats-result.html new file mode 100644 index 0000000..9564ebc --- /dev/null +++ b/templates/chat-degats-result.html @@ -0,0 +1,26 @@ +
    + {{#if actorImg}} + {{alias}} + {{/if}} +

    {{alias}}

    +
    + +
    + +{{#if actionImg}} +
    + {{name}} +
    +{{/if}} + +
    +
    + +
    +
      +
    • Arme : {{arme.name}} (+{{arme.system.totalDegats}})
    • +
    • Dégats : {{finalResult}} {{#if arme.system.nonletaux}}(Non létaux){{else}}(Létaux){{/if}}
    • +
    +
    + +
    \ No newline at end of file diff --git a/templates/chat-generic-result.html b/templates/chat-generic-result.html new file mode 100644 index 0000000..c317075 --- /dev/null +++ b/templates/chat-generic-result.html @@ -0,0 +1,68 @@ +
    + {{#if actorImg}} + {{alias}} + {{/if}} +

    {{alias}}

    +
    + +
    + +{{#if actionImg}} +
    + {{name}} +
    +{{/if}} + +
    +
    + +
    + +
    + + \ No newline at end of file diff --git a/templates/editor-notes-gm.html b/templates/editor-notes-gm.html new file mode 100644 index 0000000..ca22bd0 --- /dev/null +++ b/templates/editor-notes-gm.html @@ -0,0 +1,6 @@ +{{#if data.isGM}} +

    GM Notes :

    +
    + {{editor data.biodata.gmnotes target="system.biodata.gmnotes" button=true owner=owner editable=editable}} +
    +{{/if}} diff --git a/templates/item-arme-sheet.html b/templates/item-arme-sheet.html new file mode 100644 index 0000000..6a488ab --- /dev/null +++ b/templates/item-arme-sheet.html @@ -0,0 +1,84 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + + + + + + + + + + + + + {{#if data.isdefense}} + + + + + {{/if}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{> systems/fvtt-hawkmoon-cyd/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/item-bouclier-sheet.html b/templates/item-bouclier-sheet.html new file mode 100644 index 0000000..85e8934 --- /dev/null +++ b/templates/item-bouclier-sheet.html @@ -0,0 +1,43 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/item-capacite-sheet.html b/templates/item-capacite-sheet.html new file mode 100644 index 0000000..04527c4 --- /dev/null +++ b/templates/item-capacite-sheet.html @@ -0,0 +1,18 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/item-competence-sheet.html b/templates/item-competence-sheet.html new file mode 100644 index 0000000..2b2b36a --- /dev/null +++ b/templates/item-competence-sheet.html @@ -0,0 +1,81 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + +

    Prédilections

    +
    +
      + {{#each data.predilections as |predilection key|}} +
    • + + + +
    • + {{/each}} +
    + + + + + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    \ No newline at end of file diff --git a/templates/item-don-sheet.html b/templates/item-don-sheet.html new file mode 100644 index 0000000..b722c1d --- /dev/null +++ b/templates/item-don-sheet.html @@ -0,0 +1,43 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + + + + + + + + + + +

    Sacrifices

    +
    + +
    + {{editor sacrifice target="system.sacrifice" button=true owner=owner editable=editable}} +
    + + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/item-equipement-sheet.html b/templates/item-equipement-sheet.html new file mode 100644 index 0000000..1771942 --- /dev/null +++ b/templates/item-equipement-sheet.html @@ -0,0 +1,27 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + + + + + + + + + + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/item-heritage-sheet.html b/templates/item-heritage-sheet.html new file mode 100644 index 0000000..04527c4 --- /dev/null +++ b/templates/item-heritage-sheet.html @@ -0,0 +1,18 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/item-metier-sheet.html b/templates/item-metier-sheet.html new file mode 100644 index 0000000..04527c4 --- /dev/null +++ b/templates/item-metier-sheet.html @@ -0,0 +1,18 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/item-monnaie-sheet.html b/templates/item-monnaie-sheet.html new file mode 100644 index 0000000..2f61605 --- /dev/null +++ b/templates/item-monnaie-sheet.html @@ -0,0 +1,19 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/item-origine-sheet.html b/templates/item-origine-sheet.html new file mode 100644 index 0000000..04527c4 --- /dev/null +++ b/templates/item-origine-sheet.html @@ -0,0 +1,18 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/item-pacte-sheet.html b/templates/item-pacte-sheet.html new file mode 100644 index 0000000..4d0587e --- /dev/null +++ b/templates/item-pacte-sheet.html @@ -0,0 +1,31 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + + + + + + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/item-predilection-sheet.html b/templates/item-predilection-sheet.html new file mode 100644 index 0000000..5462793 --- /dev/null +++ b/templates/item-predilection-sheet.html @@ -0,0 +1,23 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + + + + + + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/item-protection-sheet.html b/templates/item-protection-sheet.html new file mode 100644 index 0000000..2977400 --- /dev/null +++ b/templates/item-protection-sheet.html @@ -0,0 +1,35 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + + + + + + + + + + + + + + + + + + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/item-rune-sheet.html b/templates/item-rune-sheet.html new file mode 100644 index 0000000..2392600 --- /dev/null +++ b/templates/item-rune-sheet.html @@ -0,0 +1,39 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + + + + + + + + + + + + + + + + + + + + + + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/item-runeeffet-sheet.html b/templates/item-runeeffet-sheet.html new file mode 100644 index 0000000..8e304a6 --- /dev/null +++ b/templates/item-runeeffet-sheet.html @@ -0,0 +1,44 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + + + + + + + + + + + + + + + + + + + + + +
    + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/item-tendance-sheet.html b/templates/item-tendance-sheet.html new file mode 100644 index 0000000..4d0587e --- /dev/null +++ b/templates/item-tendance-sheet.html @@ -0,0 +1,31 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + + + + + + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/item-traitchaotique-sheet.html b/templates/item-traitchaotique-sheet.html new file mode 100644 index 0000000..2f61605 --- /dev/null +++ b/templates/item-traitchaotique-sheet.html @@ -0,0 +1,19 @@ +
    +
    + +
    +

    +
    +
    + + {{!-- Sheet Body --}} +
    + +
    + + {{> systems/fvtt-mournblade/templates/partial-item-description.html}} + +
    + +
    +
    diff --git a/templates/partial-actor-equipment.html b/templates/partial-actor-equipment.html new file mode 100644 index 0000000..1e18b27 --- /dev/null +++ b/templates/partial-actor-equipment.html @@ -0,0 +1,23 @@ +
  • + + {{#if (eq level 1)}} + {{equip.name}} + {{else}} + {{equip.name}} + {{/if}} + + + + +
     
    +
    + {{#if (eq level 1)}} + {{#if equip.system.equipped}}{{else}}{{/if}} + {{/if}} + +
    +
  • diff --git a/templates/partial-item-description.html b/templates/partial-item-description.html new file mode 100644 index 0000000..89dd3db --- /dev/null +++ b/templates/partial-item-description.html @@ -0,0 +1,6 @@ + +

    Description

    +
    +
    + {{editor description target="system.description" button=true owner=owner editable=editable}} +
    diff --git a/templates/partial-list-niveau.html b/templates/partial-list-niveau.html new file mode 100644 index 0000000..706f51a --- /dev/null +++ b/templates/partial-list-niveau.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/templates/post-item.html b/templates/post-item.html new file mode 100644 index 0000000..8ce0f51 --- /dev/null +++ b/templates/post-item.html @@ -0,0 +1,8 @@ +
    +

    {{name}}

    + {{#if img}} + + {{/if}} +

    Description :

    +

    {{{system.description}}}

    +
    diff --git a/templates/roll-dialog-generic.html b/templates/roll-dialog-generic.html new file mode 100644 index 0000000..7a7280f --- /dev/null +++ b/templates/roll-dialog-generic.html @@ -0,0 +1,104 @@ +
    +
    + {{#if img}} + + {{/if}} +

    {{title}}

    +
    + +
    + +
    + {{#if (eq attrKey "tochoose")}} + Attribut + + {{else}} + {{attr.label}} + {{attr.value}} + {{/if}} +
    + + {{#if competence}} +
    + {{competence.name}} + {{competence.system.niveau}} +
    + {{/if}} + + {{#if rune}} +
    + {{rune.name}} + {{rune.system.formule}} +
    +
    + Lancement + +
    +
    + Points d'Ame + +
    + + {{/if}} + +
    + Modificateur : + +
    + + {{#if armeDefense}} +
    + Défense adversaire : + {{difficulte}} +
    + {{else}} +
    + Difficulté : + +
    + {{/if}} + + {{#if canEclatDoubleD20}} +
    + Doubler le d20 (1 Point d'Eclat) + +
    + {{/if}} + + +
    + +
    \ No newline at end of file