commit db5fbb0e638d91d57785a6a5935af7457b78b2ba Author: LeRatierBretonnien Date: Mon Dec 11 20:11:10 2023 +0100 Initial import diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..ccdaeb9 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,7 @@ +Copyright 2023 Open Sesame Games + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9517001 --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# Te Deum Pour Un Massacre for FoundryVTT (French RPG, Open Sesam Games, Official)s + + +This is a base game system with functionnal character sheets for the game Ecryme, powered by the Engrenage system. +You can join the kickstarter and obtain the base books here : https://www.kickstarter.com/projects/osg-us/ecryme + +# System overview + + +The game system in Foundry offers the following features : +- PC/NPC sheet +- Skill rolls +- Cephaly rolls (with Anency support) +- Confrontation management, with detailed result in the chat card +- Weapon rolls +- Trait management, with Spleen and Ideal also. +- Compendiums of items for the game + +![System Snapshot](https://www.lahiette.com/leratierbretonnien/wp-content/uploads/2023/08/ecryme_snapshot_01.webp "System Snapshot") + + +# Contributions + +- Original code realised by Uberwald (https://www.uberwald.me/) + + +# English translation + +English translation by Conal Longden and Ian McClung + +# Copyright mentions + +Copyright 2023 Open Sesame Games +All rights reserved + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Ecryme is a game written by Alexandre Clavel and Samuel Metzener, in a universe created by Mathieu gaborit. All of the aforementionned authors retain there moral rights regarding this work in both print and digital formats. + +# Requests or Problems + +Please report any requests or problems you have at contact@open-sesame.games diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..c645262 --- /dev/null +++ b/changelog.md @@ -0,0 +1,17 @@ +v11.0.36 + +- Enable deletion specialization +- Custome bonus for specializations +- Specialization direct rolls + +v11.0.31 + +Add profession, fix equipment tab and add missing translation + +v11.0.30 + +Snapshot and more detailed README + +v11.0.28 + +Initial release diff --git a/fonts/MailartRubberstamp-Regular.otf b/fonts/MailartRubberstamp-Regular.otf new file mode 100644 index 0000000..612dd27 Binary files /dev/null and b/fonts/MailartRubberstamp-Regular.otf differ diff --git a/fonts/MailartRubberstamp-Regular.woff b/fonts/MailartRubberstamp-Regular.woff new file mode 100644 index 0000000..abe681a Binary files /dev/null and b/fonts/MailartRubberstamp-Regular.woff differ diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..4c748fd --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,25 @@ +var gulp = require('gulp'); + +var postcss = require('gulp-postcss'); + +var autoprefixer = require('autoprefixer'); +var cssnext = require('cssnext'); +var precss = require('precss'); + +gulp.task('css', function () { + + var processors = [ + autoprefixer, + cssnext, + precss + ]; + + return gulp.src('./postcss/*.css') + .pipe(postcss(processors)) + .pipe(gulp.dest('./styles')); +}); + + +function watchUpdates() { + gulp.watch('./postcss/*.css', css); +} diff --git a/images/icons/.directory b/images/icons/.directory new file mode 100644 index 0000000..63007f0 --- /dev/null +++ b/images/icons/.directory @@ -0,0 +1,5 @@ +[Dolphin] +SortRole=modificationtime +Timestamp=2023,5,10,17,7,42.817 +Version=4 +VisibleRoles=Details_text,Details_size,Details_modificationtime,Details_creationtime,CustomizedDetails diff --git a/lang/fr.json b/lang/fr.json new file mode 100644 index 0000000..9691f70 --- /dev/null +++ b/lang/fr.json @@ -0,0 +1,175 @@ +{ + "TYPES": { + "Actor":{ + "pc": "Personnage Joueur", + "npc": "Personnage Non Joueur", + "annency": "Anence" + }, + "Item": { + "trait": "Trait", + "weapon": "Arme", + "equipment": "Equipement", + "maneuver": "Manoeuvre", + "specialization": "Spécialisation" + } + }, + "ECRY": { + "settings": { + "cogs": "Engrenages", + "cephaly": "Céphalie", + "boheme": "Bohême", + "amertume": "Amertume", + "gamelevel": "Niveau de jeu" + }, + "chat": { + "formula": "Formule", + "difficulty": "Difficulté", + "dicesum": "Dés", + "result": "Resultat", + "margin": "Marge", + "success": "Succés!", + "failure": "Echec!", + "specialization": "Spécialisation", + "traitbonus": "Trait bonus", + "traitmalus": "Trait malus", + "bonusmalustraits": "Bonus/Malus des Traits", + "spectranscend": "Dépassement de soi : ", + "confrontselected": "Confrontation selectionnée", + "sentogm": "La confrontation a été envoyée au MJ" + }, + "rule": { + "cephaly-success-12": "Durée : 1 scène - Impact : Superficiel - Bonus : 1 - Elegie : 1", + "cephaly-success-4": "Durée : 1 semaine - Impact : Léger - Bonus : 2 - Elegie : 2", + "cephaly-success-6": "Durée : 1 mois - Impact : Grave - Bonus : 3 - Elegie : 3", + "cephaly-success-8": "Durée : 1 année - Impact : Majeur - Bonus : 4 - Elegie : 4", + "cephaly-success-10": "Durée : Permanent - Impact : Mort - Bonus : 5 - Elegie : 5", + "cephaly-failure-2": "Durée : 1 scène - Impact : Superficiel - Malus : 1 - Symptôme non visible et sans gravité - Altération bégigne difficilement repérable", + "cephaly-failure-4": "Durée : 1 semaine - Impact : Léger - Malus : 2 - Symptôme visible non incapacitant - Altération repérable", + "cephaly-failure-6": "Durée : 1 mois - Impact : Grave - Malus : 3 - Symptôme incapacitant - Altération repérable et fâcheuse", + "cephaly-failure-8": "Durée : 1 année - Impact : Majeur - Malus : 4 - Symptôme très incapacitant - Altération dangereuse", + "cephaly-failure-10": "Durée : Permanent - Impact : Mort/Folie - Malus : 5 - Symptôme spectaculaire et repoussant - Altération dangereuse globalement" + + }, + "warn": { + "notenoughdice": "L'Accomplissement et la Préservation doivent avoir 2 dés chacun" + }, + "ui": { + "equipmentfree": "Equipements (saisie libre)", + "traitType": "Type de trait", + "niveauTrait": "Niveau du trait", + "effect": "Incidence", + "weight": "Poids", + "cost": "Prix", + "costUnit": "Unité", + "ingot": "Lingot", + "ingotin": "Lingotin", + "goldcoin": "Pièce d'or", + "lige": "Lige", + "hurle": "Hurle", + "coin": "Sous", + "notes": "Notes", + "bio": "Bio", + "bionotes": "Bio&Notes", + "skills": "Compétences", + "traits": "Traits", + "equipment": "Equipement", + "physical": "Physiques", + "mental": "Mentales", + "social": "Sociales", + "athletics": "Athlétisme", + "driving": "Conduite", + "fencing": "Escrime", + "brawling": "Pugilat", + "shooting": "Tir", + "anthropomecanology": "Anthropo-Mécanologie", + "ecrymology": "Écrymologie", + "traumatology": "Traumatologie", + "traversology": "Traversologie", + "urbatechnology": "Urbatechnologie", + "quibbling": "Argutie", + "creativity": "Créativité", + "loquacity": "Faconde", + "guile": "Maraude", + "performance" :"Représentation", + "skill": "Compétence", + "troublesome": "Malaisé", + "occasional": "Peu frequent", + "difficult": "Difficile", + "uncommon": "Atypique", + "verydifficult": "Très difficile", + "rare": "Rare", + "extremdifficult": "Extrêmement difficile", + "veryrare": "Très rare", + "increddifficult": "Incroyable", + "exceptrare": "Exceptionnel", + "none": "Aucun", + "roll": "Lancer les dés !", + "cancel": "Annuler", + "rolltitle": "Ou l'on teste ses compétences", + "spec": "Spécialisation", + "traitbonus": "Traits bonus", + "traitmalus": "Traits malus", + "applyideal": "Utiliser l'idéal", + "applyspleen": "Utiliser le spleen", + "skilltranscendence": "Dépassement de soi", + "confrontation": "Confrontation", + "rollnormal": "Normal (4d6)", + "rollspleen": "Avec le Spleen (5d6, 4 plus bas conservés)", + "rollideal": "Avec l'Idéal (5d6, 4 plus haut conservés)", + "superficial": "Superficiel", + "light": "Léger", + "serious": "Grave", + "major": "Majeur", + "impactType": "Type d'Impact", + "impactLevel": "Niveau d'impact", + "impactphysical": "Physique", + "impactmental": "Mental", + "impactsocial": "Social", + "impactmalus": "Malus d'Impact", + "ongoingconfront": "Confrontations en cours", + "confront":"Confrontation", + "launchconfront": "Lancer la confrontation", + "execution": "Accomplissement", + "preservation": "Préservation", + "dicepool": "Dés disponibles", + "selectconfront": "Sélectionner pour la Confrontation", + "transcendapply": "Appliquer la Transcendence à ", + "healthcombat": "Santé&Combat", + "name": "Nom", + "weapons": "Armes", + "weapon": "Arme", + "melee": "Mêlée", + "ranged": "A Distance", + "weapontype": "Type d'arme", + "type": "Type", + "applyimpact": "Appliquer l'impact", + "applybonus": "Appliquer le bonus", + "bonuspool": "Bonus disponibles", + "cephaly": "Cephalie", + "elegy": "Elégie", + "entelechy": "Entéléchie", + "mekany": "Mekanë", + "psyche": "Psyché", + "scoria": "Scorie", + "cephalydifficulty": "Difficulté de la Céphalie", + "maneuvers": "Manoeuvres", + "annency": "Anence", + "iscollective": "Collective", + "ismultiple": "Multiple", + "description": "Description", + "location": "Lieu", + "characters": "Personnages", + "enhancements": "Améliorations", + "oniricform": "Forme Onorique (Bohême)", + "ideals": "Idéaux", + "politic": "Idéaux politiques", + "boheme": "Bohême", + "annencybonus": "Bonus d'Anence", + "bornplace": "Lieu de naissance", + "residence": "Résidence", + "origin": "Origine", + "childhood": "Enfance", + "bonus": "Bonus" + } + } +} \ No newline at end of file diff --git a/modules/actors/tedeum-actor-sheet.js b/modules/actors/tedeum-actor-sheet.js new file mode 100644 index 0000000..d3b74dc --- /dev/null +++ b/modules/actors/tedeum-actor-sheet.js @@ -0,0 +1,197 @@ +/** + * Extend the basic ActorSheet with some very simple modifications + * @extends {ActorSheet} + */ + +import { EcrymeUtility } from "../common/tedeum-utility.js"; + +/* -------------------------------------------- */ +export class EcrymeActorSheet extends ActorSheet { + + /** @override */ + static get defaultOptions() { + + return mergeObject(super.defaultOptions, { + classes: ["fvtt-ecryme", "sheet", "actor"], + template: "systems/fvtt-ecryme/templates/actors/actor-sheet.hbs", + width: 860, + height:680, + tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "skills" }], + dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }], + editScore: true + }); + } + + /* -------------------------------------------- */ + async getData() { + + let formData = { + title: this.title, + id: this.actor.id, + type: this.actor.type, + img: this.actor.img, + name: this.actor.name, + editable: this.isEditable, + cssClass: this.isEditable ? "editable" : "locked", + system: duplicate(this.object.system), + limited: this.object.limited, + skills: this.actor.prepareSkills(), + traits: this.actor.getRollTraits(), + confrontations: this.actor.getConfrontations(), + ideal: this.actor.getIdeal(), + spleen: this.actor.getSpleen(), + impacts: this.object.getImpacts(), + config: duplicate(game.system.ecryme.config), + weapons: this.actor.getWeapons(), + maneuvers: this.actor.getManeuvers(), + impactsMalus: this.actor.getImpactsMalus(), + archetype: duplicate(this.actor.getArchetype()), + equipments: this.actor.getEquipments(), + hasCephaly: EcrymeUtility.hasCephaly(), + hasBoheme: EcrymeUtility.hasBoheme(), + hasAmertume: EcrymeUtility.hasAmertume(), + cephalySkills: this.actor.getCephalySkills(), + subActors: duplicate(this.actor.getSubActors()), + annency: this.actor.getAnnency(), + description: await TextEditor.enrichHTML(this.object.system.description, { async: true }), + notes: await TextEditor.enrichHTML(this.object.system.notes, { async: true }), + equipementlibre: await TextEditor.enrichHTML(this.object.system.equipementlibre, { 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; + + html.bind("keydown", function(e) { // Ignore Enter in actores sheet + if (e.keyCode === 13) return false; + }); + + html.find('.open-annency').click(ev => { + let actorId = $(ev.currentTarget).data("annency-id") + const actor = game.actors.get(actorId) + actor.sheet.render(true) + }) + + // 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") + EcrymeUtility.confirmDelete(this, li).catch("Error : No deletion confirmed") + }) + html.find('.item-add').click(ev => { + let dataType = $(ev.currentTarget).data("type") + this.actor.createEmbeddedDocuments('Item', [{ name: "NewItem", type: dataType }], { renderSheet: true }) + }) + + html.find('.subactor-edit').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let actorId = li.data("actor-id"); + let actor = game.actors.get( actorId ); + actor.sheet.render(true); + }); + + html.find('.subactor-delete').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let actorId = li.data("actor-id"); + this.actor.delSubActor(actorId); + }); + 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-skill').click((event) => { + let categKey = $(event.currentTarget).data("category-key") + let skillKey = $(event.currentTarget).data("skill-key") + this.actor.rollSkill(categKey, skillKey) + }); + html.find('.roll-spec').click((event) => { + let categKey = $(event.currentTarget).data("category-key") + let skillKey = $(event.currentTarget).data("skill-key") + let specId = $(event.currentTarget).data("spec-id") + this.actor.rollSpec(categKey, skillKey, specId) + }); + html.find('.roll-skill-confront').click((event) => { + let categKey = $(event.currentTarget).data("category-key") + let skillKey = $(event.currentTarget).data("skill-key") + this.actor.rollSkillConfront(categKey, skillKey) + }); + html.find('.roll-cephaly').click((event) => { + let skillKey = $(event.currentTarget).data("skill-key") + this.actor.rollCephalySkillConfront(skillKey) + }); + html.find('.roll-weapon-confront').click((event) => { + const li = $(event.currentTarget).parents(".item") + let weaponId = li.data("item-id"); + this.actor.rollWeaponConfront(weaponId) + }); + + html.find('.impact-modify').click((event) => { + let impactType = $(event.currentTarget).data("impact-type") + let impactLevel = $(event.currentTarget).data("impact-level") + let modifier = Number($(event.currentTarget).data("impact-modifier")) + this.actor.modifyImpact(impactType, impactLevel, modifier) + }); + + html.find('.roll-weapon').click((event) => { + const armeId = $(event.currentTarget).data("arme-id") + this.actor.rollArme(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); + }); + html.find('.update-field').change(ev => { + const fieldName = $(ev.currentTarget).data("field-name"); + let value = Number(ev.currentTarget.value); + this.actor.update( { [`${fieldName}`]: value } ); + }); + } + + /* -------------------------------------------- */ + /** @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; + } + + /* -------------------------------------------- */ + /** @override */ + _updateObject(event, formData) { + // Update the Actor + return this.object.update(formData); + } +} diff --git a/modules/actors/tedeum-actor.js b/modules/actors/tedeum-actor.js new file mode 100644 index 0000000..440cb30 --- /dev/null +++ b/modules/actors/tedeum-actor.js @@ -0,0 +1,505 @@ +/* -------------------------------------------- */ +import { EcrymeUtility } from "../common/ecryme-utility.js"; +import { EcrymeRollDialog } from "../dialogs/ecryme-roll-dialog.js"; +import { EcrymeConfrontStartDialog } from "../dialogs/ecryme-confront-start-dialog.js"; + +/* -------------------------------------------- */ +/* -------------------------------------------- */ +/** + * Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system. + * @extends {Actor} + */ +export class EcrymeActor 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; + } + + return super.create(data, options); + } + + /* -------------------------------------------- */ + async prepareData() { + super.prepareData() + } + + /* -------------------------------------------- */ + prepareDerivedData() { + super.prepareDerivedData(); + } + + /* -------------------------------------------- */ + _preUpdate(changed, options, user) { + + super._preUpdate(changed, options, user); + } + + /* -------------------------------------------- */ + async _preCreate(data, options, user) { + await super._preCreate(data, options, user); + + // Configure prototype token settings + const prototypeToken = {}; + if (this.type === "pc") Object.assign(prototypeToken, { + sight: { enabled: true }, actorLink: true, disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY + }); + this.updateSource({ prototypeToken }); + } + + /* -------------------------------------------- */ + getMoneys() { + let comp = this.items.filter(item => item.type == 'money'); + EcrymeUtility.sortArrayObjectsByName(comp) + return comp; + } + getArchetype() { + let comp = duplicate(this.items.find(item => item.type == 'archetype') || { name: "Pas d'archetype" }) + if (comp?.system) { + comp.tarot = EcrymeUtility.getTarot(comp.system.lametutelaire) + } + + return comp; + } + + /* -------------------------------------------- */ + buildAnnencyActorList() { + let membersFull = {} + for(let id of this.system.base.characters) { + let actor = game.actors.get(id) + membersFull[id] = { name: actor.name, id: actor.id, img: actor.img } + } + return membersFull + } + /* ----------------------- --------------------- */ + addAnnencyActor(actorId) { + let members = duplicate(this.system.base.characters) + members.push(actorId) + this.update({ 'system.base.characters': members }) + } + async removeAnnencyActor(actorId) { + let members = this.system.base.characters.filter(id => id != actorId) + this.update({ 'system.base.characters': members }) + } + + /* -------------------------------------------- */ + getAnnency() { + return game.actors.find(a => a.type == 'annency' && a.system.base.characters.includes(this.id)) + } + /* -------------------------------------------- */ + getConfrontations() { + return this.items.filter(it => it.type == "confrontation") + } + getRollTraits() { + return this.items.filter(it => it.type == "trait" && it.system.traitype == "normal") + } + getIdeal() { + return this.items.find(it => it.type == "trait" && it.system.traitype == "ideal") + } + getSpleen() { + return this.items.find(it => it.type == "trait" && it.system.traitype == "spleen") + } + + /* -------------------------------------------- */ + getTrait(id) { + //console.log("TRAITS", this.items, this.items.filter(it => it.type == "trait") ) + return this.items.find(it => it.type == "trait" && it._id == id) + } + /* -------------------------------------------- */ + getSpecialization(id) { + let spec = this.items.find(it => it.type == "specialization" && it.id == id) + return spec + } + /* -------------------------------------------- */ + getSpecializations(skillKey) { + return this.items.filter(it => it.type == "specialization" && it.system.skillkey == skillKey) + } + /* -------------------------------------------- */ + prepareSkills() { + let skills = duplicate(this.system.skills) + for (let categKey in skills) { + let category = skills[categKey] + for (let skillKey in category.skilllist) { + let skill = category.skilllist[skillKey] + skill.spec = this.getSpecializations(skillKey) + } + } + return skills + } + /* -------------------------------------------- */ + getCephalySkills() { + let skills = duplicate(this.system.cephaly.skilllist) + return skills + } + /* -------------------------------------------- */ + getImpacts() { + let comp = duplicate(this.items.filter(item => item.type == 'impact') || []) + return comp; + } + /* -------------------------------------------- */ + getWeapons() { + let comp = duplicate(this.items.filter(item => item.type == 'weapon') || []) + EcrymeUtility.sortArrayObjectsByName(comp) + return comp; + } + getManeuvers() { + let comp = duplicate(this.items.filter(item => item.type == 'maneuver') || []) + EcrymeUtility.sortArrayObjectsByName(comp) + return comp; + } + /* -------------------------------------------- */ + 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?.system) { + if (item.type == "armor") { + let armor = this.items.find(item => item.id != itemId && item.type == "armor" && item.system.equipped) + if (armor) { + ui.notifications.warn("You already have an armor equipped!") + return + } + } + if (item.type == "shield") { + let shield = this.items.find(item => item.id != itemId && item.type == "shield" && item.system.equipped) + if (shield) { + ui.notifications.warn("You already have a shield equipped!") + return + } + } + let update = { _id: item.id, "system.equipped": !item.system.equipped }; + await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity + } + } + + /* ------------------------------------------- */ + getEquipments() { + return this.items.filter(item => item.type == 'equipment') + } + + /* ------------------------------------------- */ + async buildContainerTree() { + let equipments = duplicate(this.items.filter(item => item.type == "equipment") || []) + for (let equip1 of equipments) { + if (equip1.system.iscontainer) { + equip1.system.contents = [] + equip1.system.contentsEnc = 0 + for (let equip2 of equipments) { + if (equip1._id != equip2.id && equip2.system.containerid == equip1.id) { + equip1.system.contents.push(equip2) + let q = equip2.system.quantity ?? 1 + equip1.system.contentsEnc += q * equip2.system.weight + } + } + } + } + + // Compute whole enc + let enc = 0 + for (let item of equipments) { + //item.data.idrDice = EcrymeUtility.getDiceFromLevel(Number(item.data.idr)) + if (item.system.equipped) { + if (item.system.iscontainer) { + enc += item.system.contentsEnc + } else if (item.system.containerid == "") { + let q = item.system.quantity ?? 1 + enc += q * item.system.weight + } + } + } + for (let item of this.items) { // Process items/shields/armors + if ((item.type == "weapon" || item.type == "shield" || item.type == "armor") && item.system.equipped) { + let q = item.system.quantity ?? 1 + enc += q * item.system.weight + } + } + + // Store local values + this.encCurrent = enc + this.containersTree = equipments.filter(item => item.system.containerid == "") // Returns the root of equipements without container + + } + + /* -------------------------------------------- */ + async equipGear(equipmentId) { + let item = this.items.find(item => item.id == equipmentId); + if (item?.system) { + let update = { _id: item.id, "system.equipped": !item.system.equipped }; + await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity + } + } + + /* -------------------------------------------- */ + modifyImpact(impactType, impactLevel, modifier) { + console.log(impactType, impactLevel, modifier) + let current = this.system.impacts[impactType][impactLevel] + if (modifier > 0) { + while ( EcrymeUtility.getImpactMax(impactLevel) == current && impactLevel != "major") { + impactLevel = EcrymeUtility.getNextImpactLevel(impactLevel) + current = this.system.impacts[impactType][impactLevel] + } + } + let newImpact = Math.max(this.system.impacts[impactType][impactLevel] + modifier, 0) + this.update({ [`system.impacts.${impactType}.${impactLevel}`]: newImpact}) + } + + /* -------------------------------------------- */ + getImpactMalus(impactKey) { + let impacts = this.system.impacts[impactKey] + return - ((impacts.serious*2) + (impacts.major*4)) + } + + /* -------------------------------------------- */ + getImpactsMalus() { + let impactsMalus = { + physical: this.getImpactMalus("physical"), + mental: this.getImpactMalus("mental"), + social: this.getImpactMalus("social") + } + return impactsMalus + } + + /* -------------------------------------------- */ + clearInitiative() { + this.getFlag("world", "initiative", -1) + } + /* -------------------------------------------- */ + getInitiativeScore(combatId, combatantId) { + let init = Math.floor((this.system.attributs.physique.value + this.system.attributs.habilite.value) / 2) + let subValue = new Roll("1d20").roll({ async: false }) + return init + (subValue.total / 100) + } + + /* -------------------------------------------- */ + 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 deleteAllItemsByType(itemType) { + let items = this.items.filter(item => item.type == itemType); + await this.deleteEmbeddedDocuments('Item', items); + } + + /* -------------------------------------------- */ + async addItemWithoutDuplicate(newItem) { + let item = this.items.find(item => item.type == newItem.type && item.name.toLowerCase() == newItem.name.toLowerCase()) + if (!item) { + await this.createEmbeddedDocuments('Item', [newItem]); + } + } + + /* -------------------------------------------- */ + async incDecQuantity(objetId, incDec = 0) { + let objetQ = this.items.get(objetId) + if (objetQ) { + let newQ = objetQ.system.quantity + incDec + if (newQ >= 0) { + await this.updateEmbeddedDocuments('Item', [{ _id: objetQ.id, 'system.quantity': newQ }]) // pdates one EmbeddedEntity + } + } + } + + /* -------------------------------------------- */ + modifyConfrontBonus( modifier ) { + let newBonus = this.system.internals.confrontbonus + modifier + this.update({'system.internals.confrontbonus': newBonus}) + } + + /* -------------------------------------------- */ + spentSkillTranscendence(skill, value) { + let newValue = this.system.skills[skill.categKey].skilllist[skill.skillKey].value - value + newValue = Math.max(0, newValue) + this.update({ [`system.skills.${skill.categKey}.skilllist.${skill.skillKey}.value`]: newValue }) + } + + /* -------------------------------------------- */ + getBonusList() { + let bonusList = [] + for(let i=0; i it.type == "specialization" && it.id == specId) + rollData.mode = "skill" + rollData.selectedSpecs = [spec.id] + rollData.forcedSpec = duplicate(spec) + rollData.title = game.i18n.localize(rollData.skill.name) + this.startRoll(rollData).catch("Error on startRoll") + } + + /* -------------------------------------------- */ + async rollSkillConfront(categKey, skillKey) { + let rollData = this.getCommonSkill(categKey, skillKey) + rollData.mode = "skill" + rollData.title = game.i18n.localize("ECRY.ui.confrontation") + " : " + game.i18n.localize(rollData.skill.name) + rollData.executionTotal = rollData.skill.value + rollData.preservationTotal = rollData.skill.value + rollData.applyTranscendence = "execution" + rollData.traitsBonus = duplicate(rollData.traits) + rollData.traitsMalus = duplicate(rollData.traits) + let confrontStartDialog = await EcrymeConfrontStartDialog.create(this, rollData) + confrontStartDialog.render(true) + } + /* -------------------------------------------- */ + async rollCephalySkillConfront(skillKey) { + let rollData = this.getCommonRollData() + rollData.mode = "cephaly" + rollData.skill = duplicate(this.system.cephaly.skilllist[skillKey]) + rollData.annency = duplicate(this.getAnnency()) + rollData.img = rollData.skill.img + rollData.skill.categKey = "cephaly" + rollData.skill.skillKey = skillKey + //rollData.impactMalus = this.getImpactMalus(categKey) + rollData.title = game.i18n.localize("ECRY.ui.cephaly") + " : " + game.i18n.localize(rollData.skill.name) + rollData.executionTotal = rollData.skill.value + rollData.preservationTotal = rollData.skill.value + rollData.traitsBonus = duplicate(rollData.traits) + rollData.traitsMalus = duplicate(rollData.traits) + rollData.applyTranscendence = "execution" + let confrontStartDialog = await EcrymeConfrontStartDialog.create(this, rollData) + confrontStartDialog.render(true) + } + + /* -------------------------------------------- */ + async rollWeaponConfront(weaponId) { + let weapon = this.items.get(weaponId) + let rollData + if (weapon && weapon.system.weapontype == "melee") { + rollData = this.getCommonSkill("physical", "fencing") + } else { + rollData = this.getCommonSkill("physical", "shooting") + } + rollData.mode = "weapon" + rollData.weapon = duplicate(weapon) + rollData.title = game.i18n.localize("ECRY.ui.confrontation") + " : " + game.i18n.localize(rollData.skill.name) + rollData.executionTotal = rollData.skill.value + rollData.preservationTotal = rollData.skill.value + rollData.traitsBonus = duplicate(rollData.traits) + rollData.traitsMalus = duplicate(rollData.traits) + rollData.applyTranscendence = "execution" + let confrontStartDialog = await EcrymeConfrontStartDialog.create(this, rollData) + confrontStartDialog.render(true) + } + + /* -------------------------------------------- */ + rollWeapon(weaponId) { + let weapon = this.items.get(weaponId) + if (weapon) { + weapon = duplicate(weapon) + let rollData = this.getCommonRollData() + if (weapon.system.armetype == "mainsnues" || weapon.system.armetype == "epee") { + rollData.attr = { label: "(Physique+Habilité)/2", value: Math.floor((this.getPhysiqueMalus() + this.system.attributs.physique.value + this.system.attributs.habilite.value) / 2) } + } else { + rollData.attr = duplicate(this.system.attributs.habilite) + } + rollData.mode = "weapon" + rollData.weapon = weapon + rollData.img = weapon.img + rollData.title = weapon.name + this.startRoll(rollData).catch("Error on startRoll") + } else { + ui.notifications.warn("Impossible de trouver l'arme concernée ") + } + } + + /* -------------------------------------------- */ + async startRoll(rollData) { + let rollDialog = await EcrymeRollDialog.create(this, rollData) + rollDialog.render(true) + } + +} diff --git a/modules/app/tedeum-combat.js b/modules/app/tedeum-combat.js new file mode 100644 index 0000000..0869aad --- /dev/null +++ b/modules/app/tedeum-combat.js @@ -0,0 +1,40 @@ +import { EcrymeUtility } from "../common/tedeum-utility.js"; + +/* -------------------------------------------- */ +export class EcrymeCombat 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( this.id, id ) : -1; + await this.updateEmbeddedDocuments("Combatant", [ { _id: id, initiative: initBonus } ]); + } + + return this; + } + + /* -------------------------------------------- */ + _onUpdate(changed, options, userId) { + } + + /* -------------------------------------------- */ + static async checkTurnPosition() { + while (game.combat.turn > 0) { + await game.combat.previousTurn() + } + } + + /* -------------------------------------------- */ + _onDelete() { + let combatants = this.combatants.contents + for (let c of combatants) { + let actor = game.actors.get(c.actorId) + actor.clearInitiative() + } + super._onDelete() + } + +} diff --git a/modules/app/tedeum-commands.js b/modules/app/tedeum-commands.js new file mode 100644 index 0000000..061dbdc --- /dev/null +++ b/modules/app/tedeum-commands.js @@ -0,0 +1,105 @@ +/* -------------------------------------------- */ + +import { EcrymeUtility } from "../common/tedeum-utility.js"; +import { EcrymeCharacterSummary } from "./ecryme-summary-app.js" + +/* -------------------------------------------- */ +export class EcrymeCommands { + + static init() { + if (!game.system.ecryme.commands) { + const commands = new EcrymeCommands(); + commands.registerCommand({ path: ["/resume"], func: (content, msg, params) => EcrymeCharacterSummary.displayPCSummary(), descr: "Affiche la liste des PJs!" }); + game.system.ecryme.commands = commands; + } + } + 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("crucibleCommands._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) { + CrucibleCommands._chatAnswer(msg, command.descr); + } + return true; + } + return false; + } + + /* -------------------------------------------- */ + static _chatAnswer(msg, content) { + msg.whisper = [game.user.id]; + msg.content = content; + ChatMessage.create(msg); + } + +} \ No newline at end of file diff --git a/modules/app/tedeum-hotbar.js b/modules/app/tedeum-hotbar.js new file mode 100644 index 0000000..ab016b6 --- /dev/null +++ b/modules/app/tedeum-hotbar.js @@ -0,0 +1,86 @@ + +export class EcrymeHotbar { + + /** + * Create a macro when dropping an entity on the hotbar + * Item - open roll dialog for item + * Actor - open actor sheet + * Journal - open journal sheet + */ + static init( ) { + + Hooks.on("hotbarDrop", async (bar, documentData, slot) => { + // Create item macro if rollable item - weapon, spell, prayer, trait, or skill + if (documentData.type == "Item") { + console.log("Drop done !!!", bar, documentData, slot) + let item = documentData.data + let command = `game.system.ecryme.EcrymeHotbar.rollMacro("${item.name}", "${item.type}");` + let macro = game.macros.contents.find(m => (m.name === item.name) && (m.command === command)) + if (!macro) { + macro = await Macro.create({ + name: item.name, + type: "script", + img: item.img, + command: command + }, { displaySheet: false }) + } + game.user.assignHotbarMacro(macro, slot); + } + // Create a macro to open the actor sheet of the actor dropped on the hotbar + else if (documentData.type == "Actor") { + let actor = game.actors.get(documentData.id); + let command = `game.actors.get("${documentData.id}").sheet.render(true)` + let macro = game.macros.contents.find(m => (m.name === actor.name) && (m.command === command)); + if (!macro) { + macro = await Macro.create({ + name: actor.data.name, + type: "script", + img: actor.data.img, + command: command + }, { displaySheet: false }) + game.user.assignHotbarMacro(macro, slot); + } + } + // Create a macro to open the journal sheet of the journal dropped on the hotbar + else if (documentData.type == "JournalEntry") { + let journal = game.journal.get(documentData.id); + let command = `game.journal.get("${documentData.id}").sheet.render(true)` + let macro = game.macros.contents.find(m => (m.name === journal.name) && (m.command === command)); + if (!macro) { + macro = await Macro.create({ + name: journal.data.name, + type: "script", + img: "", + command: command + }, { displaySheet: false }) + game.user.assignHotbarMacro(macro, slot); + } + } + return false; + }); + } + + /** Roll macro */ + static rollMacro(itemName, itemType, bypassData) { + const speaker = ChatMessage.getSpeaker() + let actor + if (speaker.token) actor = game.actors.tokens[speaker.token] + if (!actor) actor = game.actors.get(speaker.actor) + if (!actor) { + return ui.notifications.warn(`Select your actor to run the macro`) + } + + let item = actor.items.find(it => it.name === itemName && it.type == itemType) + if (!item ) { + return ui.notifications.warn(`Unable to find the item of the macro in the current actor`) + } + // Trigger the item roll + if (item.type === "weapon") { + return actor.rollWeapon( item.id) + } + if (item.type === "skill") { + return actor.rollSkill( item.id) + } + } + +} diff --git a/modules/common/tedeum-config.js b/modules/common/tedeum-config.js new file mode 100644 index 0000000..e652b72 --- /dev/null +++ b/modules/common/tedeum-config.js @@ -0,0 +1,62 @@ + +export const ECRYME_CONFIG = { + + traitTypes: { + normal: "Normal", + spleen: "Spleen", + ideal: "Ideal" + }, + weaponTypes: { + "melee": "ECRY.ui.melee", + "ranged": "ECRY.ui.ranged" + }, + traitLevel: [ + {value: -3, text: "-3"}, + {value: -2, text: "-2"}, + {value: -1, text: "-1"}, + {value: +1, text: "+1"}, + {value: +2, text: "+2"}, + {value: +3, text: "+3"} + ], + impactTypes: { + physical: "ECRY.ui.physical", + mental: "ECRY.ui.mental", + social: "ECRY.ui.social" + }, + impactLevels: { + superficial: "ECRY.ui.superficial", + light: "ECRY.ui.light", + serious: "ECRY.ui.serious", + major: "ECRY.ui.major" + }, + difficulty: { + "-1": {difficulty: "ECRY.ui.none", frequency: "ECRY.ui.none", value: "-"}, + "8": { difficulty: "ECRY.ui.troublesome", frequency: "ECRY.ui.occasional", value: 8 }, + "10": { difficulty: "ECRY.ui.difficult", frequency: "ECRY.ui.uncommon", value: 10 }, + "12": { difficulty: "ECRY.ui.verydifficult", frequency: "ECRY.ui.rare", value: 12 }, + "14": { difficulty: "ECRY.ui.extremdifficult", frequency: "ECRY.ui.veryrare", value: 14 }, + "16": { difficulty: "ECRY.ui.increddifficult", frequency: "ECRY.ui.exceptrare", value: 16 }, + }, + skillLevel: { + "0": "0", + "1": "1", + "2": "2", + "3": "3", + "4": "4", + "5": "5", + "6": "6", + "7": "7", + "8": "8", + "9": "9", + "10": "10" + }, + costUnits: { + "ingot": {name: "ECRY.ui.ingot", value: 100000}, + "ingotin": {name: "ECRY.ui.ingotin", value: 10000}, + "goldcoin": {name: "ECRY.ui.goldcoin", value: 1000 }, + "lige": {name: "ECRY.ui.lige", value: 100 }, + "hurle": {name: "ECRY.ui.hurle", value: 10 }, + "coin": {name: "ECRY.ui.coin", value: 1 } + } + +} \ No newline at end of file diff --git a/modules/common/tedeum-utility.js b/modules/common/tedeum-utility.js new file mode 100644 index 0000000..a909d92 --- /dev/null +++ b/modules/common/tedeum-utility.js @@ -0,0 +1,745 @@ +/* -------------------------------------------- */ +import { EcrymeCommands } from "../app/tedeum-commands.js"; + +/* -------------------------------------------- */ +const __maxImpacts = { superficial: 4, light: 3, serious: 2, major: 1 } +const __nextImpacts = { superficial: "light", light: "serious", serious: "major", major: "major" } +const __effect2Impact = ["none", "superficial", "superficial", "light", "light", "serious", "serious", "major", "major"] +const __cephalySuccess = { + 1: "cephaly-success-2", + 2: "cephaly-success-2", + 3: "cephaly-success-4", + 4: "cephaly-success-4", + 5: "cephaly-success-6", + 6: "cephaly-success-6", + 7: "cephaly-success-8", + 8: "cephaly-success-8", + 9: "cephaly-success-9", + 10: "cephaly-success-10" +} +const __cephalyFailure = { + 1: "cephaly-failure-2", + 2: "cephaly-failure-2", + 3: "cephaly-failure-4", + 4: "cephaly-failure-4", + 5: "cephaly-failure-6", + 6: "cephaly-failure-6", + 7: "cephaly-failure-8", + 8: "cephaly-failure-8", + 9: "cephaly-failure-9", + 10: "cephaly-failure-10" +} + +/* -------------------------------------------- */ +export class EcrymeUtility { + + /* -------------------------------------------- */ + static async init() { + Hooks.on('renderChatLog', (log, html, data) => EcrymeUtility.chatListeners(html)); + Hooks.on("getChatLogEntryContext", (html, options) => EcrymeUtility.chatMenuManager(html, options)); + + this.rollDataStore = {} + this.defenderStore = {} + + EcrymeCommands.init(); + } + + /* -------------------------------------------- */ + static async ready() { + + 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); + }) + Handlebars.registerHelper('add', function (a, b) { + return parseInt(a) + parseInt(b); + }) + Handlebars.registerHelper('valueAtIndex', function (arr, idx) { + return arr[idx]; + }) + Handlebars.registerHelper('for', function (from, to, incr, block) { + let accum = ''; + for (let i = from; i <= to; i += incr) + accum += block.fn(i); + return accum; + }) + Handlebars.registerHelper('isGM', function () { + return game.user.isGM + }) + + game.settings.register("fvtt-ecryme", "ecryme-game-level", { + name: game.i18n.localize("ECRY.settings.gamelevel"), + label: game.i18n.localize("ECRY.settings.gamelevelhelp"), + scope: 'world', + config: true, + type: String, + choices: { + "level_e": game.i18n.localize("ECRY.settings.cogs"), + "level_c": game.i18n.localize("ECRY.settings.cephaly"), + "level_b": game.i18n.localize("ECRY.settings.boheme"), + "level_a": game.i18n.localize("ECRY.settings.amertume"), + }, + restricted: true + }) + + this.buildSkillConfig() + + } + + /*-------------------------------------------- */ + static hasCephaly() { + let level = game.settings.get("fvtt-ecryme", "ecryme-game-level") + return level != "level_e" + } + /*-------------------------------------------- */ + static hasBoheme() { + let level = game.settings.get("fvtt-ecryme", "ecryme-game-level") + return level == "level_b" || level == "level_a" + } + /*-------------------------------------------- */ + static hasAmertume() { + let level = game.settings.get("fvtt-ecryme", "ecryme-game-level") + return level == "level_a" + } + + /*-------------------------------------------- */ + static buildSkillConfig() { + game.system.ecryme.config.skills = {} + for (let categKey in game.data.template.Actor.templates.core.skills) { + let category = game.data.template.Actor.templates.core.skills[categKey] + for (let skillKey in category.skilllist) { + let skill = duplicate(category.skilllist[skillKey]) + skill.categKey = categKey // Auto reference the category + game.system.ecryme.config.skills[skillKey] = skill + } + } + } + + /*-------------------------------------------- */ + static upperFirst(text) { + if (typeof text !== 'string') return text + return text.charAt(0).toUpperCase() + text.slice(1) + } + + /* -------------------------------------------- */ + static async loadCompendiumData(compendium) { + const pack = game.packs.get(compendium) + return await pack?.getDocuments() ?? [] + } + + /* -------------------------------------------- */ + static async loadCompendium(compendium, filter = item => true) { + let compendiumData = await EcrymeUtility.loadCompendiumData(compendium) + return compendiumData.filter(filter) + } + + /* -------------------------------------------- */ + static getActorFromRollData(rollData) { + let actor = game.actors.get(rollData.actorId) + if (rollData.tokenId) { + let token = canvas.tokens.placeables.find(t => t.id == rollData.tokenId) + if (token) { + actor = token.actor + } + } + return actor + } + /* -------------------------------------------- */ + static getImpactFromEffect(effectValue) { + if (effectValue >= __effect2Impact.length) { + return "major" + } + return __effect2Impact[effectValue] + } + /* -------------------------------------------- */ + static async processConfrontation() { + let confront = { + type: "confront-data", + rollData1: this.confrontData1, + rollData2: this.confrontData2, + } + // Compute margin + confront.marginExecution = this.confrontData1.executionTotal - this.confrontData2.preservationTotal + confront.marginPreservation = this.confrontData1.preservationTotal - this.confrontData2.executionTotal + console.log(confront.marginExecution, confront.marginPreservation) + // Filter margin + let maxMargin // Dummy max + if (confront.marginExecution > 0) { // Successful hit + // Limit with skill+spec + maxMargin = confront.rollData1.skill.value + ((confront.rollData1.spec) ? 2 : 0) + confront.marginExecution = Math.min(confront.marginExecution, maxMargin) + } else { // Failed hit + maxMargin = confront.rollData2.skill.value + ((confront.rollData2.spec) ? 2 : 0) + confront.marginExecution = -Math.min(Math.abs(confront.marginExecution), maxMargin) + } + + if (confront.marginPreservation > 0) { // Successful defense + // Limit with skill+spec + maxMargin = confront.rollData1.skill.value + ((confront.rollData1.spec) ? 2 : 0) + confront.marginPreservation = Math.min(confront.marginPreservation, maxMargin) + } else { // Failed defense + maxMargin = confront.rollData2.skill.value + ((confront.rollData2.spec) ? 2 : 0) + confront.marginPreservation = - Math.min(Math.abs(confront.marginPreservation), maxMargin) + } + + // Compute effects + confront.effectExecution = confront.marginExecution + if (confront.rollData1.weapon && confront.marginExecution > 0) { + confront.effectExecution += confront.rollData1.weapon.system.effect + confront.impactExecution = this.getImpactFromEffect(confront.effectExecution) + } + if (confront.marginExecution < 0) { + confront.bonus2 = -confront.marginExecution + } + confront.effectPreservation = confront.marginPreservation + if (confront.rollData2.weapon && confront.marginPreservation < 0) { + confront.effectPreservation = - (Math.abs(confront.marginPreservation) + confront.rollData2.weapon.system.effect) + confront.impactPreservation = this.getImpactFromEffect(Math.abs(confront.effectPreservation)) + } + if (confront.marginPreservation > 0) { + confront.bonus1 = -confront.marginPreservation + } + + let msg = await this.createChatWithRollMode(this.confrontData1.alias, { + content: await renderTemplate(`systems/fvtt-ecryme/templates/chat/chat-confrontation-result.hbs`, confront) + }) + await msg.setFlag("world", "ecryme-rolldata", confront) + console.log("Confront result", confront) + + this.lastConfront = confront + } + /* -------------------------------------------- */ + static async manageCephalyDifficulty(rollData, difficulty) { + rollData.difficulty = Number(difficulty) + if (rollData.executionTotal > difficulty) { + rollData.marginExecution = rollData.executionTotal - difficulty + rollData.cephalySuccess = "ECRY.rule." + __cephalySuccess[(rollData.marginExecution > 10) ? 10 : rollData.marginExecution] + } else { + rollData.marginExecution = -1 + } + if (rollData.preservationTotal < difficulty) { + rollData.marginPreservation = difficulty - rollData.preservationTotal + rollData.cephalyFailure = "ECRY.rule." + __cephalyFailure[(rollData.marginPreservation > 10) ? 10 : rollData.marginPreservation] + } else { + rollData.marginPreservation = -1 + } + let msg = await this.createChatWithRollMode(rollData.alias, { + content: await renderTemplate(`systems/fvtt-ecryme/templates/chat/chat-cephaly-result.hbs`, rollData) + }) + msg.setFlag("world", "ecryme-rolldata", rollData) + console.log("Cephaly result", rollData) + } + + /* -------------------------------------------- */ + static manageConfrontation(rollData) { + console.log("Confront", rollData) + // Auto - Reset + if (this.confrontData1 && this.confrontData2) { + this.confrontData1 = undefined + this.confrontData2 = undefined + } + // Then attribute + if (!this.confrontData1) { + this.confrontData1 = rollData + } else if (this.confrontData1 && this.confrontData1.rollId != rollData.rollId) { + this.confrontData2 = rollData + this.processConfrontation().catch("Error during confrontation processing") + } else { + ui.notifications.warn(game.i18n.localize("ECRY.warn.confrontalready")) + } + } + + /* -------------------------------------------- */ + static chatMenuManager(html, options) { + let canTranscendRoll = [] + for (let i = 1; i <= 10; i++) { + canTranscendRoll[i] = function (li) { + let message = game.messages.get(li.attr("data-message-id")) + let rollData = message.getFlag("world", "rolldata") + //console.log(">>>>>>>>>>>>>>>>>>>>>>>>>> Menu !!!!", rollData) + if (rollData.skill && i <= rollData.skill.value && !rollData.transcendUsed && rollData.spec) { + return true + } + return false + } + options.push({ + name: game.i18n.localize("ECRY.chat.spectranscend") + i, + icon: '', + condition: canTranscendRoll[i], + callback: li => { + let message = game.messages.get(li.attr("data-message-id")) + let rollData = message.getFlag("world", "rolldata") + EcrymeUtility.transcendFromSpec(rollData, i).catch("Error on Transcend") + } + }) + } + } + + /* -------------------------------------------- */ + static async chatListeners(html) { + + html.on("click", '.button-select-confront', event => { + let messageId = EcrymeUtility.findChatMessageId(event.currentTarget) + let message = game.messages.get(messageId) + let rollData = message.getFlag("world", "ecryme-rolldata") + ui.notifications.info( game.i18n.localize("ECRY.chat.confrontselect")) + EcrymeUtility.manageConfrontation(rollData) + }) + html.on("click", '.button-apply-cephaly-difficulty', event => { + let messageId = EcrymeUtility.findChatMessageId(event.currentTarget) + let message = game.messages.get(messageId) + let rollData = message.getFlag("world", "ecryme-rolldata") + let difficulty = $("#" + rollData.rollId + "-cephaly-difficulty").val() + EcrymeUtility.manageCephalyDifficulty(rollData, difficulty) + }) + html.on("click", '.button-apply-impact', event => { + let messageId = EcrymeUtility.findChatMessageId(event.currentTarget) + let message = game.messages.get(messageId) + let actor = game.actors.get($(event.currentTarget).data("actor-id")) + actor.modifyImpact($(event.currentTarget).data("impact-type"), $(event.currentTarget).data("impact"), 1) + }) + html.on("click", '.button-apply-bonus', event => { + let messageId = EcrymeUtility.findChatMessageId(event.currentTarget) + let message = game.messages.get(messageId) + let actor = game.actors.get($(event.currentTarget).data("actor-id")) + actor.modifyConfrontBonus($(event.currentTarget).data("bonus")) + }) + + } + + /* -------------------------------------------- */ + static async preloadHandlebarsTemplates() { + + const templatePaths = [ + 'systems/fvtt-ecryme/templates/actors/editor-notes-gm.hbs', + 'systems/fvtt-ecryme/templates/items/partial-item-nav.hbs', + 'systems/fvtt-ecryme/templates/items/partial-item-equipment.hbs', + 'systems/fvtt-ecryme/templates/items/partial-item-description.hbs', + 'systems/fvtt-ecryme/templates/dialogs/partial-common-roll-dialog.hbs', + 'systems/fvtt-ecryme/templates/dialogs/partial-confront-dice-area.hbs', + 'systems/fvtt-ecryme/templates/dialogs/partial-confront-bonus-area.hbs', + 'systems/fvtt-ecryme/templates/actors/partial-impacts.hbs', + ] + return loadTemplates(templatePaths); + } + + /* -------------------------------------------- */ + static removeChatMessageId(messageId) { + if (messageId) { + game.messages.get(messageId)?.delete(); + } + } + + static findChatMessageId(current) { + return EcrymeUtility.getChatMessageId(EcrymeUtility.findChatMessage(current)); + } + + static getChatMessageId(node) { + return node?.attributes.getNamedItem('data-message-id')?.value; + } + + static findChatMessage(current) { + return EcrymeUtility.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 EcrymeUtility.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) { + for (let target of game.user.targets) { + return target + } + } + return undefined + } + + /* -------------------------------------------- */ + static updateRollData(rollData) { + + let id = rollData.rollId + let oldRollData = this.rollDataStore[id] || {} + let newRollData = mergeObject(oldRollData, rollData) + this.rollDataStore[id] = newRollData + } + + /* -------------------------------------------- */ + static async onSocketMesssage(msg) { + console.log("SOCKET MESSAGE", msg) + if (msg.name == "msg_gm_chat_message") { + let rollData = msg.data.rollData + if ( game.user.isGM ) { + let chatMsg = await this.createChatMessage(rollData.alias, "blindroll", { + content: await renderTemplate(msg.data.template, rollData), + whisper: game.user.id + }) + chatMsg.setFlag("world", "ecryme-rolldata", rollData) + } + } + } + + /* -------------------------------------------- */ + static async searchItem(dataItem) { + let item + if (dataItem.pack) { + let id = dataItem.id || dataItem._id + let items = await this.loadCompendium(dataItem.pack, item => item.id == id) + item = items[0] || undefined + } else { + item = game.items.get(dataItem.id) + } + return item + } + + /* -------------------------------------------- */ + static chatDataSetup(content, modeOverride, forceWhisper, isRoll = false) { + 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 getImpactMax(impactLevel) { + return __maxImpacts[impactLevel] + } + static getNextImpactLevel(impactLevel) { + return __nextImpacts[impactLevel] + } + /* -------------------------------------------- */ + 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 + whisper = this.getUsers(user => user.isGM); + blind = true; + break + 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 computeResults(rollData) { + rollData.isSuccess = false + if (!rollData.difficulty || rollData.difficulty == "-") { + return + } + rollData.margin = rollData.total - rollData.difficulty + if (rollData.total > rollData.difficulty) { + rollData.isSuccess = true + let maxMargin = rollData.skill.value + ((rollData.spec) ? 2 : 0) + rollData.margin = Math.min(rollData.margin, maxMargin) + } + } + + /* -------------------------------------------- */ + static computeRollFormula(rollData, actor, isConfrontation = false) { + // Build the dice formula + let diceFormula = (isConfrontation) ? "4d6" : "2d6" + if (rollData.useIdeal) { + diceFormula = (isConfrontation) ? "5d6kh2" : "3d6kh2" + } + if (rollData.useSpleen) { + diceFormula = (isConfrontation) ? "5d6kl2" : "3d6kl2" + } + if (rollData.skill) { + diceFormula += "+" + rollData.skill.value + } + if (rollData.skillTranscendence) { + diceFormula += "+" + rollData.skillTranscendence + actor.spentSkillTranscendence(rollData.skill, rollData.skillTranscendence) + } + if (rollData.selectedSpecs && rollData.selectedSpecs.length > 0) { + rollData.spec = actor.getSpecialization(rollData.selectedSpecs[0]) + diceFormula += "+" + (String(rollData.spec.system?.bonus) || "2") + } + rollData.bonusMalusTraits = 0 + if (rollData.traitsBonus && rollData.traitsBonus.length > 0) { + rollData.traitsBonusList = [] + for (let id of rollData.traitsBonus) { + let trait = actor.getTrait(id) + console.log(trait, id) + rollData.traitsBonusList.push(trait) + rollData.bonusMalusTraits += trait.system.level + } + } + if (rollData.traitsMalus && rollData.traitsMalus.length > 0) { + rollData.traitsMalusList = [] + for (let id of rollData.traitsMalus) { + let trait = actor.getTrait(id) + rollData.traitsMalusList.push(trait) + rollData.bonusMalusTraits -= trait.system.level + } + } + diceFormula += "+" + rollData.bonusMalusTraits + diceFormula += "+" + rollData.bonusMalusPerso + diceFormula += "+" + rollData.impactMalus + if (rollData.annency) { + diceFormula += "+" + rollData.annencyBonus + } + rollData.diceFormula = diceFormula + return diceFormula + } + + /* -------------------------------------------- */ + static async rollEcryme(rollData) { + + let actor = game.actors.get(rollData.actorId) + // Fix difficulty + if (!rollData.difficulty || rollData.difficulty == "-") { + rollData.difficulty = 0 + } + rollData.difficulty = Number(rollData.difficulty) + + let diceFormula = this.computeRollFormula(rollData, actor) + + // Performs roll + let myRoll = new Roll(diceFormula).roll({ async: false }) + await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode")) + rollData.roll = duplicate(myRoll) + rollData.total = myRoll.total + rollData.diceSum = myRoll.terms[0].total + + this.computeResults(rollData) + + let msg = await this.createChatWithRollMode(rollData.alias, { + content: await renderTemplate(`systems/fvtt-ecryme/templates/chat/chat-generic-result.hbs`, rollData) + }) + await msg.setFlag("world", "ecryme-rolldata", rollData) + console.log("Rolldata result", rollData) + } + + /* -------------------------------------------- */ + static async transcendFromSpec(rollData, value) { + rollData.total += value + rollData.transcendUsed = true + this.computeResults(rollData) + //console.log("Adding spec", value, rollData.total) + + let actor = game.actors.get(rollData.actorId) + actor.spentSkillTranscendence(rollData.skill, value) + + let msg = await this.createChatWithRollMode(rollData.alias, { + content: await renderTemplate(`systems/fvtt-ecryme/templates/chat/chat-generic-result.hbs`, rollData) + }) + await msg.setFlag("world", "ecryme-rolldata", rollData) + } + + /* -------------------------------------------- */ + static sortArrayObjectsByName(myArray) { + myArray.sort((a, b) => { + let fa = a.name.toLowerCase(); + let fb = b.name.toLowerCase(); + if (fa < fb) { + return -1; + } + if (fa > fb) { + return 1; + } + return 0; + }) + } + + /* -------------------------------------------- */ + static getUsers(filter) { + return game.users.filter(filter).map(user => user.id); + } + /* -------------------------------------------- */ + static getWhisperRecipients(rollMode, name) { + switch (rollMode) { + case "blindroll": return this.getUsers(user => user.isGM); + case "gmroll": return this.getWhisperRecipientsAndGMs(name); + case "useronly": return this.getWhisperRecipientsOnly(name); + case "selfroll": return [game.user.id]; + } + return undefined; + } + /* -------------------------------------------- */ + static getWhisperRecipientsOnly(name) { + let recep1 = ChatMessage.getWhisperRecipients(name) || []; + return recep1 + } + /* -------------------------------------------- */ + static getWhisperRecipientsAndGMs(name) { + let recep1 = ChatMessage.getWhisperRecipients(name) || []; + return recep1.concat(ChatMessage.getWhisperRecipients('GM')); + } + + /* -------------------------------------------- */ + static blindMessageToGM(chatData) { + chatData.whisper = this.getUsers(user => user.isGM); + console.log("blindMessageToGM", chatData); + game.socket.emit("system.fvtt-ecryme", { name: "msg_gm_chat_message", data: chatData }); + } + + + /* -------------------------------------------- */ + 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) { + switch (rollMode) { + case "blindroll": // GM only + if (!game.user.isGM) { + chatOptions.whisper = [game.user.id]; + } else { + chatOptions.whisper = this.getUsers(user => user.isGM); + } + break; + default: + chatOptions.whisper = this.getWhisperRecipients(rollMode, name); + break; + } + chatOptions.alias = chatOptions.alias || name; + return await ChatMessage.create(chatOptions); + } + + /* -------------------------------------------- */ + static getBasicRollData() { + let rollData = { + rollId: randomID(16), + type: "roll-data", + bonusMalusPerso: 0, + bonusMalusSituation: 0, + bonusMalusDef: 0, + annencyBonus: 0, + bonusMalusPortee: 0, + skillTranscendence: 0, + rollMode: game.settings.get("core", "rollMode"), + difficulty: "-", + useSpleen: false, + useIdeal: false, + impactMalus: 0, + config: duplicate(game.system.ecryme.config) + } + EcrymeUtility.updateWithTarget(rollData) + return rollData + } + + /* -------------------------------------------- */ + static updateWithTarget(rollData) { + let target = EcrymeUtility.getTarget() + if (target) { + rollData.defenderTokenId = target.id + } + } + + /* -------------------------------------------- */ + static async createChatWithRollMode(name, chatOptions) { + return await this.createChatMessage(name, game.settings.get("core", "rollMode"), chatOptions) + } + + /* -------------------------------------------- */ + 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/modules/dialogs/tedeum-roll-dialog.js b/modules/dialogs/tedeum-roll-dialog.js new file mode 100644 index 0000000..2fa4833 --- /dev/null +++ b/modules/dialogs/tedeum-roll-dialog.js @@ -0,0 +1,86 @@ +import { EcrymeUtility } from "../common/ecryme-utility.js"; + +export class EcrymeRollDialog extends Dialog { + + /* -------------------------------------------- */ + static async create(actor, rollData) { + + let options = { classes: ["ecryme-roll-dialog"], width: 540, height: 'fit-content', 'z-index': 99999 } + let html = await renderTemplate('systems/fvtt-ecryme/templates/dialogs/roll-dialog-generic.hbs', rollData); + return new EcrymeRollDialog(actor, rollData, html, options); + } + + /* -------------------------------------------- */ + constructor(actor, rollData, html, options, close = undefined) { + let conf = { + title: game.i18n.localize("ECRY.ui.rolltitle"), + content: html, + buttons: { + roll: { + icon: '', + label: game.i18n.localize("ECRY.ui.roll"), + callback: () => { this.roll() } + }, + cancel: { + icon: '', + label: game.i18n.localize("ECRY.ui.cancel"), + callback: () => { this.close() } + } + }, + close: close + } + + super(conf, options); + + this.actor = actor; + this.rollData = rollData; + } + + /* -------------------------------------------- */ + roll() { + EcrymeUtility.rollEcryme(this.rollData) + } + + /* -------------------------------------------- */ + async refreshDialog() { + const content = await renderTemplate("systems/fvtt-ecryme/templates/dialogs/roll-dialog-generic.hbs", this.rollData) + this.data.content = content + this.render(true) + } + + /* -------------------------------------------- */ + activateListeners(html) { + super.activateListeners(html); + + var dialog = this; + function onLoad() { + } + $(function () { onLoad(); }); + + html.find('#bonusMalusPerso').change((event) => { + this.rollData.bonusMalusPerso = Number(event.currentTarget.value) + }) + html.find('#roll-difficulty').change((event) => { + this.rollData.difficulty = Number(event.currentTarget.value) || 0 + }) + html.find('#roll-specialization').change((event) => { + this.rollData.selectedSpecs = $('#roll-specialization').val() + }) + html.find('#roll-trait-bonus').change((event) => { + this.rollData.traitsBonus = $('#roll-trait-bonus').val() + }) + html.find('#roll-trait-malus').change((event) => { + this.rollData.traitsMalus = $('#roll-trait-malus').val() + }) + html.find('#roll-select-transcendence').change((event) => { + this.rollData.skillTranscendence = Number($('#roll-select-transcendence').val()) + }) + html.find('#roll-use-spleen').change((event) => { + this.rollData.useSpleen = event.currentTarget.checked + }) + html.find('#roll-use-ideal').change((event) => { + this.rollData.useIdeal = event.currentTarget.checked + }) + + } +} \ No newline at end of file diff --git a/modules/items/tedeum-item-sheet.js b/modules/items/tedeum-item-sheet.js new file mode 100644 index 0000000..8352025 --- /dev/null +++ b/modules/items/tedeum-item-sheet.js @@ -0,0 +1,185 @@ +import { EcrymeUtility } from "../common/tedeum-utility.js"; + +/** + * Extend the basic ItemSheet with some very simple modifications + * @extends {ItemSheet} + */ +export class EcrymeItemSheet extends ItemSheet { + + /** @override */ + static get defaultOptions() { + return mergeObject(super.defaultOptions, { + classes: ["fvtt-ecryme", "sheet", "item"], + template: "systems/fvtt-ecryme/templates/item-sheet.hbs", + dragDrop: [{ dragSelector: null, dropSelector: null }], + width: 620, + height: 580, + 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() { + + let formData = { + title: this.title, + id: this.id, + type: this.object.type, + img: this.object.img, + name: this.object.name, + editable: this.isEditable, + cssClass: this.isEditable ? "editable" : "locked", + system: duplicate(this.object.system), + config: duplicate(game.system.ecryme.config), + limited: this.object.limited, + options: this.options, + owner: this.document.isOwner, + description: await TextEditor.enrichHTML(this.object.system.description, { async: true }), + notes: await TextEditor.enrichHTML(this.object.system.notes, { async: true }), + isGM: game.user.isGM + } + + if ( this.object.type == "archetype") { + formData.tarots = EcrymeUtility.getTarots() + } + + 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(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/Ecryme/templates/post-item.html', chatData).then(html => { + let chatOptions = EcrymeUtility.chatDataSetup(html); + ChatMessage.create(chatOptions) + }); + } + + /* -------------------------------------------- */ + async viewSubitem(ev) { + let levelIndex = Number($(ev.currentTarget).parents(".item").data("level-index")) + let choiceIndex = Number($(ev.currentTarget).parents(".item").data("choice-index")) + let featureId = $(ev.currentTarget).parents(".item").data("feature-id") + + let itemData = this.object.system.levels[levelIndex].choices[choiceIndex].features[featureId] + + if (itemData.name != 'None') { + let item = await Item.create(itemData, { temporary: true }); + item.system.origin = "embeddedItem"; + new EcrymeItemSheet(item).render(true); + } + } + + /* -------------------------------------------- */ + async deleteSubitem(ev) { + let field = $(ev.currentTarget).data('type'); + let idx = Number($(ev.currentTarget).data('index')); + let oldArray = this.object.system[field]; + let itemData = this.object.system[field][idx]; + if (itemData.name != 'None') { + let newArray = []; + for (var i = 0; i < oldArray.length; i++) { + if (i != idx) { + newArray.push(oldArray[i]); + } + } + this.object.update({ [`system.${field}`]: newArray }); + } + } + + /* -------------------------------------------- */ + /** @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); + }); + + // 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-ecryme/templates/items/item-${type}-sheet.hbs` + } + + /* -------------------------------------------- */ + /** @override */ + _updateObject(event, formData) { + return this.object.update(formData) + } +} \ No newline at end of file diff --git a/modules/items/tedeum-item.js b/modules/items/tedeum-item.js new file mode 100644 index 0000000..b7362dd --- /dev/null +++ b/modules/items/tedeum-item.js @@ -0,0 +1,27 @@ +import { EcrymeUtility } from "../common/ecryme-utility.js"; + +export const defaultItemImg = { + weapon: "systems/fvtt-ecryme/images/icons/icon_weapon.webp", + equipment: "systems/fvtt-ecryme/images/icons/icon_equipment.webp", + contact: "systems/fvtt-ecryme/images/icons/icon_contact.webp", + boheme: "systems/fvtt-ecryme/images/icons/icon_boheme.webp", + trait: "systems/fvtt-ecryme/images/icons/icon_trait.webp", + annency: "systems/fvtt-ecryme/images/icons/icon_annency.webp", + skill: "systems/fvtt-ecryme/images/icons/icon_skill.webp", + specialization: "systems/fvtt-ecryme/images/icons/icon_spec.webp" +} + +/** + * Extend the basic ItemSheet with some very simple modifications + * @extends {ItemSheet} + */ +export class EcrymeItem extends Item { + + constructor(data, context) { + if (!data.img) { + data.img = defaultItemImg[data.type]; + } + super(data, context); + } + +} diff --git a/modules/tedeum-main.js b/modules/tedeum-main.js new file mode 100644 index 0000000..defda9b --- /dev/null +++ b/modules/tedeum-main.js @@ -0,0 +1,147 @@ +/** + * Ecryme system + * Author: Uberwald + * Software License: Prop + */ + +/* -------------------------------------------- */ + +/* -------------------------------------------- */ +// Import Modules +import { EcrymeActor } from "./actors/ecryme-actor.js"; +import { EcrymeItemSheet } from "./items/tedeum-item-sheet.js"; +import { EcrymeActorSheet } from "./actors/ecryme-actor-sheet.js"; +import { EcrymeAnnencySheet } from "./actors/ecryme-annency-sheet.js"; +import { EcrymeUtility } from "./common/ecryme-utility.js"; +import { EcrymeCombat } from "./app/ecryme-combat.js"; +import { EcrymeItem } from "./items/ecryme-item.js"; +import { EcrymeHotbar } from "./app/ecryme-hotbar.js" +import { EcrymeCharacterSummary } from "./app/ecryme-summary-app.js" +import { ECRYME_CONFIG } from "./common/ecryme-config.js" + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ + +/************************************************************************************/ +Hooks.once("init", async function () { + + console.log(`Initializing Ecryme RPG`); + + game.system.ecryme = { + config: ECRYME_CONFIG, + EcrymeHotbar + } + + /* -------------------------------------------- */ + // preload handlebars templates + EcrymeUtility.preloadHandlebarsTemplates(); + + /* -------------------------------------------- */ + // Set an initiative formula for the system + CONFIG.Combat.initiative = { + formula: "1d6", + decimals: 1 + }; + + /* -------------------------------------------- */ + game.socket.on("system.fvtt-ecryme", data => { + EcrymeUtility.onSocketMesssage(data) + }); + + /* -------------------------------------------- */ + // Define custom Entity classes + CONFIG.Combat.documentClass = EcrymeCombat + CONFIG.Actor.documentClass = EcrymeActor + CONFIG.Item.documentClass = EcrymeItem + + /* -------------------------------------------- */ + // Register sheet application classes + Actors.unregisterSheet("core", ActorSheet); + Actors.registerSheet("fvtt-ecryme", EcrymeActorSheet, { types: ["pc"], makeDefault: true }); + Actors.registerSheet("fvtt-ecryme", EcrymeActorSheet, { types: ["npc"], makeDefault: true }); + Actors.registerSheet("fvtt-ecryme", EcrymeAnnencySheet, { types: ["annency"], makeDefault: false }); + + Items.unregisterSheet("core", ItemSheet); + Items.registerSheet("fvtt-ecryme", EcrymeItemSheet, { makeDefault: true }); + + EcrymeUtility.init() + + console.log("Babele INIT!") + Babele.get().setSystemTranslationsDir("translated") + +}); + +/* -------------------------------------------- */ +function welcomeMessage() { + if (game.user.isGM) { + ChatMessage.create({ + user: game.user.id, + whisper: [game.user.id], + content: `
+ Bienvenu dans Ecryme !` }); + } +} + +/* -------------------------------------------- */ +// 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 () { + + // User warning + if (!game.user.isGM && game.user.character == undefined) { + ui.notifications.info("Attention ! Aucun personnage relié au joueur !"); + ChatMessage.create({ + content: "WARNING Le joueur " + game.user.name + " n'est pas relié à un personnage !", + user: game.user._id + }); + } + + registerUsageCount(game.system.id) + welcomeMessage(); + EcrymeUtility.ready() + EcrymeCharacterSummary.ready() + +}) + + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ +Hooks.on("chatMessage", (html, content, msg) => { + if (content[0] == '/') { + let regExp = /(\S+)/g; + let commands = content.match(regExp); + if (game.system.ecryme.commands.processChatCommand(commands, content, msg)) { + return false; + } + } + return true; +}); + diff --git a/postcss/tedeum.css b/postcss/tedeum.css new file mode 100644 index 0000000..d3571b5 --- /dev/null +++ b/postcss/tedeum.css @@ -0,0 +1,1409 @@ + /* ==================== (A) Fonts ==================== */ + @font-face { + font-family: "MailartRubberstamp"; + src: url('../fonts/MailartRubberstamp-Regular.woff') format("woff"); + } + + :root { + /* =================== 1. ACTOR SHEET FONT STYLES =========== */ + --window-header-title-font-size: 1.3rem; + --window-header-title-font-weight: normal; + --window-header-title-color: #f5f5f5; + + --major-button-font-size: 1.05rem; + --major-button-font-weight: normal; + --major-button-color: #dadada; + + --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 { + text-align: justify; + font-size: 16px; + letter-spacing: 1px; +} + +/* Fonts */ +.sheet header.sheet-header h1 input, .window-app .window-header, #actors .directory-list, #navigation #scene-list .scene.nav-item { + font-size: 1.0rem; +} /* For title, sidebar character and scene */ +.sheet nav.sheet-tabs { + font-size: 0.8rem; +} /* For nav and title */ +.window-app input, .fvtt-ecryme .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:hover { + border-width: 4px; + border-color: rgba(37, 124, 37, 0.7); +} + +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 sheets */ +.fvtt-ecryme .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-ecryme .sheet-header .profile-img { + -webkit-box-flex: 0; + -ms-flex: 0 0 128px; + flex: 0 0 128px; + width: 128px; + height: auto; + max-height:128px; + margin-top: 0px; + margin-right: 10px; + object-fit: cover; + object-position: 50% 0; + border-width: 0px; +} +.profile-img-container { + margin-right: 0.2rem; + max-width: 140px; + width: 140px; +} + +.button-img { + vertical-align: baseline; + width: 8%; + height: 8%; + max-height: 48px; + border-width: 0px; + 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-ecryme .sheet-header .header-fields { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.fvtt-ecryme .sheet-header h1.charname { + height: 50px; + padding: 0px; + margin: 5px 0; + border-bottom: 0; +} + +.fvtt-ecryme .sheet-header h1.charname input { + font-family: MailartRubberstamp; + font-size: 3rem; + width: 100%; + height: 100%; + margin: 0; +} + +.fvtt-ecryme .sheet-tabs { + -webkit-box-flex: 0; + -ms-flex: 0; + flex: 0; + font-family: MailartRubberstamp; + font-size: 2.2rem; +} + +.fvtt-ecryme .sheet-body, +.fvtt-ecryme .sheet-body .tab, +.fvtt-ecryme .sheet-body .tab .editor { + height: 100%; + font-size: 0.8rem; +} + +.editor { + border: 2; + height: 100%; + padding: 0 3px; +} + +.medium-editor { + border: 2; + height: 240px; + padding: 0 3px; +} + +.small-editor { + border: 2; + height: 120px; + padding: 0 3px; +} + +.fvtt-ecryme .tox .tox-editor-container { + background: #fff; +} + +.fvtt-ecryme .tox .tox-edit-area { + padding: 0 8px; +} + +.fvtt-ecryme .resource-label { + font-weight: bold; + text-transform: uppercase; +} + +.fvtt-ecryme .tabs { + height: 40px; + border-top: 1px solid #AAA; + border-bottom: 1px solid #AAA; + color: #000000; +} + +.fvtt-ecryme .tabs .item { + line-height: 40px; + font-weight: bold; +} + +.fvtt-ecryme .tabs .item.active { + text-decoration: underline; + text-shadow: none; +} + +.fvtt-ecryme .items-list { + list-style: none; + margin: 1px 0; + padding: 0; + overflow-y: auto; +} + +.fvtt-ecryme .items-list .item-header { + font-weight: bold; +} + +.fvtt-ecryme .items-list .item { + height: 30px; + line-height: 24px; + padding: 1px 0; + border-bottom: 1px solid #BBB; +} + +.fvtt-ecryme .items-list .item .item-image { + -webkit-box-flex: 0; + -ms-flex: 0 0 24px; + flex: 0 0 24px; + margin-right: 5px; +} + +.fvtt-ecryme .items-list .item img { + display: block; +} + +.fvtt-ecryme .items-list .item-name { + margin: 0; +} + +.fvtt-ecryme .items-list .item-controls { + -webkit-box-flex: 0; + -ms-flex: 0 0 86px; + flex: 0 0 86px; + text-align: right; +} + +li.folder > .folder-header h3 { + color: rgba(19, 18, 18, 0.95); +} + +/* ======================================== */ +/* Sheet */ +.window-app.sheet .window-content .sheet-header{ + color: rgba(19, 18, 18, 0.95); + background: url("../images/ui/fond_carnet_01.webp"); + /*background: #494e6b;*/ +} + +input[type="text"], select[type="text"] { + background:white; + color: #494e6b; +} + +select { + background:white; + color: #494e6b; +} +/* background: #011d33 url("../images/ui/fond1.webp") repeat left top;*/ +/*color: rgba(168, 139, 139, 0.5);*/ +.window-app.sheet .window-content .sheet-header select[type="text"], .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-body input[type="text"], .window-app.sheet .window-content .sheet-body input[type="number"], .window-app.sheet .window-content .sheet-body select[type="text"] { + color: rgba(19, 18, 18, 0.95); + /*color: #494e6b;*/ +} + +.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(19, 18, 18, 0.95); + background: url("../images/ui/fond_carnet_01.webp"); + border: 1 none; + margin-bottom: 0.25rem; + margin-left: 2px; +} + +.window-app.sheet .window-content .sheet-body input[type="password"], .window-app.sheet .window-content .sheet-body input[type="date"], .window-app.sheet .window-content .sheet-body input[type="time"] { + color: rgba(19, 18, 18, 0.95); + background: url("../images/ui/fond_carnet_01.webp"); + border: 1 none; + margin-bottom: 0.25rem; + margin-left: 2px; +} + +.window-app.sheet .window-content .sheet-body select, .window-app.sheet .window-content .sheet-header select { + color: rgba(19, 18, 18, 0.95); + background: #fff; + border: 1 none; + margin-bottom: 0.25rem; + margin-left: 2px; +} + +.window-app .window-content, .window-app.sheet .window-content .sheet-body{ + font-size: 0.8rem; + /*background: url("../images/ui/pc_sheet_bg.webp") repeat left top;*/ + background: url("../images/ui/fond_carnet_01.webp"); + color: rgba(19, 18, 18, 0.95); +} + +/* 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: 1.2rem; + 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:#252525; + 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; + display: flex !important; + color: rgba(19, 18, 18, 0.95); +} +.list-item-shadow { + background:rgba(87, 60, 32, 0.35); + flex-grow: 0; + flex-wrap: nowrap; + justify-content: flex-start; +} +.list-item-shadow2 { + background:rgba(87, 60, 32, 0.25); + flex-grow: 0; + flex-wrap: nowrap; + justify-content: flex-start; +} +.item-display-show { + display: block; +} +.item-display-hide { + display: none; +} +.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; + max-width: 24px; + height: 24px; + max-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; +} + +.description-label { + flex-grow: 2; + margin-left: 4px; +} +.status-header-label { + margin-left: 2px; +} +.roll-dialog-label { + margin: 4px 0; + min-width: 96px; +} +.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; +} +.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; +} +.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; +} + +.abilities-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; +} +.token-sheet .window-content .flexcol .sheet-tabs { + font-size: 0.8rem; +} + +/* ======================================== */ +.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; +} + +.skill-label { + font-size: 0.7rem; +} +.skill-good-checkbox { + max-height: 10px; + max-width: 10px; +} + +.flex-actions-bar { + flex-grow: 2; +} + +/* ======================================== */ +/* Sidebar CSS */ +#sidebar { + font-size: 1rem; + /*background-position: 100%;*/ + background-color:#f5f5f5; + background-position: 0px 35px; + background-repeat: no-repeat; + background-image: url("../images/ui/fond_carnet_01.webp"); + color: rgba(19, 18, 18, 0.95); +} + +/* 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(19, 18, 18, 0.95); + 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-result-text, +.chat-actor-name { + font-weight: bold; + font-family: MailartRubberstamp; + font-size: 1.2rem; + padding: 4px; +} +.chat-result-success { + color:darkgreen; +} +.chat-result-failure { + color:darkred; +} +.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-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; +} + +.ability-icon { + border: 0; + padding: 2px 2px 2px 2px; + max-width:32px; + max-height:32px; + width: auto; + height: auto; +} +.small-ability-icon { + border: 0; + padding: 2px 2px 2px 2px; + max-width:16px; + max-height:16px; + width: auto; + height: auto; +} +.combat-icon { + border: 0; + padding: 2px 2px 2px 2px; + max-width:24px; + max-height:24px; + width: auto; + height: auto; +} + +#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{ + display: inline-block; + background-position:center; + background-size:cover; + text-shadow: 1px 1px 0 rgba(0,0,0,0.75); + +} + +/*--------------------------------------------------------------------------*/ +/* 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-nobottom { + border-bottom: unset; /* If you want dots under the hoverable text */ +} + +/* Show the tooltip text when you mouse over the tooltip container */ +.tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; +} + +.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 12px 0px 12px; + 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; +} + +.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: 2px; + border: 1px ridge #846109; + display: inline-block; + cursor: pointer; + color: #ffffff; + margin: 2px 2px 2px 2px; + padding: 2px 2px 2px 2px; + text-decoration: none; + text-shadow: 0px 1px 0px #4d3534; + position: relative; + margin:0px; +} + +.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; +} + + +/*************************************************************/ +#pause +{ + font-size: 2rem; +} +#pause > h3 +{ + color: #CCC +} +#pause > img { + content: url(../images/ui/ecryme_logo_small_01.webp); + height: 200px; + width: 200px; + top: -200px; + left: calc(50% - 132px); +} + +#logo { + content : url(../images/ui/ecryme_logo_small_01.webp); + width: 100px; + height: 60px; +} + +.dice-cell { + padding-left: 12px; + padding-right: 12px; + width: 60px; + text-align: center; +} + +.dice-formula, +.dice-total { + height: 54px; + position:relative; +} + +.status-small-label { + font-size: 0.65rem; +} +.no-grow { + flex-grow: 1; + max-width: 32px; +} +.status-col-name { + max-width: 72px; +} +.img-no-border { + max-width: 48px; + max-height: 48px; + border: 0px; +} +.items-title-bg { + margin-top: 6px; + color: rgba(19, 18, 18, 0.95); + +} +.impact-box { + border-width: 2px; + border-color: #000000; + border-radius: 6px; + border: 2px ridge #443307; + margin: 4px; + padding: 4px; +} +.impact-title { + font-size: bold; + display: flex; + align-items: center; + justify-content: center; + margin-left: auto; + margin-right: auto; + text-align: center; +} + +.items-title-text { + text-align: center; + font-family: MailartRubberstamp; + margin-left: 4px; +} +.lock-icon { + width:16px; + height: 16px; +} +.item-sheet-img { + width: 64px; + height: auto; + border: 0; +} +.item-name-img { + flex-grow:1; + max-width: 2rem; + min-width: 2rem; +} +.item-name-label-header { + flex-grow:2; + max-width: 12rem; + min-width: 12rem; +} +.item-name-label-header-long { + flex-grow:2; + max-width: 14rem; + min-width: 14rem; +} +.item-name-label-header-long2 { + flex-grow:2; + max-width: 24rem; + min-width: 24rem; +} +.item-name-label { + flex-grow:2; + max-width: 10rem; + min-width: 10rem; +} +.item-name-label-long { + margin-top: 4px; + flex-grow:2; + max-width: 12rem; + min-width: 12rem; +} +.item-name-label-short { + flex-grow:1; + max-width: 4rem; + min-width: 4rem; +} +.item-name-label-medium { + margin-top: 4px; + flex-grow:2; + max-width: 6rem; + min-width: 6rem; +} +.item-name-label-long2 { + margin-top: 4px; + flex-grow:2; + max-width: 22rem; + min-width: 22rem; +} +.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-skill { + flex-grow:1; + max-width: 6.8rem; + min-width: 6.8rem; +} +.item-field-label-long { + flex-grow:1; + max-width: 10rem; + min-width: 10rem; +} +.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:2rem; + max-width: 2rem; +} +.item-controls-fixed-full { + min-width:3rem; + max-width: 3rem; +} +.attribute-label { + font-weight: bold; +} +.flexrow-no-expand { + flex-grow: 0; +} +.item-input-small { + max-width: 16px; + max-height: 12px; +} +.character-summary-rollable { + text-decoration: underline; +} + +.ecryme-roll-dialog .window-header { + border-radius: 10px 10px 0% 0%; +} +.ecryme-roll-dialog .window-content { + border-radius: 0% 0% 10px 10px; +} + +.skill-roll-dialog div { + margin-top: 4px; + margin-bottom: 4px; +} + +.confront-dice { + border-width: 0px; +} + +.bonus-spec { + max-width: 48px; +} +.confront-bonus-container { + position: relative; + flex-grow: 1; + text-align: center; + color: black; +} +.pool-list { + align-items: center; + justify-content: center; +} +.confront-bonus-centered { + position: absolute; + top: 50%; + left: 50%; + font-size: 1.6rem; + color: darkgreen; + font-family: MailartRubberstamp; + transform: translate(-50%, -55%); +} + +.dice-spec { + max-width: 64px; +} +.confront-dice-container { + position: relative; + flex-grow: 1; + text-align: center; + color: black; +} +.confront-dice-centered { + position: absolute; + top: 50%; + left: 50%; + font-size: 2rem; + color: darkgreen; + font-family: MailartRubberstamp; + transform: translate(-50%, -55%); +} +.confront-area { + margin: 2px; + padding: 4px; + min-height: 64px; + border-width: 2px; + border-color: #000000; + border-radius: 6px; + border: 2px ridge #443307; + align-items: center; + justify-content: center; +} diff --git a/styles/tedeum.css b/styles/tedeum.css new file mode 100644 index 0000000..b63cfdc --- /dev/null +++ b/styles/tedeum.css @@ -0,0 +1,1389 @@ + /* ==================== (A) Fonts ==================== */ + @font-face { + font-family: "MailartRubberstamp"; + src: url('../fonts/MailartRubberstamp-Regular.woff') format("woff"); + } + + :root { + /* =================== 1. ACTOR SHEET FONT STYLES =========== */ + --window-header-title-font-size: 1.3rem; + --window-header-title-font-weight: normal; + --window-header-title-color: #f5f5f5; + + --major-button-font-size: 1.05rem; + --major-button-font-weight: normal; + --major-button-color: #dadada; + + --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: rgba(70,67,49,0.76863); + + /* =================== 2. DEBUGGING HIGHLIGHTERS ============ */ + --debug-background-color-red: rgba(255,0,0,0.32941); + --debug-background-color-blue: rgba(29,0,255,0.32941); + --debug-background-color-green: rgba(84,255,0,0.32941); + + --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 { + text-align: justify; + font-size: 16px; + letter-spacing: 1px; +} + +/* Fonts */ +.sheet header.sheet-header h1 input, .window-app .window-header, #actors .directory-list, #navigation #scene-list .scene.nav-item { + font-size: 1.0rem; +} /* For title, sidebar character and scene */ +.sheet nav.sheet-tabs { + font-size: 0.8rem; +} /* For nav and title */ +.window-app input, .fvtt-ecryme .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:hover { + border-width: 4px; + border-color: rgba(37, 124, 37, 0.7); +} + +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)); + grid-gap: 10px; + 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 { + justify-content: center; + align-items: center; + text-align: center; + padding: 5px; +} + +.flex-group-left { + justify-content: flex-start; + text-align: left; +} + +.flex-group-right { + justify-content: flex-end; + text-align: right; +} + +.flex-center { + align-items: center; + justify-content: center; + text-align: center; +} + +.table-create-actor { + font-size: 0.8rem; +} + +.flex-between { + justify-content: space-between; +} + +.flex-shrink { + flex: 'flex-shrink' ; +} + +/* Styles limited to sheets */ +.fvtt-ecryme .sheet-header { + flex: 0 0 210px; + overflow: hidden; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-start; + margin-bottom: 10px; +} + +.fvtt-ecryme .sheet-header .profile-img { + flex: 0 0 128px; + width: 128px; + height: auto; + max-height:128px; + margin-top: 0px; + margin-right: 10px; + -o-object-fit: cover; + object-fit: cover; + -o-object-position: 50% 0; + object-position: 50% 0; + border-width: 0px; +} +.profile-img-container { + margin-right: 0.2rem; + max-width: 140px; + width: 140px; +} + +.button-img { + vertical-align: baseline; + width: 8%; + height: 8%; + max-height: 48px; + border-width: 0px; + 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-ecryme .sheet-header .header-fields { + flex: 1; +} + +.fvtt-ecryme .sheet-header h1.charname { + height: 50px; + padding: 0px; + margin: 5px 0; + border-bottom: 0; +} + +.fvtt-ecryme .sheet-header h1.charname input { + font-family: MailartRubberstamp; + font-size: 3rem; + width: 100%; + height: 100%; + margin: 0; +} + +.fvtt-ecryme .sheet-tabs { + flex: 0; + font-family: MailartRubberstamp; + font-size: 2.2rem; +} + +.fvtt-ecryme .sheet-body, +.fvtt-ecryme .sheet-body .tab, +.fvtt-ecryme .sheet-body .tab .editor { + height: 100%; + font-size: 0.8rem; +} + +.editor { + border: 2; + height: 100%; + padding: 0 3px; +} + +.medium-editor { + border: 2; + height: 240px; + padding: 0 3px; +} + +.small-editor { + border: 2; + height: 120px; + padding: 0 3px; +} + +.fvtt-ecryme .tox .tox-editor-container { + background: #fff; +} + +.fvtt-ecryme .tox .tox-edit-area { + padding: 0 8px; +} + +.fvtt-ecryme .resource-label { + font-weight: bold; + text-transform: uppercase; +} + +.fvtt-ecryme .tabs { + height: 40px; + border-top: 1px solid #AAA; + border-bottom: 1px solid #AAA; + color: #000000; +} + +.fvtt-ecryme .tabs .item { + line-height: 40px; + font-weight: bold; +} + +.fvtt-ecryme .tabs .item.active { + text-decoration: underline; + text-shadow: none; +} + +.fvtt-ecryme .items-list { + list-style: none; + margin: 1px 0; + padding: 0; + overflow-y: auto; +} + +.fvtt-ecryme .items-list .item-header { + font-weight: bold; +} + +.fvtt-ecryme .items-list .item { + height: 30px; + line-height: 24px; + padding: 1px 0; + border-bottom: 1px solid #BBB; +} + +.fvtt-ecryme .items-list .item .item-image { + flex: 0 0 24px; + margin-right: 5px; +} + +.fvtt-ecryme .items-list .item img { + display: block; +} + +.fvtt-ecryme .items-list .item-name { + margin: 0; +} + +.fvtt-ecryme .items-list .item-controls { + flex: 0 0 86px; + text-align: right; +} + +li.folder > .folder-header h3 { + color: rgba(19, 18, 18, 0.95); +} + +/* ======================================== */ +/* Sheet */ +.window-app.sheet .window-content .sheet-header{ + color: rgba(19, 18, 18, 0.95); + background: url("../images/ui/fond_carnet_01.webp"); + /*background: #494e6b;*/ +} + +input[type="text"], select[type="text"] { + background:white; + color: #494e6b; +} + +select { + background:white; + color: #494e6b; +} +/* background: #011d33 url("../images/ui/fond1.webp") repeat left top;*/ +/*color: rgba(168, 139, 139, 0.5);*/ +.window-app.sheet .window-content .sheet-header select[type="text"], .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-body input[type="text"], .window-app.sheet .window-content .sheet-body input[type="number"], .window-app.sheet .window-content .sheet-body select[type="text"] { + color: rgba(19, 18, 18, 0.95); + /*color: #494e6b;*/ +} + +.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(19, 18, 18, 0.95); + background: url("../images/ui/fond_carnet_01.webp"); + border: 1 none; + margin-bottom: 0.25rem; + margin-left: 2px; +} + +.window-app.sheet .window-content .sheet-body input[type="password"], .window-app.sheet .window-content .sheet-body input[type="date"], .window-app.sheet .window-content .sheet-body input[type="time"] { + color: rgba(19, 18, 18, 0.95); + background: url("../images/ui/fond_carnet_01.webp"); + border: 1 none; + margin-bottom: 0.25rem; + margin-left: 2px; +} + +.window-app.sheet .window-content .sheet-body select, .window-app.sheet .window-content .sheet-header select { + color: rgba(19, 18, 18, 0.95); + background: #fff; + border: 1 none; + margin-bottom: 0.25rem; + margin-left: 2px; +} + +.window-app .window-content, .window-app.sheet .window-content .sheet-body{ + font-size: 0.8rem; + /*background: url("../images/ui/pc_sheet_bg.webp") repeat left top;*/ + background: url("../images/ui/fond_carnet_01.webp"); + color: rgba(19, 18, 18, 0.95); +} + +/* 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 { + -o-object-fit: cover; + object-fit: cover; + -o-object-position: 50% 0; + object-position: 50% 0; + margin: 0.5rem 0 0.5rem 0.5rem; + padding: 0; +} + +.sheet nav.sheet-tabs { + font-size: 1.2rem; + 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:#252525; + 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; + display: flex !important; + color: rgba(19, 18, 18, 0.95); +} +.list-item-shadow { + background:rgba(87, 60, 32, 0.35); + flex-grow: 0; + flex-wrap: nowrap; + justify-content: flex-start; +} +.list-item-shadow2 { + background:rgba(87, 60, 32, 0.25); + flex-grow: 0; + flex-wrap: nowrap; + justify-content: flex-start; +} +.item-display-show { + display: block; +} +.item-display-hide { + display: none; +} +.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; + max-width: 24px; + height: 24px; + max-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; +} + +.description-label { + flex-grow: 2; + margin-left: 4px; +} +.status-header-label { + margin-left: 2px; +} +.roll-dialog-label { + margin: 4px 0; + min-width: 96px; +} +.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; +} +.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; +} +.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; +} + +.abilities-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: -moz-fit-content; + width: fit-content; + height: -moz-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; +} +.token-sheet .window-content .flexcol .sheet-tabs { + font-size: 0.8rem; +} + +/* ======================================== */ +.item-checkbox { + height: 25px; + border: 1px solid rgba(115,105,83,0.65098); + 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; +} + +.skill-label { + font-size: 0.7rem; +} +.skill-good-checkbox { + max-height: 10px; + max-width: 10px; +} + +.flex-actions-bar { + flex-grow: 2; +} + +/* ======================================== */ +/* Sidebar CSS */ +#sidebar { + font-size: 1rem; + /*background-position: 100%;*/ + background-color:#f5f5f5; + background-position: 0px 35px; + background-repeat: no-repeat; + background-image: url("../images/ui/fond_carnet_01.webp"); + color: rgba(19, 18, 18, 0.95); +} + +/* 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(19, 18, 18, 0.95); + 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-result-text, +.chat-actor-name { + font-weight: bold; + font-family: MailartRubberstamp; + font-size: 1.2rem; + padding: 4px; +} +.chat-result-success { + color:darkgreen; +} +.chat-result-failure { + color:darkred; +} +.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-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; +} + +.ability-icon { + border: 0; + padding: 2px 2px 2px 2px; + max-width:32px; + max-height:32px; + width: auto; + height: auto; +} +.small-ability-icon { + border: 0; + padding: 2px 2px 2px 2px; + max-width:16px; + max-height:16px; + width: auto; + height: auto; +} +.combat-icon { + border: 0; + padding: 2px 2px 2px 2px; + max-width:24px; + max-height:24px; + width: auto; + height: auto; +} + +#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{ + display: inline-block; + background-position:center; + background-size:cover; + text-shadow: 1px 1px 0 rgba(0,0,0,0.75); + +} + +/*--------------------------------------------------------------------------*/ +/* 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; + -o-border-image: url(img/ui/footer-button.png) 10 repeat; + 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; + -o-border-image: url(img/ui/footer-button.png) 10 repeat; + 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 { + -o-border-image: url(img/ui/bg_control.jpg) 21 repeat; + 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 { + -o-border-image: url(img/ui/footer-button.png) 10 repeat; + 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; + -o-border-image: url(img/ui/footer-button.png) 10 repeat; + 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; + -o-border-image: url(img/ui/footer-button.png) 10 repeat; + 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; + -o-border-image: url(img/ui/footer-button.png) 10 repeat; + 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-nobottom { + border-bottom: unset; /* If you want dots under the hoverable text */ +} + +/* Show the tooltip text when you mouse over the tooltip container */ +.tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; +} + +.chat-card-button { + box-shadow: inset 0px 1px 0px 0px #a6827e; + background: linear-gradient(to bottom, rgba(33,55,74,0.98824) 5%, rgba(21,40,51,0.67059) 100%); + background-color: rgba(125,93,59,0); + border-radius: 3px; + border: 2px ridge #846109; + display: inline-block; + cursor: pointer; + color: #ffffff; + font-size: 0.8rem; + padding: 4px 12px 0px 12px; + 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; +} + +.plus-minus-button { + box-shadow: inset 0px 1px 0px 0px #a6827e; + background: linear-gradient(to bottom, rgba(33,55,74,0.98824) 5%, rgba(21,40,51,0.67059) 100%); + background-color: rgba(125,93,59,0); + border-radius: 2px; + border: 1px ridge #846109; + display: inline-block; + cursor: pointer; + color: #ffffff; + margin: 2px 2px 2px 2px; + padding: 2px 2px 2px 2px; + text-decoration: none; + text-shadow: 0px 1px 0px #4d3534; + position: relative; + margin:0px; +} + +.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; +} + + +/*************************************************************/ +#pause +{ + font-size: 2rem; +} +#pause > h3 +{ + color: #CCC +} +#pause > img { + content: url(../images/ui/ecryme_logo_small_01.webp); + height: 200px; + width: 200px; + top: -200px; + left: calc(50% - 132px); +} + +#logo { + content : url(../images/ui/ecryme_logo_small_01.webp); + width: 100px; + height: 60px; +} + +.dice-cell { + padding-left: 12px; + padding-right: 12px; + width: 60px; + text-align: center; +} + +.dice-formula, +.dice-total { + height: 54px; + position:relative; +} + +.status-small-label { + font-size: 0.65rem; +} +.no-grow { + flex-grow: 1; + max-width: 32px; +} +.status-col-name { + max-width: 72px; +} +.img-no-border { + max-width: 48px; + max-height: 48px; + border: 0px; +} +.items-title-bg { + margin-top: 6px; + color: rgba(19, 18, 18, 0.95); + +} +.impact-box { + border-width: 2px; + border-color: #000000; + border-radius: 6px; + border: 2px ridge #443307; + margin: 4px; + padding: 4px; +} +.impact-title { + font-size: bold; + display: flex; + align-items: center; + justify-content: center; + margin-left: auto; + margin-right: auto; + text-align: center; +} + +.items-title-text { + text-align: center; + font-family: MailartRubberstamp; + margin-left: 4px; +} +.lock-icon { + width:16px; + height: 16px; +} +.item-sheet-img { + width: 64px; + height: auto; + border: 0; +} +.item-name-img { + flex-grow:1; + max-width: 2rem; + min-width: 2rem; +} +.item-name-label-header { + flex-grow:2; + max-width: 12rem; + min-width: 12rem; +} +.item-name-label-header-long { + flex-grow:2; + max-width: 14rem; + min-width: 14rem; +} +.item-name-label-header-long2 { + flex-grow:2; + max-width: 24rem; + min-width: 24rem; +} +.item-name-label { + flex-grow:2; + max-width: 10rem; + min-width: 10rem; +} +.item-name-label-long { + margin-top: 4px; + flex-grow:2; + max-width: 12rem; + min-width: 12rem; +} +.item-name-label-short { + flex-grow:1; + max-width: 4rem; + min-width: 4rem; +} +.item-name-label-medium { + margin-top: 4px; + flex-grow:2; + max-width: 6rem; + min-width: 6rem; +} +.item-name-label-long2 { + margin-top: 4px; + flex-grow:2; + max-width: 22rem; + min-width: 22rem; +} +.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-skill { + flex-grow:1; + max-width: 6.8rem; + min-width: 6.8rem; +} +.item-field-label-long { + flex-grow:1; + max-width: 10rem; + min-width: 10rem; +} +.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:2rem; + max-width: 2rem; +} +.item-controls-fixed-full { + min-width:3rem; + max-width: 3rem; +} +.attribute-label { + font-weight: bold; +} +.flexrow-no-expand { + flex-grow: 0; +} +.item-input-small { + max-width: 16px; + max-height: 12px; +} +.character-summary-rollable { + text-decoration: underline; +} + +.ecryme-roll-dialog .window-header { + border-radius: 10px 10px 0% 0%; +} +.ecryme-roll-dialog .window-content { + border-radius: 0% 0% 10px 10px; +} + +.skill-roll-dialog div { + margin-top: 4px; + margin-bottom: 4px; +} + +.confront-dice { + border-width: 0px; +} + +.bonus-spec { + max-width: 48px; +} +.confront-bonus-container { + position: relative; + flex-grow: 1; + text-align: center; + color: black; +} +.pool-list { + align-items: center; + justify-content: center; +} +.confront-bonus-centered { + position: absolute; + top: 50%; + left: 50%; + font-size: 1.6rem; + color: darkgreen; + font-family: MailartRubberstamp; + transform: translate(-50%, -55%); +} + +.dice-spec { + max-width: 64px; +} +.confront-dice-container { + position: relative; + flex-grow: 1; + text-align: center; + color: black; +} +.confront-dice-centered { + position: absolute; + top: 50%; + left: 50%; + font-size: 2rem; + color: darkgreen; + font-family: MailartRubberstamp; + transform: translate(-50%, -55%); +} +.confront-area { + margin: 2px; + padding: 4px; + min-height: 64px; + border-width: 2px; + border-color: #000000; + border-radius: 6px; + border: 2px ridge #443307; + align-items: center; + justify-content: center; +} diff --git a/system.json b/system.json new file mode 100644 index 0000000..5f0bf10 --- /dev/null +++ b/system.json @@ -0,0 +1,56 @@ +{ + "description": "Te Deum Pour Un Massacre", + "esmodules": [ + "modules/tedeum-main.js" + ], + "gridDistance": 1, + "gridUnits": "m", + "languages": [ + { + "lang": "fr", + "name": "French", + "path": "lang/fr.json", + "flags": {} + } + ], + "authors": [ + { + "name": "Uberwald", + "flags": {} + } + ], + "packs": [ + { + "label": "Equipment", + "type": "Item", + "name": "equipment", + "path": "packs/equipment", + "system": "fvtt-ecryme", + "flags": {}, + "ownership": { + "PLAYER": "OBSERVER", + "ASSISTANT": "OWNER" + } + } + ], + "license": "LICENSE.txt", + "manifest": "https://www.uberwald.me/gitea/public/fvtt-te-deum/raw/branch/master/system.json", + "compatibility": { + "minimum": "10", + "verified": "11" + }, + "id": "fvtt-tedeum", + "primaryTokenAttribute": "secondary.health", + "secondaryTokenAttribute": "secondary.delirium", + "socket": true, + "styles": [ + "styles/tedeum.css" + ], + "relationships": { + }, + "title": "Te Deum pour Un Massacre, le Jeu de Rôles", + "url": "https://www.uberwald.me/gitea/public/fvtt-tedeum", + "version": "11.0.0", + "download": "https://www.uberwald.me/gitea/public/fvtt-tedeum/archive/fvtt-tedeum-v11.0.0.zip", + "background": "" +} \ No newline at end of file diff --git a/template.json b/template.json new file mode 100644 index 0000000..197b048 --- /dev/null +++ b/template.json @@ -0,0 +1,312 @@ +{ + "Actor": { + "types": [ + "pc","annency", "npc" + ], + "templates": { + "biodata": { + "biodata": { + "age": "", + "size": "", + "lieunaissance": "", + "nationalite": "", + "profession": "", + "residence": "", + "milieusocial": "", + "poids": "", + "cheveux": "", + "sexe": "", + "yeux": "", + "enfance": "", + "description": "", + "gmnotes": "" + } + }, + "core": { + "subactors": [], + "equipmentfree": "", + "skills": { + "physical": { + "name": "ECRY.ui.physical", + "skilllist": { + "athletics": { + "name": "ECRY.ui.athletics", + "max": 0, + "value": 0 + }, + "driving": { + "name": "ECRY.ui.driving", + "max": 0, + "value": 0 + }, + "fencing": { + "name": "ECRY.ui.fencing", + "max": 0, + "value": 0 + }, + "brawling": { + "name": "ECRY.ui.brawling", + "max": 0, + "value": 0 + }, + "shooting": { + "name": "ECRY.ui.shooting", + "max": 0, + "value": 0 + } + } + }, + "mental": { + "name": "ECRY.ui.mental", + "skilllist": { + "anthropomecanology": { + "name": "ECRY.ui.anthropomecanology", + "value": 0, + "max": 10 + }, + "ecrymology": { + "name": "ECRY.ui.ecrymology", + "value": 0, + "max": 10 + }, + "traumatology": { + "name": "ECRY.ui.traumatology", + "value": 0, + "max": 10 + }, + "traversology": { + "name": "ECRY.ui.traversology", + "value": 0, + "max": 10 + }, + "urbatechnology": { + "name": "ECRY.ui.urbatechnology", + "value": 0, + "max": 10 + } + } + }, + "social": { + "name": "ECRY.ui.social", + "skilllist": { + "quibbling": { + "name": "ECRY.ui.quibbling", + "value": 0, + "max": 10 + }, + "creativity": { + "name": "ECRY.ui.creativity", + "value": 0, + "max": 10 + }, + "loquacity": { + "name": "ECRY.ui.loquacity", + "value": 0, + "max": 10 + }, + "guile": { + "name": "ECRY.ui.guile", + "value": 0, + "max": 10 + }, + "performance": { + "name": "ECRY.ui.performance", + "value": 0, + "max": 10 + } + } + } + }, + "impacts": { + "physical": { + "superficial": 0, + "light": 0, + "serious": 0, + "major": 0 + }, + "mental": { + "superficial": 0, + "light": 0, + "serious": 0, + "major": 0 + }, + "social": { + "superficial": 0, + "light": 0, + "serious": 0, + "major": 0 + } + }, + "cephaly": { + "name": "ECRY.ui.cephaly", + "skilllist": { + "elegy": { + "name": "ECRY.ui.elegy", + "value": 0, + "max": 10 + }, + "entelechy": { + "name": "ECRY.ui.entelechy", + "value": 0, + "max": 10 + }, + "mekany": { + "name": "ECRY.ui.mekany", + "value": 0, + "max": 10 + }, + "psyche": { + "name": "ECRY.ui.psyche", + "value": 0, + "max": 10 + }, + "scoria": { + "name": "ECRY.ui.scoria", + "value": 0, + "max": 10 + } + } + }, + "internals": { + "confrontbonus": 0 + } + }, + "npccore": { + "npctype": "", + "description": "" + }, + "annency": { + "base": { + "iscollective": false, + "ismultiple": false, + "characters": [], + "location": {"1": "", "2": "", "3":"", "4":"", "5":"" }, + "description": "", + "enhancements": "" + }, + "boheme": { + "name": "", + "ideals": "", + "politic": "", + "description": "" + } + } + }, + "annency": { + "templates": [ + "annency" + ] + }, + "npc": { + "templates": [ + "biodata", + "core" + ] + }, + "pc": { + "templates": [ + "biodata", + "core" + ] + } + }, + "Item": { + "types": [ + "equipment", + "trait", + "weapon", + "specialization", + "maneuver" + ], + "templates": { + "common": { + "description": "" + }, + "equipement": { + "weight": 0, + "cost": 0, + "costunit": "" + } + }, + "maneuver": { + "templates": [ + "common" + ] + }, + "confrontation": { + "templates": [ + "common" + ], + "attackerId": "", + "defenserId": "", + "rolllist": [], + "bonusexecution": 0, + "bonuspreservation": 0 + }, + "equipment": { + "templates": [ + "common", + "equipement" + ], + "quantity": 1, + "weight": 0 + }, + "trait": { + "templates": [ + "common" + ], + "traitype": "normal", + "level": 1 + }, + "weapon": { + "templates": [ + "common", + "equipement" + ], + "weapontype": "melee", + "effect": 0 + }, + "specialization": { + "bonus": 2, + "templates": [ + "common" + ], + "skillkey": "" + }, + "scar": { + "templates": [ + "common" + ], + "skillcategory": [ + "physical", + "mental", + "social", + "cephalie" + ], + "scarLevel": 1 + }, + "annency": { + "templates": [ + "common" + ], + "collective": false, + "multiple": false, + "improvements": "" + }, + "boheme": { + "templates": [ + "common" + ], + "ideals": "", + "political": "" + }, + "contact": { + "templates": [ + "common" + ], + "attitude": "neutral", + "organization": "", + "location": "" + } + } +} \ No newline at end of file diff --git a/templates/actors/actor-sheet.hbs b/templates/actors/actor-sheet.hbs new file mode 100644 index 0000000..1bc0014 --- /dev/null +++ b/templates/actors/actor-sheet.hbs @@ -0,0 +1,385 @@ +
+ + {{!-- Sheet Header --}} +
+
+
+ +
+ +
+ +
+

+ +
+
    +
  • + + +
     
    + {{#if spleen}} +
    + + + +
    + {{/if}} +
  • +
  • + + +
     
    + {{#if ideal}} +
    + + + +
    + {{/if}} +
  • +
+
+ +
+
+
+
+ + + + {{!-- Sheet Tab Navigation --}} + + + {{!-- Sheet Body --}} +
+ + {{!-- Skills Tab --}} +
+ + +
+ + {{#each skills as |category categkey|}} +
+
    +
  • + +

    +
    +
  • + {{#each category.skilllist as |skill skillkey|}} +
  • + + + + + + + {{localize skill.name}} + + +
  • +
  • + +
  • + {{/each}} +
+
+ {{/each}} +
+ +
+ + {{#if hasCephaly}} + {{!-- Cephaly Tab --}} +
+ +
+ +
+ +

{{localize "ECRY.ui.cephaly"}}

+
    + {{#each cephalySkills as |skill skillkey|}} +
  • + + + + {{localize skill.name}} + + +
  • + {{/each}} +
+ +
+ +
+ {{#if annency}} +

{{localize "ECRY.ui.annency"}} : {{annency.name}}

+
    +
  • + + {{annency.system.base.description}} + +
  • +
+ {{/if}} + +
+ +
+ +
+ {{/if}} + +
+ +
    +
  • + +

    +
    + + + +
    + +
    +
  • + {{#each traits as |trait key|}} +
  • + + {{trait.name}} + + +
     
    +
    + +
    +
  • + {{/each}} +
+ +
+ +
+ +
+ {{> systems/fvtt-ecryme/templates/actors/partial-impacts.hbs impacts=system.impacts.physical + impacttype="physical" impactMalus=impactsMalus.physical}} + {{> systems/fvtt-ecryme/templates/actors/partial-impacts.hbs impacts=system.impacts.mental + impacttype="mental" impactMalus=impactsMalus.mental}} + {{> systems/fvtt-ecryme/templates/actors/partial-impacts.hbs impacts=system.impacts.social + impacttype="social" impactMalus=impactsMalus.social}} +
+ +
    +
  • + +

    +
    + + + + + + +
  • + {{#each weapons as |weapon key|}} +
  • + + + + + {{weapon.name}} + + + {{localize (concat "ECRY.ui." weapon.system.weapontype)}} + {{weapon.system.effect}} + +
     
    +
    + +
    +
  • + {{/each}} +
+ +
    +
  • + +

    +
    +
  • + {{#each maneuvers as |maneuver key|}} +
  • + + + {{maneuver.name}} + + +
     
    +
    + +
    +
  • + {{/each}} +
+ +
+ + + {{!-- Equipement Tab --}} +
+ + +

+
+
+ {{editor equipementlibre target="system.equipmentfree" button=true owner=owner editable=editable}} +
+ +
    +
  • + +

    +
    + + + +
     
    +
    + +
    + +
  • + {{#each equipments as |equip key|}} +
  • + + {{equip.name}} + {{equip.system.weight}} + +
     
    +
    + +
    +
  • + {{/each}} +
+ +
+ +
+ + {{!-- Biography Tab --}} +
+ +
+
+
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+
+
+
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+
+
+ +
+ +

+
+
+ {{editor description target="system.biodata.description" button=true owner=owner + editable=editable}} +
+
+ +

+
+
+ {{editor notes target="system.biodata.notes" button=true owner=owner editable=editable}} +
+
+ +
+ + +
+ \ No newline at end of file diff --git a/templates/actors/annency-sheet.hbs b/templates/actors/annency-sheet.hbs new file mode 100644 index 0000000..1b60cb6 --- /dev/null +++ b/templates/actors/annency-sheet.hbs @@ -0,0 +1,138 @@ +
+ + {{!-- Sheet Header --}} +
+
+
+ +
+ +
+ +
+

+ +
+
    +
  • + + +
  • +
+
+ +
+
+
+ + +
+ + {{!-- Sheet Tab Navigation --}} + + + {{!-- Sheet Body --}} +
+ + {{#if hasCephaly}} + {{!-- Cephaly Tab --}} +
+ +
+ +
+ +

{{localize "ECRY.ui.annency"}}

+
    +
  • + + {{localize "ECRY.ui.iscollective"}} + + + + {{localize "ECRY.ui.ismultiple"}} + + +
  • +
+ +

{{localize "ECRY.ui.characters"}}

+
    + {{#each characters as |character id|}} +
  • + + {{character.name}} +
     
    +
    + + +
    +
  • + {{/each}} +
+ +
+ +
+

{{localize "ECRY.ui.location"}}

+
    + {{#each system.base.location as |location index|}} +
  • + + {{localize "ECRY.ui.location"}} {{index}} + + +
  • + {{/each}} +
  • + + {{localize "ECRY.ui.enhancements"}} + + +
  • +
+
+ +
+ +
+ {{/if}} + + {{#if hasBoheme}} +
+ +

{{localize "ECRY.ui.oniricform"}}

+
    +
  • + {{localize "ECRY.ui.name"}} + +
  • +
  • + {{localize "ECRY.ui.ideals"}} + +
  • +
  • + {{localize "ECRY.ui.politic"}} + +
  • +
  • + {{localize "ECRY.ui.description"}} + +
  • +
+ +
+ {{/if}} + + +
+
\ No newline at end of file diff --git a/templates/actors/editor-notes-gm.hbs b/templates/actors/editor-notes-gm.hbs new file mode 100644 index 0000000..4c17392 --- /dev/null +++ b/templates/actors/editor-notes-gm.hbs @@ -0,0 +1,6 @@ +{{#if data.isGM}} +

GM Notes :

+
+ {{editor data.gmnotes target="system.gmnotes" button=true owner=owner editable=editable}} +
+{{/if}} diff --git a/templates/actors/partial-impacts.hbs b/templates/actors/partial-impacts.hbs new file mode 100644 index 0000000..0da3375 --- /dev/null +++ b/templates/actors/partial-impacts.hbs @@ -0,0 +1,17 @@ +
+ +
+ +
+ +
    + {{#each impacts as |value key|}} +
  • + + + {{value}} + +
  • + {{/each}} +
+
\ No newline at end of file diff --git a/templates/chat/chat-cephaly-result.hbs b/templates/chat/chat-cephaly-result.hbs new file mode 100644 index 0000000..3d9d9f6 --- /dev/null +++ b/templates/chat/chat-cephaly-result.hbs @@ -0,0 +1,37 @@ +
+ {{#if actorImg}} + {{alias}} + {{/if}} +

{{alias}}

+
+ +
+ +{{#if img}} +
+ {{alias}} +
+{{/if}} + +
+
    +
  • {{localize "ECRY.ui.cephaly"}} : {{localize skill.name}}
  • + + {{#if annency}} +
  • {{localize "ECRY.ui.annencybonus"}} {{annency.name}}: {{annencyBonus}}
  • + {{/if}} + + {{#if (gt marginExecution 0)}} +
  • {{localize "ECRY.ui.execution"}} {{executionTotal}} vs {{difficulty}} : {{marginExecution}}
  • +
  • {{localize cephalySuccess}}
  • + {{/if}} + + {{#if (gt marginPreservation 0)}} +
  • {{localize "ECRY.ui.preservation"}} {{preservationTotal}} vs {{difficulty}} : {{marginPreservation}}
  • +
  • {{localize cephalyFailure}}
  • + {{/if}} + +
+
+ + \ No newline at end of file diff --git a/templates/chat/chat-confrontation-pending.hbs b/templates/chat/chat-confrontation-pending.hbs new file mode 100644 index 0000000..76530b3 --- /dev/null +++ b/templates/chat/chat-confrontation-pending.hbs @@ -0,0 +1,72 @@ +
+ {{#if actorImg}} + {{alias}} + {{/if}} +

{{alias}}

+
+ +
+ +{{#if img}} +
+ {{alias}} +
+{{/if}} + +
+
    + {{#if (eq mode "cephaly")}} +
  • {{localize "ECRY.ui.cephaly"}} : {{localize skill.name}}
  • + {{else}} +
  • Confrontation : {{alias}}
  • + {{/if}} + +
  • {{localize skill.name}}: {{skill.value}}
  • + {{#if spec}} +
  • {{localize "ECRY.chat.specialization"}} {{spec.name}} (+{{spec.system.bonus}})
  • + {{/if}} + + {{#each traitsBonus as |trait idx|}} + {{#if trait.activated}} +
  • {{localize "ECRY.chat.traitbonus"}}: {{trait.name}} ({{trait.system.level}})
  • + {{/if}} + {{/each}} + {{#each traitsMalus as |trait idx|}} + {{#if trait.activated}} +
  • {{localize "ECRY.chat.traitmalus"}}: {{trait.name}} ({{trait.system.level}})
  • + {{/if}} + {{/each}} + {{#if bonusMalusTraits}} +
  • {{localize "ECRY.chat.bonusmalustraits"}}: {{bonusMalusTraits}}
  • + {{/if}} + + {{#if (isGM)}} + {{else}} +
  • {{localize "ECRY.ui.execution"}} : {{executionTotal}}
  • +
  • {{localize "ECRY.ui.preservation"}} : {{preservationTotal}}
  • + {{/if}} + +
+ + {{#if (isGM)}} + {{#if (eq mode "cephaly")}} +
+ {{localize "ECRY.chat.difficulty"}} + +
+ + {{else}} + + {{/if}} + {{else}} +
+ {{localize "ECRY.chat.sentogm"}} +
+ {{/if}} +
+ + \ No newline at end of file diff --git a/templates/chat/chat-confrontation-result.hbs b/templates/chat/chat-confrontation-result.hbs new file mode 100644 index 0000000..144d6ef --- /dev/null +++ b/templates/chat/chat-confrontation-result.hbs @@ -0,0 +1,54 @@ +
+ {{#if actorImg}} + {{alias}} + {{/if}} +

{{alias}}

+
+ +
+ +{{#if img}} +
+ {{alias}} +
+{{/if}} + +
+
    +
  • Confrontation : {{rollData1.alias}} vs {{rollData2.alias}}
  • +
  • {{localize rollData1.skill.name}} ({{rollData1.skill.value}}) vs {{localize rollData2.skill.name}} ({{rollData2.skill.value}})
  • +
  • {{rollData1.executionTotal}} vs {{rollData2.preservationTotal}} : {{marginExecution}}
  • +
  • {{rollData1.preservationTotal}} vs {{rollData2.executionTotal}} : {{marginPreservation}}
  • + + {{#if rollData1.weapon}} +
  • {{rollData1.alias}} {{rollData1.weapon.name}} ({{rollData1.weapon.system.effect}}) +
  • + {{/if}} + + {{#if rollData2.weapon}} +
  • {{rollData2.alias}} {{rollData2.weapon.name}} ({{rollData2.weapon.system.effect}})
  • + {{/if}} + +
  • {{localize "ECRY.ui.effect"}} {{localize "ECRY.ui.execution"}} : {{effectExecution}}
  • + {{#if impactExecution}} +
  • Impact {{rollData2.alias}} : 1 {{localize (concat "ECRY.ui." impactExecution)}}
  • + + {{/if}} + {{#if bonus2}} +
  • Bonus {{rollData2.alias}} : {{bonus2}}
  • + + {{/if}} + +
  • {{localize "ECRY.ui.effect"}} {{localize "ECRY.ui.preservation"}} : {{effectPreservation}}
  • + {{#if impactPreservation}} +
  • Impact {{rollData1.alias}} : 1 {{localize (concat "ECRY.ui." impactPreservation)}}
  • + + {{/if}} + {{#if bonus1}} +
  • Bonus {{rollData1.alias}} : {{bonus1}}
  • + + {{/if}} +
+
+ + \ No newline at end of file diff --git a/templates/chat/chat-generic-result.hbs b/templates/chat/chat-generic-result.hbs new file mode 100644 index 0000000..41d2a08 --- /dev/null +++ b/templates/chat/chat-generic-result.hbs @@ -0,0 +1,65 @@ +
+ {{#if actorImg}} + {{alias}} + {{/if}} +

{{alias}}

+
+ +
+ + {{#if img}} +
+ {{name}} +
+ {{/if}} + +
+
+ +
+
    + {{#if skill}} +
  • {{localize skill.name}}: {{skill.value}}
  • + {{#if spec}} +
  • {{localize "ECRY.chat.specialization"}} {{spec.name}} (+{{spec.system.bonus}})
  • + {{/if}} + {{/if}} + + {{#if impactMalus}} +
  • {{localize "ECRY.ui.impactmalus"}}: {{impactMalus}}
  • + {{/if}} + + {{#if skillTranscendence}} +
  • {{localize "ECRY.ui.skilltranscendence"}}: {{skillTranscendence}}
  • + {{/if}} + + {{#if traitsBonusList}} + {{#each traitsBonusList as |trait idx|}} +
  • {{localize "ECRY.chat.traitbonus"}}: {{trait.name}} ({{trait.system.level}})
  • + {{/each}} + {{/if}} + {{#if traitsMalusList}} + {{#each traitsMalusList as |trait idx|}} +
  • {{localize "ECRY.chat.traitmalus"}}: {{trait.name}} ({{trait.system.level}})
  • + {{/each}} + {{/if}} + {{#if bonusMalusTraits}} +
  • {{localize "ECRY.chat.bonusmalustraits"}}: {{bonusMalusTraits}}
  • + {{/if}} + +
  • {{localize "ECRY.chat.formula"}}: {{diceFormula}}
  • +
  • {{localize "ECRY.chat.dicesum"}}: {{diceSum}}
  • +
  • {{localize "ECRY.chat.result"}}: {{total}}
  • + {{#if difficulty}} +
  • {{localize "ECRY.chat.difficulty"}}: {{difficulty}} - {{localize "ECRY.chat.margin"}}: {{margin}}
  • + {{#if isSuccess}} +
  • + {{else}} +
  • + {{/if}} + {{/if}} + +
+
+ + diff --git a/templates/dialogs/character-summary.hbs b/templates/dialogs/character-summary.hbs new file mode 100644 index 0000000..8dd88c7 --- /dev/null +++ b/templates/dialogs/character-summary.hbs @@ -0,0 +1,86 @@ +
+ +
    + +
  1. +
    Nom
    + {{#each config.attributs as |attr key|}} +
    {{attr}}
    + {{/each}} +
    Destin
    +
    Fluide
    +
    MPMB
    +
    MPMN
    + +
  2. + + {{#each pcs as |pc key|}} +
  3. + + {{#each pc.system.attributs as |attr key|}} + + {{/each}} + + + + + +
  4. + {{/each}} + +
  5. +
    PNJs
    + {{#each config.attributs as |attr key|}} +
    attr
    + {{/each}} +
    Destin
    +
    Fluide
    +
    MPMB
    +
    MPMN
    + +
  6. + + {{#each npcs as |pc key|}} +
  7. + + {{#each pc.system.attributs as |attr key|}} + + {{/each}} + + + + + +
    + +
    +
  8. + {{/each}} + +
+ +
\ No newline at end of file diff --git a/templates/dialogs/confront-dialog.hbs b/templates/dialogs/confront-dialog.hbs new file mode 100644 index 0000000..9e6a363 --- /dev/null +++ b/templates/dialogs/confront-dialog.hbs @@ -0,0 +1,140 @@ +
+
+ {{#if img}} + + {{/if}} +

{{title}} ({{skill.value}})

+
+ +
+ +
+ +
+

{{localize "ECRY.ui.execution"}} : {{executionTotal}}

+
+ {{> systems/fvtt-ecryme/templates/dialogs/partial-confront-dice-area.hbs filter="execution"}} + {{> systems/fvtt-ecryme/templates/dialogs/partial-confront-bonus-area.hbs filter="execution"}} +
+
+ +
+

{{localize "ECRY.ui.preservation"}} : {{preservationTotal}}

+
+ {{> systems/fvtt-ecryme/templates/dialogs/partial-confront-dice-area.hbs filter="preservation"}} + {{> systems/fvtt-ecryme/templates/dialogs/partial-confront-bonus-area.hbs filter="preservation"}} +
+
+ +
+ +

{{localize "ECRY.ui.dicepool"}}

+
+ {{> systems/fvtt-ecryme/templates/dialogs/partial-confront-dice-area.hbs filter="mainpool"}} +
+ +

{{localize "ECRY.ui.bonuspool"}} (Total : {{count confrontBonus}})

+
+ {{> systems/fvtt-ecryme/templates/dialogs/partial-confront-bonus-area.hbs filter="mainpool"}} +
+ + {{#if weapon}} +
+ {{localize "ECRY.ui.weapon"}} : + {{weapon.name}} ({{localize "ECRY.ui.effect"}} {{weapon.system.effect}}) +
+ {{/if}} + + {{#if impactMalus}} +
+ {{localize "ECRY.ui.impactmalus"}} : + {{impactMalus}} +
+ {{/if}} + +
+ {{localize "ECRY.ui.skilltranscendence"}} : + +
+
+ {{localize "ECRY.ui.transcendapply"}} : + +
+ + {{#if skill.spec}} +
+ {{localize "ECRY.ui.spec"}} : + +
+ {{/if}} + +
+ {{localize "ECRY.ui.traitbonus"}} : + +
+ +
+ {{localize "ECRY.ui.traitmalus"}} : + +
+ + {{#if annency}} +
+ {{localize "ECRY.ui.annency"}} : {{annency.name}} + {{annency.system.base.description}} +
+
+ {{localize "ECRY.ui.annencybonus"}} + +
+ {{/if}} + +
+ Bonus/Malus : + +
+ + +
+ +
\ No newline at end of file diff --git a/templates/dialogs/confront-start-dialog.hbs b/templates/dialogs/confront-start-dialog.hbs new file mode 100644 index 0000000..93d861f --- /dev/null +++ b/templates/dialogs/confront-start-dialog.hbs @@ -0,0 +1,13 @@ +
+
+ {{#if img}} + + {{/if}} +

{{title}}

+
+ +
+ +
+ +
\ No newline at end of file diff --git a/templates/dialogs/partial-common-roll-dialog.hbs b/templates/dialogs/partial-common-roll-dialog.hbs new file mode 100644 index 0000000..327bf48 --- /dev/null +++ b/templates/dialogs/partial-common-roll-dialog.hbs @@ -0,0 +1,90 @@ + {{#if skill}} +
+ {{localize skill.name}} : + {{skill.value}} +
+ + {{#if impactMalus}} +
+ {{localize "ECRY.ui.impactmalus"}} : + {{impactMalus}} +
+ {{/if}} + +
+ {{localize "ECRY.ui.skilltranscendence"}} : + +
+ + {{#if forcedSpec}} +
+ {{localize "ECRY.ui.spec"}} : + {{forcedSpec.name}} (+{{forcedSpec.system.bonus}}) +
+ {{else}} +
+ {{localize "ECRY.ui.spec"}} : + +
+ {{/if}} + + {{#if spleen}} +
+ {{localize "ECRY.ui.applyspleen"}} {{spleen.name}} + +
+ {{/if}} + + {{#if ideal}} +
+ {{localize "ECRY.ui.applyideal"}} {{ideal.name}} + +
+ {{/if}} + +
+ {{localize "ECRY.ui.traitbonus"}} : + +
+ +
+ {{localize "ECRY.ui.traitmalus"}} : + +
+ + {{/if}} + + +
+ Bonus/Malus : + +
+ diff --git a/templates/dialogs/partial-confront-bonus-area.hbs b/templates/dialogs/partial-confront-bonus-area.hbs new file mode 100644 index 0000000..62aa300 --- /dev/null +++ b/templates/dialogs/partial-confront-bonus-area.hbs @@ -0,0 +1,10 @@ +{{#each confrontBonus as |bonus idx|}} +{{#if (eq bonus.location ../filter)}} +
+ + + + +
+{{/if}} +{{/each}} diff --git a/templates/dialogs/partial-confront-dice-area.hbs b/templates/dialogs/partial-confront-dice-area.hbs new file mode 100644 index 0000000..8b24afc --- /dev/null +++ b/templates/dialogs/partial-confront-dice-area.hbs @@ -0,0 +1,11 @@ +{{#each availableDices as |dice idx|}} +{{#if (eq dice.location ../filter)}} +
+ + + + +
+{{/if}} +{{/each}} \ No newline at end of file diff --git a/templates/dialogs/roll-dialog-generic.hbs b/templates/dialogs/roll-dialog-generic.hbs new file mode 100644 index 0000000..4359a22 --- /dev/null +++ b/templates/dialogs/roll-dialog-generic.hbs @@ -0,0 +1,27 @@ +
+
+ {{#if img}} + + {{/if}} +

{{title}}

+
+ +
+ + {{> systems/fvtt-ecryme/templates/dialogs/partial-common-roll-dialog.hbs}} + +
+ Difficulté : + +
+ +
+ +
\ No newline at end of file diff --git a/templates/items/item-elementbio-sheet.hbs b/templates/items/item-elementbio-sheet.hbs new file mode 100644 index 0000000..8be4ccc --- /dev/null +++ b/templates/items/item-elementbio-sheet.hbs @@ -0,0 +1,27 @@ +
+
+ +
+

+
+
+ + {{> systems/fvtt-ecryme/templates/items/partial-item-nav.hbs}} + + + {{!-- Sheet Body --}} +
+ + {{> systems/fvtt-ecryme/templates/items/partial-item-description.hbs}} + +
+ +
+
    + +
+
+
+ +
+
diff --git a/templates/items/item-equipment-sheet.hbs b/templates/items/item-equipment-sheet.hbs new file mode 100644 index 0000000..799f8e1 --- /dev/null +++ b/templates/items/item-equipment-sheet.hbs @@ -0,0 +1,25 @@ +
+
+ +
+

+
+
+ + {{> systems/fvtt-ecryme/templates/items/partial-item-nav.hbs}} + + + {{!-- Sheet Body --}} +
+ + {{> systems/fvtt-ecryme/templates/items/partial-item-description.hbs}} + +
+ + {{> systems/fvtt-ecryme/templates/items/partial-item-equipment.hbs}} + +
+ + +
+
diff --git a/templates/items/item-impact-sheet.hbs b/templates/items/item-impact-sheet.hbs new file mode 100644 index 0000000..ee0cf14 --- /dev/null +++ b/templates/items/item-impact-sheet.hbs @@ -0,0 +1,48 @@ +
+
+ +
+

+
+
+ + {{> systems/fvtt-ecryme/templates/items/partial-item-nav.hbs}} + + + {{!-- Sheet Body --}} +
+ + {{> systems/fvtt-ecryme/templates/items/partial-item-description.hbs}} + +
+ +
+
    + +
  • + + +
  • + +
  • + + +
  • +
+
+
+ +
+
diff --git a/templates/items/item-maneuver-sheet.hbs b/templates/items/item-maneuver-sheet.hbs new file mode 100644 index 0000000..8be4ccc --- /dev/null +++ b/templates/items/item-maneuver-sheet.hbs @@ -0,0 +1,27 @@ +
+
+ +
+

+
+
+ + {{> systems/fvtt-ecryme/templates/items/partial-item-nav.hbs}} + + + {{!-- Sheet Body --}} +
+ + {{> systems/fvtt-ecryme/templates/items/partial-item-description.hbs}} + +
+ +
+
    + +
+
+
+ +
+
diff --git a/templates/items/item-specialization-sheet.hbs b/templates/items/item-specialization-sheet.hbs new file mode 100644 index 0000000..777dda0 --- /dev/null +++ b/templates/items/item-specialization-sheet.hbs @@ -0,0 +1,43 @@ +
+
+ +
+

+
+
+ + {{> systems/fvtt-ecryme/templates/items/partial-item-nav.hbs}} + + + {{!-- Sheet Body --}} +
+ + {{> systems/fvtt-ecryme/templates/items/partial-item-description.hbs}} + +
+ +
+
    + +
  • + + +
  • + +
  • + + +
  • + +
+
+
+ +
+
diff --git a/templates/items/item-trait-sheet.hbs b/templates/items/item-trait-sheet.hbs new file mode 100644 index 0000000..83584ba --- /dev/null +++ b/templates/items/item-trait-sheet.hbs @@ -0,0 +1,48 @@ +
+
+ +
+

+
+
+ + {{> systems/fvtt-ecryme/templates/items/partial-item-nav.hbs}} + + + {{!-- Sheet Body --}} +
+ + {{> systems/fvtt-ecryme/templates/items/partial-item-description.hbs}} + +
+ +
+
    + +
  • + + +
  • + +
  • + + +
  • +
+
+
+ +
+
diff --git a/templates/items/item-weapon-sheet.hbs b/templates/items/item-weapon-sheet.hbs new file mode 100644 index 0000000..dba2f80 --- /dev/null +++ b/templates/items/item-weapon-sheet.hbs @@ -0,0 +1,45 @@ +
+
+ +
+

+
+
+ + {{> systems/fvtt-ecryme/templates/items/partial-item-nav.hbs}} + + + {{!-- Sheet Body --}} +
+ + {{> systems/fvtt-ecryme/templates/items/partial-item-description.hbs}} + +
+ +
+
    + +
  • + + +
  • + +
  • + + +
  • + + {{> systems/fvtt-ecryme/templates/items/partial-item-equipment.hbs}} + +
+
+
+ +
+
diff --git a/templates/items/partial-item-description.hbs b/templates/items/partial-item-description.hbs new file mode 100644 index 0000000..fafd2b5 --- /dev/null +++ b/templates/items/partial-item-description.hbs @@ -0,0 +1,3 @@ +
+ {{editor description target="system.description" button=true owner=owner editable=editable}} +
diff --git a/templates/items/partial-item-equipment.hbs b/templates/items/partial-item-equipment.hbs new file mode 100644 index 0000000..3304e4f --- /dev/null +++ b/templates/items/partial-item-equipment.hbs @@ -0,0 +1,18 @@ +
  • + + +
  • + +
  • + + + +
  • + diff --git a/templates/items/partial-item-nav.hbs b/templates/items/partial-item-nav.hbs new file mode 100644 index 0000000..95b52cd --- /dev/null +++ b/templates/items/partial-item-nav.hbs @@ -0,0 +1,5 @@ +{{!-- Sheet Tab Navigation --}} +