diff --git a/assets/ui/lethal_fatansy_main_picture.webp b/assets/ui/lethal_fatansy_main_picture.webp new file mode 100644 index 0000000..efa0e1c Binary files /dev/null and b/assets/ui/lethal_fatansy_main_picture.webp differ diff --git a/css/fvtt-lethal-fantasy.css b/css/fvtt-lethal-fantasy.css index e7d0f25..4f4f486 100644 --- a/css/fvtt-lethal-fantasy.css +++ b/css/fvtt-lethal-fantasy.css @@ -606,7 +606,7 @@ i.lethalfantasy { .lethalfantasy .tab.character-miracles prose-mirror.active { min-height: 150px; } -.lethalfantasy .opponent-content { +.lethalfantasy .monster-content { font-family: var(--font-primary); font-size: calc(var(--font-size-standard) * 1); color: var(--color-dark-1); @@ -615,20 +615,20 @@ i.lethalfantasy { background-size: 100% 100%; overflow: scroll; } -.lethalfantasy .opponent-content input:disabled, -.lethalfantasy .opponent-content select:disabled { +.lethalfantasy .monster-content input:disabled, +.lethalfantasy .monster-content select:disabled { background-color: rgba(0, 0, 0, 0.2); border-color: transparent; color: var(--color-dark-3); } -.lethalfantasy .opponent-content input, -.lethalfantasy .opponent-content select { +.lethalfantasy .monster-content input, +.lethalfantasy .monster-content select { height: 1.5rem; background-color: rgba(0, 0, 0, 0.1); border-color: var(--color-dark-6); color: var(--color-dark-2); } -.lethalfantasy .opponent-content input[name="name"] { +.lethalfantasy .monster-content input[name="name"] { height: 2.5rem; margin-right: 4px; font-family: var(--font-secondary); @@ -636,91 +636,457 @@ i.lethalfantasy { font-weight: bold; border: none; } -.lethalfantasy .opponent-content fieldset { +.lethalfantasy .monster-content fieldset { margin-bottom: 4px; border-radius: 4px; } -.lethalfantasy .opponent-content .form-fields input, -.lethalfantasy .opponent-content .form-fields select { +.lethalfantasy .monster-content .form-fields input, +.lethalfantasy .monster-content .form-fields select { text-align: center; font-size: calc(var(--font-size-standard) * 1); } -.lethalfantasy .opponent-content .form-fields select { +.lethalfantasy .monster-content .form-fields select { font-family: var(--font-secondary); font-size: calc(var(--font-size-standard) * 1); } -.lethalfantasy .opponent-content legend { +.lethalfantasy .monster-content legend { font-family: var(--font-secondary); font-size: calc(var(--font-size-standard) * 1.2); font-weight: bold; letter-spacing: 1px; } -.lethalfantasy .opponent-content label { +.lethalfantasy .monster-content label { font-family: var(--font-secondary); + font-size: calc(var(--font-size-standard) * 1.2); +} +.lethalfantasy .monster-main { + display: flex; +} +.lethalfantasy .monster-main .monster-pc { + display: flex; + gap: 10px; + flex: 1; +} +.lethalfantasy .monster-main .monster-pc .monster-left { + min-width: 220px; + max-width: 220px; + display: flex; + flex-direction: column; +} +.lethalfantasy .monster-main .monster-pc .monster-left .monster-left-image { + display: flex; + justify-content: center; + align-items: center; + padding-bottom: 8px; +} +.lethalfantasy .monster-main .monster-pc .monster-left .monster-left-image .monster-img { + height: 140px; + width: 140px; + width: auto; + border: none; +} +.lethalfantasy .monster-main .monster-pc .monster-left .monster-hp { + display: flex; + gap: 2px; + align-items: center; +} +.lethalfantasy .monster-main .monster-pc .monster-left .monster-hp .monster-hp-value .form-fields input { + flex: none; + min-width: 3rem; + max-width: 3rem; + margin-left: 10px; font-size: calc(var(--font-size-standard) * 1.4); } -.lethalfantasy .opponent-header { - display: flex; - align-items: center; - justify-content: center; -} -.lethalfantasy .opponent-header .opponent-img { - width: 100px; - height: auto; - margin: 10px; -} -.lethalfantasy .opponent-header .character-name { - display: flex; - width: 100%; -} -.lethalfantasy .opponent-main { +.lethalfantasy .monster-main .monster-pc .monster-left .monster-hp .monster-hp-max { + clear: both; display: flex; flex-direction: row; - gap: 10px; + flex-wrap: wrap; + margin: 3px 0; + align-items: center; } -.lethalfantasy .opponent-main .opponent-gauche { +.lethalfantasy .monster-main .monster-pc .monster-left .monster-hp .monster-hp-max input { + width: 50px; + text-align: center; + font-size: calc(var(--font-size-standard) * 1.4); +} +.lethalfantasy .monster-main .monster-pc .monster-right { display: flex; flex-direction: column; - min-width: 250px; + gap: 4px; } -.lethalfantasy .opponent-main .opponent-gauche .opponent-caracteristiques { +.lethalfantasy .monster-main .monster-pc .monster-right .monster-name { + display: flex; +} +.lethalfantasy .monster-main .monster-pc .monster-right .monster-name input { + width: 400px; +} +.lethalfantasy .monster-main .monster-pc-play { + min-width: 400px; +} +.lethalfantasy .monster-main .monster-pc-edit { + min-width: 400px; +} +.lethalfantasy .monster-main .monster-characteristics { display: flex; flex-direction: column; + gap: 4px; + flex: 1; } -.lethalfantasy .opponent-main .opponent-gauche .opponent-caracteristiques .form-fields { +.lethalfantasy .monster-main .monster-characteristics .monster-characteristic { + display: flex; + align-items: center; +} +.lethalfantasy .monster-main .monster-characteristics .monster-characteristic .rollable:hover, +.lethalfantasy .monster-main .monster-characteristics .monster-characteristic .rollable:focus { + text-shadow: 0 0 8px var(--color-shadow-primary); + cursor: pointer; +} +.lethalfantasy .monster-main .monster-characteristics .monster-characteristic .form-group { + flex: 1; + padding-left: 4px; +} +.lethalfantasy .monster-main .monster-characteristics .monster-characteristic .form-group .form-fields { flex: none; + width: 3rem; } -.lethalfantasy .opponent-main .opponent-gauche .opponent-caracteristiques .form-fields input { +.lethalfantasy .monster-main .monster-resists { + display: flex; + flex-direction: column; + gap: 4px; + flex: 1; +} +.lethalfantasy .monster-main .monster-resists .monster-resist { + display: flex; + align-items: center; + margin-right: 0.5rem; +} +.lethalfantasy .monster-main .monster-resists .monster-resist .rollable:hover, +.lethalfantasy .monster-main .monster-resists .monster-resist .rollable:focus { + text-shadow: 0 0 8px var(--color-shadow-primary); + cursor: pointer; +} +.lethalfantasy .monster-main .monster-resists .monster-resist .name { + flex: 1; + min-width: 3rem; + margin-left: 0.5rem; +} +.lethalfantasy .monster-main .monster-resists .monster-resist .form-group { + flex: 1; + padding-left: 4px; +} +.lethalfantasy .monster-main .monster-resists .monster-resist .form-group .form-fields { + flex: none; width: 50px; } -.lethalfantasy .opponent-main .opponent-gauche .opponent-attacks legend a { - font-size: calc(var(--font-size-standard) * 1.6); -} -.lethalfantasy .opponent-main .opponent-gauche .opponent-attacks .opponent-attack { +.lethalfantasy .monster-main .monster-movements { display: flex; - justify-content: space-between; -} -.lethalfantasy .opponent-main .opponent-gauche .opponent-spells legend a { - font-size: calc(var(--font-size-standard) * 1.6); -} -.lethalfantasy .opponent-main .opponent-gauche .opponent-spells .opponent-spell { - display: flex; - justify-content: space-between; -} -.lethalfantasy .opponent-main .opponent-droite .opponent-description { + flex-direction: column; + gap: 4px; flex: 1; - /* Prend également l'espace disponible */ - min-width: 200px; - /* Pour éviter que le contenu ne déborde */ } -.lethalfantasy .opponent-main .opponent-droite .opponent-description prose-mirror.inactive { +.lethalfantasy .monster-main .monster-movements .monster-movement { + display: flex; + align-items: center; + margin-right: 0.5rem; +} +.lethalfantasy .monster-main .monster-movements .monster-movement .rollable:hover, +.lethalfantasy .monster-main .monster-movements .monster-movement .rollable:focus { + text-shadow: 0 0 8px var(--color-shadow-primary); + cursor: pointer; +} +.lethalfantasy .monster-main .monster-movements .monster-movement .name { + flex: 1; + min-width: 3rem; + margin-left: 0.5rem; +} +.lethalfantasy .monster-main .monster-movements .monster-movement .form-group { + flex: 1; + padding-left: 4px; +} +.lethalfantasy .monster-main .monster-movements .monster-movement .form-group .form-fields { + flex: none; + width: 50px; +} +.lethalfantasy .monster-main .monster-saves { + display: flex; + flex-direction: column; + gap: 4px; + flex: 1; +} +.lethalfantasy .monster-main .monster-saves .monster-save { + display: flex; + align-items: center; + margin-right: 0.5rem; +} +.lethalfantasy .monster-main .monster-saves .monster-save .rollable:hover, +.lethalfantasy .monster-main .monster-saves .monster-save .rollable:focus { + text-shadow: 0 0 8px var(--color-shadow-primary); + cursor: pointer; +} +.lethalfantasy .monster-main .monster-saves .monster-save .name { + flex: 0; + min-width: 5rem; + max-width: 5rem; + margin-left: 0.5rem; +} +.lethalfantasy .monster-main .monster-saves .monster-save .name-pain { + flex: 0; + min-width: 3rem; + max-width: 3rem; + margin-left: 0.5rem; +} +.lethalfantasy .monster-main .monster-saves .monster-save .form-group { + flex: 0; + padding-left: 4px; +} +.lethalfantasy .monster-main .monster-saves .monster-save .form-group .form-fields { + flex: none; + width: 50px; +} +.lethalfantasy .monster-main .monster-characteristics-play { + min-width: 225px; +} +.lethalfantasy .monster-main .monster-characteristic-edit { + min-width: 400px; +} +.lethalfantasy .tab.monster-biography .biodata { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 4px; +} +.lethalfantasy .tab.monster-biography .biodata .biodata-elem { + display: flex; + align-items: center; + gap: 4px; +} +.lethalfantasy .tab.monster-biography .biodata .biodata-elem .name { + min-width: 8rem; +} +.lethalfantasy .monster-biography prose-mirror.inactive { min-height: 40px; } -.lethalfantasy .opponent-main .opponent-droite .opponent-description prose-mirror.active { - min-height: 450px; - height: 100%; - min-width: 200px; - width: 100%; +.lethalfantasy .monster-biography prose-mirror.active { + min-height: 150px; +} +.lethalfantasy .tab.monster-skills { + display: grid; + grid-template-columns: 1fr; +} +.lethalfantasy .tab.monster-skills legend a { + font-size: calc(var(--font-size-standard) * 1); + padding-left: 4px; +} +.lethalfantasy .tab.monster-skills .skills { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 4px; +} +.lethalfantasy .tab.monster-skills .skills .skill { + display: flex; + align-items: center; + gap: 4px; +} +.lethalfantasy .tab.monster-skills .skills .skill .item-img { + width: 24px; + height: 24px; +} +.lethalfantasy .tab.monster-skills .skills .skill .name { + min-width: 12rem; +} +.lethalfantasy .tab.monster-skills .gifts { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 4px; +} +.lethalfantasy .tab.monster-skills .gifts .gift { + display: flex; + align-items: center; + gap: 4px; +} +.lethalfantasy .tab.monster-skills .gifts .gift .item-img { + width: 24px; + height: 24px; +} +.lethalfantasy .tab.monster-skills .gifts .gift .name { + min-width: 12rem; +} +.lethalfantasy .tab.monster-skills .vulnerabilities { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 4px; +} +.lethalfantasy .tab.monster-skills .vulnerabilities .vulnerability { + display: flex; + align-items: center; + gap: 4px; +} +.lethalfantasy .tab.monster-skills .vulnerabilities .vulnerability .item-img { + width: 24px; + height: 24px; +} +.lethalfantasy .tab.monster-skills .vulnerabilities .vulnerability .name { + min-width: 12rem; +} +.lethalfantasy .tab.monster-equipment { + display: grid; + grid-template-columns: 1fr; +} +.lethalfantasy .tab.monster-equipment legend a { + font-size: calc(var(--font-size-standard) * 1.4); + padding-left: 4px; +} +.lethalfantasy .tab.monster-equipment .moneys { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 4px; +} +.lethalfantasy .tab.monster-equipment .equipments { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 4px; +} +.lethalfantasy .tab.monster-equipment .equipments .equipment { + display: flex; + align-items: center; + gap: 4px; +} +.lethalfantasy .tab.monster-equipment .equipments .equipment .item-img { + width: 24px; + height: 24px; + margin: 4px 0 0 0; +} +.lethalfantasy .tab.monster-equipment .equipments .equipment .name { + min-width: 12rem; +} +.lethalfantasy .tab.monster-equipment .equipments .name { + min-width: 12rem; +} +.lethalfantasy .tab.monster-combat { + display: grid; + grid-template-columns: 1fr; +} +.lethalfantasy .tab.monster-combat legend a { + font-size: calc(var(--font-size-standard) * 1.4); + padding-left: 4px; +} +.lethalfantasy .tab.monster-combat .combat-details { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 4px; +} +.lethalfantasy .tab.monster-combat .combat-details .combat-detail { + display: flex; + align-items: center; + gap: 4px; +} +.lethalfantasy .tab.monster-combat .combat-details .combat-detail button { + min-width: 10rem; +} +.lethalfantasy .tab.monster-combat .combat-details .combat-detail .armor-hp { + min-width: 20rem; + max-width: 20rem; +} +.lethalfantasy .tab.monster-combat .combat-details .combat-detail .armor-hp .input { + min-width: 3rem; + max-width: 3rem; +} +.lethalfantasy .tab.monster-combat .attacks { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 4px; +} +.lethalfantasy .tab.monster-combat .attacks .attack { + display: flex; + align-items: center; + gap: 4px; +} +.lethalfantasy .tab.monster-combat .attacks .attack .numeric { + width: 3rem; +} +.lethalfantasy .tab.monster-combat .attacks .attack .attack-icons a { + margin-left: 8px; + margin-right: 8px; +} +.lethalfantasy .tab.monster-combat .armors { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 4px; +} +.lethalfantasy .tab.monster-combat .armors .armor { + display: flex; + align-items: center; + gap: 4px; +} +.lethalfantasy .tab.monster-combat .armors .armor .item-img { + width: 24px; + height: 24px; + margin: 4px 0 0 0; +} +.lethalfantasy .tab.monster-combat .armors .name { + min-width: 12rem; +} +.lethalfantasy .tab.monster-spells { + display: grid; + grid-template-columns: 1fr; +} +.lethalfantasy .tab.monster-spells legend a { + font-size: calc(var(--font-size-standard) * 1.4); + padding-left: 4px; +} +.lethalfantasy .tab.monster-spells .spells { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 4px; +} +.lethalfantasy .tab.monster-spells .spells .spell { + display: flex; + align-items: center; + gap: 4px; +} +.lethalfantasy .tab.monster-spells .spells .spell .item-img { + width: 24px; + height: 24px; +} +.lethalfantasy .tab.monster-spells .spells .spell .name { + min-width: 12rem; +} +.lethalfantasy .tab.monster-spells prose-mirror.inactive { + min-height: 40px; +} +.lethalfantasy .tab.monster-spells prose-mirror.active { + min-height: 150px; +} +.lethalfantasy .tab.monster-miracles { + display: grid; + grid-template-columns: 1fr; +} +.lethalfantasy .tab.monster-miracles legend a { + font-size: calc(var(--font-size-standard) * 1.4); + padding-left: 4px; +} +.lethalfantasy .tab.monster-miracles .miracles { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 4px; +} +.lethalfantasy .tab.monster-miracles .miracles .miracle { + display: flex; + align-items: center; + gap: 4px; +} +.lethalfantasy .tab.monster-miracles .miracles .miracle .item-img { + width: 24px; + height: 24px; +} +.lethalfantasy .tab.monster-miracles .miracles .miracle .name { + min-width: 12rem; +} +.lethalfantasy .tab.monster-miracles prose-mirror.inactive { + min-height: 40px; +} +.lethalfantasy .tab.monster-miracles prose-mirror.active { + min-height: 150px; } .lethalfantasy .skill-content { font-family: var(--font-primary); diff --git a/lang/en.json b/lang/en.json index 327203d..2af098d 100644 --- a/lang/en.json +++ b/lang/en.json @@ -154,6 +154,95 @@ } } }, + "Monster": { + "FIELDS": { + "resists": { + "resistTorture": { + "label": "Resist torture" + }, + "resistPerformance": { + "label": "Resist performance" + }, + "resistIntimidation": { + "label": "Resist intimidation" + } + }, + "app": { + "label": "Appearance" + }, + "challenges": { + "agility": { + "label": "Agility" + }, + "dying": { + "label": "Dying" + }, + "str": { + "label": "Strength" + } + }, + "developmentPoints": { + "label": "Development points", + "total": { + "label": "Total" + }, + "remaining": { + "label": "Remaining" + } + }, + "char": { + "label": "Charisma" + }, + "combat": { + "armorHitPoints": { + "label": "Armor hit points" + } + }, + "con": { + "label": "Constitution" + }, + "dex": { + "label": "Dexterity" + }, + "int": { + "label": "Intelligence" + }, + "perception": { + "bonus": { + "label": "Bonus" + }, + "value": { + "label": "Perception" + } + }, + "saves": { + "contagion": { + "label": "Contagion" + }, + "dodge": { + "label": "Dodge" + }, + "pain": { + "label": "Pain" + }, + "poison": { + "label": "Poison" + }, + "toughness": { + "label": "Toughness" + }, + "will": { + "label": "Will" + } + }, + "str": { + "label": "Strength" + }, + "wis": { + "label": "Wisdom" + } + } + }, "Delete": "Delete", "Edit": "Edit", "Equipment": { @@ -183,6 +272,13 @@ } }, "Label": { + "attacks": "Attacks", + "monster": "Monster", + "Resist" :"Resist", + "resist": "Resist", + "resistTorture": "Resist torture", + "resistPerformance": "Resist performance", + "resistIntimidation": "Resist intimidation", "initiative": "Initiative", "maxInitiativeWisdom": "Max initiative (from wisdom)", "combat": "Combat", @@ -279,6 +375,9 @@ "weapon-attack": "Weapon attack", "weapon-damage": "Weapon damage", "weapon-defense": "Weapon defense", + "monster-attack": "Monster attack", + "monster-damage": "Monster damage", + "monster-defense": "Monster defense", "weapons": "Weapons", "wis": "WIS" }, @@ -656,7 +755,7 @@ "TYPES": { "Actor": { "character": "Character", - "opponent": "NPC" + "monster": "Monster" }, "Item": { "armor": "Armor", diff --git a/lethal-fantasy.mjs b/lethal-fantasy.mjs index d7412f2..63887fa 100644 --- a/lethal-fantasy.mjs +++ b/lethal-fantasy.mjs @@ -40,7 +40,7 @@ Hooks.once("init", function () { CONFIG.Actor.documentClass = documents.LethalFantasyActor CONFIG.Actor.dataModels = { character: models.LethalFantasyCharacter, - opponent: models.LethalFantasyOpponent, + monster: models.LethalFantasyMonster, } CONFIG.Item.documentClass = documents.LethalFantasyItem @@ -59,7 +59,7 @@ Hooks.once("init", function () { // Register sheet application classes Actors.unregisterSheet("core", ActorSheet) Actors.registerSheet("lethalFantasy", applications.LethalFantasyCharacterSheet, { types: ["character"], makeDefault: true }) - Actors.registerSheet("lethalFantasy", applications.LethalFantasyOpponentSheet, { types: ["opponent"], makeDefault: true }) + Actors.registerSheet("lethalFantasy", applications.LethalFantasyMonsterSheet, { types: ["monster"], makeDefault: true }) Items.unregisterSheet("core", ItemSheet) Items.registerSheet("lethalFantasy", applications.LethalFantasySkillSheet, { types: ["skill"], makeDefault: true }) diff --git a/module/applications/_module.mjs b/module/applications/_module.mjs index bbd149e..cd73c64 100644 --- a/module/applications/_module.mjs +++ b/module/applications/_module.mjs @@ -1,5 +1,5 @@ export { default as LethalFantasyCharacterSheet } from "./sheets/character-sheet.mjs"; -export { default as LethalFantasyOpponentSheet } from "./sheets/opponent-sheet.mjs" +export { default as LethalFantasyMonsterSheet } from "./sheets/monster-sheet.mjs" export { default as LethalFantasyWeaponSheet } from "./sheets/weapon-sheet.mjs" export { default as LethalFantasySkillSheet } from "./sheets/skill-sheet.mjs" export { default as LethalFantasyGiftSheet } from "./sheets/gift-sheet.mjs" diff --git a/module/applications/sheets/monster-sheet.mjs b/module/applications/sheets/monster-sheet.mjs new file mode 100644 index 0000000..7e4ab48 --- /dev/null +++ b/module/applications/sheets/monster-sheet.mjs @@ -0,0 +1,266 @@ +import LethalFantasyActorSheet from "./base-actor-sheet.mjs" +import LethalFantasyRoll from "../../documents/roll.mjs" + +export default class LethalFantasyMonsterSheet extends LethalFantasyActorSheet { + /** @override */ + static DEFAULT_OPTIONS = { + classes: ["monster"], + position: { + width: 980, + height: 780, + }, + window: { + contentClasses: ["monster-content"], + }, + actions: { + rangedAttackDefense: LethalFantasyMonsterSheet.#onRangedAttackDefense, + rollInitiative: LethalFantasyMonsterSheet.#onRollInitiative, + }, + } + + /** @override */ + static PARTS = { + main: { + template: "systems/fvtt-lethal-fantasy/templates/monster-main.hbs", + }, + tabs: { + template: "templates/generic/tab-navigation.hbs", + }, + combat: { + template: "systems/fvtt-lethal-fantasy/templates/monster-combat.hbs", + }, + biography: { + template: "systems/fvtt-lethal-fantasy/templates/monster-biography.hbs", + }, + } + + /** @override */ + tabGroups = { + sheet: "combat", + } + + /** + * Prepare an array of form header tabs. + * @returns {Record>} + */ + #getTabs() { + let tabs = { + combat: { id: "combat", group: "sheet", icon: "fa-solid fa-swords", label: "LETHALFANTASY.Label.combat" }, + biography: { id: "biography", group: "sheet", icon: "fa-solid fa-book", label: "LETHALFANTASY.Label.biography" }, + } + for (const v of Object.values(tabs)) { + v.active = this.tabGroups[v.group] === v.id + v.cssClass = v.active ? "active" : "" + } + return tabs + } + + /** @override */ + async _prepareContext() { + const context = await super._prepareContext() + context.tabs = this.#getTabs() + + return context + } + + _generateTooltip(type, target) { + } + + /** @override */ + async _preparePartContext(partId, context) { + const doc = this.document + switch (partId) { + case "main": + break + case "combat": + context.tab = context.tabs.combat + context.weapons = doc.itemTypes.weapon + break + case "biography": + context.tab = context.tabs.biography + context.enrichedDescription = await TextEditor.enrichHTML(doc.system.description, { async: true }) + context.enrichedNotes = await TextEditor.enrichHTML(doc.system.notes, { async: true }) + break + } + return context + } + + // #region Drag-and-Drop Workflow + + /** + * Callback actions which occur when a dragged element is dropped on a target. + * @param {DragEvent} event The originating DragEvent + * @protected + */ + async _onDrop(event) { + if (!this.isEditable || !this.isEditMode) return + const data = TextEditor.getDragEventData(event) + + // Handle different data types + switch (data.type) { + case "Item": + const item = await fromUuid(data.uuid) + return this._onDropItem(item) + } + } + + static async #onRangedAttackDefense(event, target) { + const hasTarget = false + + let roll = await LethalFantasyRoll.promptRangedDefense({ + actorId: this.actor.id, + actorName: this.actor.name, + actorImage: this.actor.img, + }) + if (!roll) return null + + await roll.toMessage({}, { rollMode: roll.options.rollMode }) + } + + static async #onRollInitiative(event, target) { + const hasTarget = false + let actorClass = this.actor.system.biodata.class; + + let wisDef = SYSTEM.CHARACTERISTICS_TABLES.wis.find((c) => c.value === this.actor.system.characteristics.wis.value) + let maxInit = Number(wisDef.init_cap) || 1000 + + let roll = await LethalFantasyRoll.promptInitiative({ + actorId: this.actor.id, + actorName: this.actor.name, + actorImage: this.actor.img, + actorClass, + maxInit, + }) + if (!roll) return null + + await roll.toMessage({}, { rollMode: roll.options.rollMode }) + } + + static #onArmorHitPointsPlus(event, target) { + let armorHP = this.actor.system.combat.armorHitPoints + armorHP += 1 + this.actor.update({ "system.combat.armorHitPoints": armorHP }) + } + + static #onArmorHitPointsMinus(event, target) { + let armorHP = this.actor.system.combat.armorHitPoints + armorHP -= 1 + this.actor.update({ "system.combat.armorHitPoints": Math.max(armorHP, 0) }) + } + + static #onCreateEquipment(event, target) { + } + + getBestWeaponClassSkill(skills, rollType, multiplier = 1.0) { + let maxValue = 0 + let goodSkill = skills[0] + for (let s of skills) { + if (rollType === "weapon-attack") { + if (s.system.weaponBonus.attack > maxValue) { + maxValue = Number(s.system.weaponBonus.attack) + goodSkill = s + } + } + if (rollType === "weapon-defense") { + if (s.system.weaponBonus.defense > maxValue) { + maxValue = Number(s.system.weaponBonus.defense) + goodSkill = s + } + } + if (rollType.includes("weapon-damage")) { + if (s.system.weaponBonus.damage > maxValue) { + maxValue = Number(s.system.weaponBonus.damage) + goodSkill = s + } + } + } + goodSkill.weaponSkillModifier = maxValue * multiplier + return goodSkill + } + + /** + * Handles the roll action triggered by user interaction. + * + * @param {PointerEvent} event The event object representing the user interaction. + * @param {HTMLElement} target The target element that triggered the roll. + * + * @returns {Promise} A promise that resolves when the roll action is complete. + * + * @throws {Error} Throws an error if the roll type is not recognized. + * + * @description This method checks the current mode (edit or not) and determines the type of roll + * (save, resource, or damage) based on the target element's data attributes. It retrieves the + * corresponding value from the document's system and performs the roll. + */ + + async _onRoll(event, target) { + if (this.isEditMode) return + const rollType = event.target.dataset.rollType + let rollTarget + let rollKey = event.target.dataset.rollKey + switch (rollType) { + case "monster-attack": + case "monster-defense": + case "monster-damage": + rollTarget = foundry.utils.duplicate(this.document.system.attacks[rollKey]) + rollTarget.rollKey = rollKey + break + case "resist": + rollTarget = foundry.utils.duplicate(this.document.system.resists[rollKey]) + rollTarget.rollKey = rollKey + break + case "save": + rollTarget = foundry.utils.duplicate(this.document.system.saves[rollKey]) + rollTarget.rollKey = rollKey + rollTarget.rollDice = event.target.dataset?.rollDice + break + case "weapon-damage-small": + case "weapon-damage-medium": + case "weapon-attack": + case "weapon-defense": + let weapon = this.actor.items.find((i) => i.type === "weapon" && i.id === rollKey) + let skill + let skills = this.actor.items.filter((i) => i.type === "skill" && i.name.toLowerCase() === weapon.name.toLowerCase()) + if (skills.length > 0) { + skill = this.getBestWeaponClassSkill(skills, rollType, 1.0) + } else { + skills = this.actor.items.filter((i) => i.type === "skill" && i.name.toLowerCase().replace(" skill", "") === weapon.name.toLowerCase()) + if (skills.length > 0) { + skill = this.getBestWeaponClassSkill(skills, rollType, 1.0) + } else { + skills = this.actor.items.filter((i) => i.type === "skill" && i.system.weaponClass === weapon.system.weaponClass) + if (skills.length > 0) { + skill = this.getBestWeaponClassSkill(skills, rollType, 0.5) + } else { + skills = this.actor.items.filter((i) => i.type === "skill" && i.system.weaponClass.includes(SYSTEM.WEAPON_CATEGORIES[weapon.system.weaponClass])) + if (skills.length > 0) { + skill = this.getBestWeaponClassSkill(skills, rollType, 0.25) + } else { + ui.notifications.warn(game.i18n.localize("LETHALFANTASY.Notifications.skillNotFound")) + return + } + } + } + } + if (!weapon || !skill) { + console.error("Weapon or skill not found", weapon, skill) + ui.notifications.warn(game.i18n.localize("LETHALFANTASY.Notifications.skillNotFound")) + return + } + rollTarget = skill + rollTarget.weapon = weapon + rollTarget.weaponSkillModifier = skill.weaponSkillModifier + rollTarget.rollKey = rollKey + rollTarget.combat = foundry.utils.duplicate(this.actor.system.combat) + break + default: + ui.notifications.error(game.i18n.localize("LETHALFANTASY.Notifications.rollTypeNotFound") + String(rollType)) + break + } + + // In all cases + console.log(rollTarget) + await this.document.system.roll(rollType, rollTarget) + } + // #endregion +} diff --git a/module/applications/sheets/opponent-sheet.mjs b/module/applications/sheets/opponent-sheet.mjs deleted file mode 100644 index af5b4cb..0000000 --- a/module/applications/sheets/opponent-sheet.mjs +++ /dev/null @@ -1,89 +0,0 @@ -import LethalFantasyActorSheet from "./base-actor-sheet.mjs" - -export default class LethalFantasyOpponentSheet extends LethalFantasyActorSheet { - /** @override */ - static DEFAULT_OPTIONS = { - classes: ["opponent"], - position: { - width: 800, - height: 700, - }, - window: { - contentClasses: ["opponent-content"], - }, - actions: { - createAttack: LethalFantasyOpponentSheet.#onCreateAttack, - }, - } - - /** @override */ - static PARTS = { - main: { - template: "systems/fvtt-lethal-fantasy/templates/opponent.hbs", - }, - } - - /** @override */ - async _prepareContext() { - const context = await super._prepareContext() - context.attacks = context.actor.itemTypes.attack - context.spells = context.actor.itemTypes.spell - context.hasSpells = context.spells.length > 0 - return context - } - - /** - * Callback actions which occur when a dragged element is dropped on a target. - * @param {DragEvent} event The originating DragEvent - * @protected - */ - async _onDrop(event) { - if (!this.isEditable || !this.isEditMode) return - const data = TextEditor.getDragEventData(event) - - // Handle different data types - switch (data.type) { - case "Item": - const item = await fromUuid(data.uuid) - if (item.type === "attack") return this.#onDropAttackItem(item) - if (item.type === "spell") return this._onDropItem(item) - } - } - - /** - * Handles the drop event of an attack item. - * - * @param {Object} item The attack item being dropped. - * @returns {Promise} A promise that resolves when the attack item has been added to the document. - * @private - */ - async #onDropAttackItem(item) { - await this.document.addAttack(item) - } - - /** - * Handles the creation of a new attack item. - * - * @param {Event} event The event that triggered the creation of the attack. - * @param {Object} target The target object where the attack will be created. - * @private - * @static - */ - static #onCreateAttack(event, target) { - const item = this.document.createEmbeddedDocuments("Item", [{ name: "Nouvelle attaque", type: "attack" }]) - } - - /** - * Roll a damage roll. - * @param {PointerEvent} event The originating click event - * @param {HTMLElement} target the capturing HTML element which defined a [data-action] - */ - async _onRoll(event, target) { - if (this.isEditMode) return - const elt = event.currentTarget - const rollValue = elt.dataset.rollValue - const rollTarget = elt.dataset.itemName - - await this.document.system.roll(rollValue, rollTarget) - } -} diff --git a/module/config/monster.mjs b/module/config/monster.mjs new file mode 100644 index 0000000..72a4a53 --- /dev/null +++ b/module/config/monster.mjs @@ -0,0 +1,48 @@ +export const MONSTER_CHARACTERISTICS = Object.freeze({ + int: { + id: "int", + label: "LETHALFANTASY.Character.int.label" + }, + dex: { + id: "dex", + label: "LETHALFANTASY.Character.dex.label" + }, +}) + +export const MONSTER_RESIST = Object.freeze({ + resistTorture: { + id: "resistTorture", + label: "LETHALFANTASY.Character.resistTorture.label" + }, + resistPerformance: { + id: "resistPerformance", + label: "LETHALFANTASY.Character.resistPerformance.label" + }, + resistIntimidation: { + id: "resistIntimidation", + label: "LETHALFANTASY.Character.resistIntimidation.label" + } +}) + +export const MONSTER_SAVES = Object.freeze({ + will: { + id: "will", + label: "LETHALFANTASY.Character.will.label" + }, + dodge: { + id: "dodge", + label: "LETHALFANTASY.Character.dodge.label" + }, + toughness: { + id: "toughness", + label: "LETHALFANTASY.Character.toughness.label" + }, + contagion: { + id: "contagion", + label: "LETHALFANTASY.Character.contagion.label" + }, + poison: { + id: "poison", + label: "LETHALFANTASY.Character.poison.label" + } +}) diff --git a/module/config/system.mjs b/module/config/system.mjs index af53326..793012b 100644 --- a/module/config/system.mjs +++ b/module/config/system.mjs @@ -5,6 +5,7 @@ import * as SPELL from "./spell.mjs" import * as SKILL from "./skill.mjs" import * as EQUIPMENT from "./equipment.mjs" import * as CHARACTERISTICS from "./characteristic-tables.mjs" +import * as MONSTER from "./monster.mjs" export const SYSTEM_ID = "fvtt-lethal-fantasy" export const DEV_MODE = false @@ -230,6 +231,9 @@ export const SYSTEM = { CHARACTERISTICS: CHARACTER.CHARACTERISTICS, CHARACTERISTICS_TABLES: CHARACTERISTICS.TABLES, CHARACTERISTICS_MAJOR: CHARACTERISTICS.MAJOR, + MONSTER_CHARACTERISTICS: MONSTER.MONSTER_CHARACTERISTICS, + MONSTER_RESIST: MONSTER.MONSTER_RESIST, + MONSTER_SAVES: MONSTER.MONSTER_SAVES, SAVES: CHARACTER.SAVES, CHALLENGES: CHARACTER.CHALLENGES, SKILL_CATEGORY: SKILL.CATEGORY, diff --git a/module/documents/roll.mjs b/module/documents/roll.mjs index 22a3810..4602647 100644 --- a/module/documents/roll.mjs +++ b/module/documents/roll.mjs @@ -135,8 +135,32 @@ export default class LethalFantasyRoll extends Roll { } else { dice = "1D20" maxValue = 20 + hasFavor = true } - + + } else if (options.rollType === "monster-attack" || options.rollType === "monster-defense") { + hasD30 = true + options.rollName = options.rollTarget.name + dice = "1D20" + baseFormula = "D20" + maxValue = 20 + hasModifier = true + hasChangeDice = false + hasFavor = true + if (options.rollType === "monster-attack") { + options.rollTarget.value = options.rollTarget.attackModifier + options.rollTarget.charModifier = 0 + } else { + options.rollTarget.value = options.rollTarget.defenseModifier + options.rollTarget.charModifier = 0 + } + + } else if (options.rollType === "resist") { + dice = "1D20" + maxValue = 20 + hasFavor = true + options.rollName = options.rollTarget.rollKey + } else if (options.rollType === "skill") { options.rollName = options.rollTarget.name dice = "1D100" @@ -200,8 +224,20 @@ export default class LethalFantasyRoll extends Roll { dice = dice.replace("E", "") baseFormula = dice maxValue = 20 + + } else if (options.rollType.includes("monster-damage")) { + options.rollName = options.rollTarget.name + hasModifier = true + hasChangeDice = false + options.rollTarget.value = 0 + options.rollTarget.charModifier = 0 + dice = options.rollTarget.damageDice + dice = dice.replace("E", "") + baseFormula = dice + maxValue = 20 } + if (options.rollType === "save" && options.rollTarget.rollKey === "pain") { dice = options.rollTarget.rollDice baseFormula = options.rollTarget.rollDice @@ -336,7 +372,7 @@ export default class LethalFantasyRoll extends Roll { let rollBase = new this(baseFormula, options.data, rollData) const rollModifier = new Roll(modifierFormula, options.data, rollData) - rollModifier.evaluate() + await rollModifier.evaluate() await rollBase.evaluate() let rollFavor @@ -372,7 +408,7 @@ export default class LethalFantasyRoll extends Roll { if (hasD30) { let rollD30 = await new Roll("1D30").evaluate() if (game?.dice3d) { - await game.dice3d.showForRoll(rollFavor, game.user, true) + game.dice3d.showForRoll(rollD30, game.user, true) } options.D30result = rollD30.total } diff --git a/module/models/_module.mjs b/module/models/_module.mjs index 3b6c0c9..58be9c7 100644 --- a/module/models/_module.mjs +++ b/module/models/_module.mjs @@ -1,5 +1,5 @@ export { default as LethalFantasyCharacter } from "./character.mjs" -export { default as LethalFantasyOpponent } from "./opponent.mjs" +export { default as LethalFantasyMonster } from "./monster.mjs" export { default as LethalFantasyWeapon } from "./weapon.mjs" export { default as LethalFantasySpell } from "./spell.mjs" export { default as LethalFantasySkill } from "./skill.mjs" diff --git a/module/models/monster.mjs b/module/models/monster.mjs new file mode 100644 index 0000000..fd50b24 --- /dev/null +++ b/module/models/monster.mjs @@ -0,0 +1,144 @@ +import { SYSTEM } from "../config/system.mjs" +import LethalFantasyRoll from "../documents/roll.mjs" + +export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel { + static defineSchema() { + const fields = foundry.data.fields + const requiredInteger = { required: true, nullable: false, integer: true } + const schema = {} + + schema.description = new fields.HTMLField({ required: true, textSearch: true }) + schema.notes = new fields.HTMLField({ required: true, textSearch: true }) + + // Carac + const characteristicField = (label) => { + const schema = { + value: new fields.NumberField({ ...requiredInteger, initial: 3, min: 0 }), + percent: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0, max: 100 }), + attackMod: new fields.NumberField({ ...requiredInteger, initial: 0 }), + defenseMod: new fields.NumberField({ ...requiredInteger, initial: 0 }) + } + return new fields.SchemaField(schema, { label }) + } + + schema.characteristics = new fields.SchemaField( + Object.values(SYSTEM.MONSTER_CHARACTERISTICS).reduce((obj, characteristic) => { + obj[characteristic.id] = characteristicField(characteristic.label) + return obj + }, {}), + ) + + // Save + const saveField = (label) => { + const schema = { + value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) + } + return new fields.SchemaField(schema, { label }) + } + schema.saves = new fields.SchemaField( + Object.values(SYSTEM.MONSTER_SAVES).reduce((obj, save) => { + obj[save.id] = saveField(save.label) + return obj + }, {}), + ) + + // Resist + const resistField = (label) => { + const schema = { + value: new fields.StringField({ initial: "0", required: true, nullable: false }), + } + return new fields.SchemaField(schema, { label }) + } + schema.resists = new fields.SchemaField( + Object.values(SYSTEM.MONSTER_RESIST).reduce((obj, save) => { + obj[save.id] = resistField(save.label) + return obj + }, {}), + ) + + schema.hp = new fields.SchemaField({ + value: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 }), + average: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 }), + max: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 }), + }) + + const attackField = (label) => { + const schema = { + key: new fields.StringField({ required: true, nullable: false, initial: `attack${label}` }), + name: new fields.StringField({ required: true, nullable: false, initial: `Attack ${label}` }), + attackScore: new fields.NumberField({ ...requiredInteger, initial: Number(label), min: 0 }), + attackModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + defenseModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + damageDice: new fields.StringField({ required: true, nullable: false, initial: "1D6" }), + } + return new fields.SchemaField(schema, { label }) + } + // Add 4 attackFields in an attack schema + schema.attacks = new fields.SchemaField({ + attack1: attackField("1"), + attack2: attackField("2"), + attack3: attackField("3"), + attack4: attackField("4"), + }) + + schema.perception = new fields.SchemaField({ + value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + bonus: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) + }) + + schema.movement = new fields.SchemaField({ + walk: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + jog: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + sprint: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + run: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + armorAdjust: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + }) + schema.jump = new fields.SchemaField({ + broad: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + running: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + vertical: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + }) + schema.biodata = new fields.SchemaField({ + alignment: new fields.StringField({ required: true, nullable: false, initial: "" }), + vision: new fields.StringField({ required: true, nullable: false, initial: "" }), + height: new fields.NumberField({ ...requiredInteger, initial: 170, min: 50 }), + length: new fields.StringField({ required: true, nullable: false, initial: "" }), + weight: new fields.NumberField({ ...requiredInteger, initial: 70, min: 0 }) + }) + schema.combat = new fields.SchemaField({ + attackModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + defenseModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + damageModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + armorHitPoints: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + }) + + + return schema + } + + /** @override */ + static LOCALIZATION_PREFIXES = ["LETHALFANTASY.Monster"] + + /** + * Rolls a dice for a character. + * @param {("save"|"resource|damage")} rollType The type of the roll. + * @param {number} rollTarget The target value for the roll. Which caracteristic or resource. If the roll is a damage roll, this is the id of the item. + * @param {"="|"+"|"++"|"-"|"--"} rollAdvantage If there is an avantage (+), a disadvantage (-), a double advantage (++), a double disadvantage (--) or a normal roll (=). + * @returns {Promise} - A promise that resolves to null if the roll is cancelled. + */ + async roll(rollType, rollTarget) { + const hasTarget = false + let roll = await LethalFantasyRoll.prompt({ + rollType, + rollTarget, + actorId: this.parent.id, + actorName: this.parent.name, + actorImage: this.parent.img, + hasTarget, + target: false + }) + if (!roll) return null + + await roll.toMessage({}, { rollMode: roll.options.rollMode }) + } +} diff --git a/module/models/opponent.mjs b/module/models/opponent.mjs deleted file mode 100644 index 705a7fb..0000000 --- a/module/models/opponent.mjs +++ /dev/null @@ -1,48 +0,0 @@ -import LethalFantasyRoll from "../documents/roll.mjs" - -export default class LethalFantasyOpponent extends foundry.abstract.TypeDataModel { - static defineSchema() { - const fields = foundry.data.fields - const requiredInteger = { required: true, nullable: false, integer: true } - const schema = {} - - schema.dv = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) - schema.pv = new fields.SchemaField({ - value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), - max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), - }) - schema.armure = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) - schema.malus = new fields.NumberField({ ...requiredInteger, initial: 0, max: 0 }) - schema.actions = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) - schema.description = new fields.HTMLField({ required: true, textSearch: true }) - // Attaques : embedded items of type Attack - - return schema - } - - /** @override */ - static LOCALIZATION_PREFIXES = ["LETHALFANTSY.Opponent"] - - /** - * Rolls a dice attack for an opponent. - * @param {number} rollType Type of roll. - * @param {number} rollTarget The name of the attack - * @returns {Promise} - A promise that resolves to null if the roll is cancelled. - */ - async roll(rollType, rollTarget) { - let roll = await LethalFantasyRoll.prompt({ - rollType: rollType, - rollValue, - rollTarget, - actorId: this.parent.id, - actorName: this.parent.name, - actorImage: this.parent.img, - }) - if (!roll) return null - await roll.toMessage({}, { rollMode: roll.options.rollMode }) - } - - get toolTip() { - return this.description || "" - } -} diff --git a/module/utils.mjs b/module/utils.mjs index 7631793..704c049 100644 --- a/module/utils.mjs +++ b/module/utils.mjs @@ -18,6 +18,9 @@ export default class LethalFantasyUtils { Handlebars.registerHelper('isNull', function (val) { return val == null; }); + Handlebars.registerHelper('match', function (val, search) { + return val.match(search); + }); Handlebars.registerHelper('exists', function (val) { return val != null && val !== undefined; @@ -156,9 +159,6 @@ export default class LethalFantasyUtils { if (typeof text !== 'string') return text return text.charAt(0).toUpperCase() }) - Handlebars.registerHelper('isCreature', function (key) { - return key === "creature" || key === "daemon"; - }) // Handle v12 removal of this helper Handlebars.registerHelper('select', function (selected, options) { diff --git a/packs-system/lf-equipment/000028.log b/packs-system/lf-equipment/000040.log similarity index 100% rename from packs-system/lf-equipment/000028.log rename to packs-system/lf-equipment/000040.log diff --git a/packs-system/lf-equipment/CURRENT b/packs-system/lf-equipment/CURRENT index 8b15215..59611b0 100644 --- a/packs-system/lf-equipment/CURRENT +++ b/packs-system/lf-equipment/CURRENT @@ -1 +1 @@ -MANIFEST-000026 +MANIFEST-000038 diff --git a/packs-system/lf-equipment/LOG b/packs-system/lf-equipment/LOG index bded37b..e5aa69e 100644 --- a/packs-system/lf-equipment/LOG +++ b/packs-system/lf-equipment/LOG @@ -1,7 +1,7 @@ -2025/01/12-15:30:20.575853 7f0abaffd6c0 Recovering log #24 -2025/01/12-15:30:20.585410 7f0abaffd6c0 Delete type=3 #22 -2025/01/12-15:30:20.585495 7f0abaffd6c0 Delete type=0 #24 -2025/01/12-15:39:07.053127 7f0ab9bff6c0 Level-0 table #29: started -2025/01/12-15:39:07.053167 7f0ab9bff6c0 Level-0 table #29: 0 bytes OK -2025/01/12-15:39:07.059547 7f0ab9bff6c0 Delete type=0 #27 -2025/01/12-15:39:07.066140 7f0ab9bff6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) +2025/01/15-11:51:12.467493 7f4744ffa6c0 Recovering log #36 +2025/01/15-11:51:12.477732 7f4744ffa6c0 Delete type=3 #34 +2025/01/15-11:51:12.477823 7f4744ffa6c0 Delete type=0 #36 +2025/01/15-12:14:13.075366 7f473e7fc6c0 Level-0 table #41: started +2025/01/15-12:14:13.075395 7f473e7fc6c0 Level-0 table #41: 0 bytes OK +2025/01/15-12:14:13.081973 7f473e7fc6c0 Delete type=0 #39 +2025/01/15-12:14:13.095216 7f473e7fc6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) diff --git a/packs-system/lf-equipment/LOG.old b/packs-system/lf-equipment/LOG.old index eeb39c2..90206e8 100644 --- a/packs-system/lf-equipment/LOG.old +++ b/packs-system/lf-equipment/LOG.old @@ -1,7 +1,7 @@ -2025/01/12-10:35:21.828083 7f0abaffd6c0 Recovering log #20 -2025/01/12-10:35:21.838218 7f0abaffd6c0 Delete type=3 #18 -2025/01/12-10:35:21.838276 7f0abaffd6c0 Delete type=0 #20 -2025/01/12-10:41:38.476442 7f0ab9bff6c0 Level-0 table #25: started -2025/01/12-10:41:38.476474 7f0ab9bff6c0 Level-0 table #25: 0 bytes OK -2025/01/12-10:41:38.511873 7f0ab9bff6c0 Delete type=0 #23 -2025/01/12-10:41:38.546259 7f0ab9bff6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) +2025/01/13-23:50:18.537006 7fc3427fc6c0 Recovering log #32 +2025/01/13-23:50:18.546721 7fc3427fc6c0 Delete type=3 #30 +2025/01/13-23:50:18.546794 7fc3427fc6c0 Delete type=0 #32 +2025/01/14-00:20:04.955616 7fc340bff6c0 Level-0 table #37: started +2025/01/14-00:20:04.955640 7fc340bff6c0 Level-0 table #37: 0 bytes OK +2025/01/14-00:20:04.964840 7fc340bff6c0 Delete type=0 #35 +2025/01/14-00:20:04.964991 7fc340bff6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) diff --git a/packs-system/lf-equipment/MANIFEST-000026 b/packs-system/lf-equipment/MANIFEST-000026 deleted file mode 100644 index 07b73bf..0000000 Binary files a/packs-system/lf-equipment/MANIFEST-000026 and /dev/null differ diff --git a/packs-system/lf-equipment/MANIFEST-000038 b/packs-system/lf-equipment/MANIFEST-000038 new file mode 100644 index 0000000..b8d6ce3 Binary files /dev/null and b/packs-system/lf-equipment/MANIFEST-000038 differ diff --git a/packs-system/lf-gifts/000028.log b/packs-system/lf-gifts/000040.log similarity index 100% rename from packs-system/lf-gifts/000028.log rename to packs-system/lf-gifts/000040.log diff --git a/packs-system/lf-gifts/CURRENT b/packs-system/lf-gifts/CURRENT index 8b15215..59611b0 100644 --- a/packs-system/lf-gifts/CURRENT +++ b/packs-system/lf-gifts/CURRENT @@ -1 +1 @@ -MANIFEST-000026 +MANIFEST-000038 diff --git a/packs-system/lf-gifts/LOG b/packs-system/lf-gifts/LOG index e301696..b22a9d5 100644 --- a/packs-system/lf-gifts/LOG +++ b/packs-system/lf-gifts/LOG @@ -1,7 +1,7 @@ -2025/01/12-15:30:20.587864 7f0abbfff6c0 Recovering log #24 -2025/01/12-15:30:20.598665 7f0abbfff6c0 Delete type=3 #22 -2025/01/12-15:30:20.598718 7f0abbfff6c0 Delete type=0 #24 -2025/01/12-15:39:07.046715 7f0ab9bff6c0 Level-0 table #29: started -2025/01/12-15:39:07.046747 7f0ab9bff6c0 Level-0 table #29: 0 bytes OK -2025/01/12-15:39:07.052973 7f0ab9bff6c0 Delete type=0 #27 -2025/01/12-15:39:07.066129 7f0ab9bff6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) +2025/01/15-11:51:12.479884 7f473ffff6c0 Recovering log #36 +2025/01/15-11:51:12.490107 7f473ffff6c0 Delete type=3 #34 +2025/01/15-11:51:12.490160 7f473ffff6c0 Delete type=0 #36 +2025/01/15-12:14:13.068889 7f473e7fc6c0 Level-0 table #41: started +2025/01/15-12:14:13.068947 7f473e7fc6c0 Level-0 table #41: 0 bytes OK +2025/01/15-12:14:13.075233 7f473e7fc6c0 Delete type=0 #39 +2025/01/15-12:14:13.095205 7f473e7fc6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) diff --git a/packs-system/lf-gifts/LOG.old b/packs-system/lf-gifts/LOG.old index 9cc59cd..089bdc6 100644 --- a/packs-system/lf-gifts/LOG.old +++ b/packs-system/lf-gifts/LOG.old @@ -1,7 +1,7 @@ -2025/01/12-10:35:21.840236 7f0abb7fe6c0 Recovering log #20 -2025/01/12-10:35:21.850711 7f0abb7fe6c0 Delete type=3 #18 -2025/01/12-10:35:21.850769 7f0abb7fe6c0 Delete type=0 #20 -2025/01/12-10:41:38.512028 7f0ab9bff6c0 Level-0 table #25: started -2025/01/12-10:41:38.512054 7f0ab9bff6c0 Level-0 table #25: 0 bytes OK -2025/01/12-10:41:38.546064 7f0ab9bff6c0 Delete type=0 #23 -2025/01/12-10:41:38.546272 7f0ab9bff6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) +2025/01/13-23:50:18.548621 7fc342ffd6c0 Recovering log #32 +2025/01/13-23:50:18.558159 7fc342ffd6c0 Delete type=3 #30 +2025/01/13-23:50:18.558214 7fc342ffd6c0 Delete type=0 #32 +2025/01/14-00:20:04.927200 7fc340bff6c0 Level-0 table #37: started +2025/01/14-00:20:04.927267 7fc340bff6c0 Level-0 table #37: 0 bytes OK +2025/01/14-00:20:04.936500 7fc340bff6c0 Delete type=0 #35 +2025/01/14-00:20:04.964948 7fc340bff6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) diff --git a/packs-system/lf-gifts/MANIFEST-000026 b/packs-system/lf-gifts/MANIFEST-000026 deleted file mode 100644 index 07b73bf..0000000 Binary files a/packs-system/lf-gifts/MANIFEST-000026 and /dev/null differ diff --git a/packs-system/lf-gifts/MANIFEST-000038 b/packs-system/lf-gifts/MANIFEST-000038 new file mode 100644 index 0000000..b8d6ce3 Binary files /dev/null and b/packs-system/lf-gifts/MANIFEST-000038 differ diff --git a/packs-system/lf-skills/000028.log b/packs-system/lf-skills/000040.log similarity index 100% rename from packs-system/lf-skills/000028.log rename to packs-system/lf-skills/000040.log diff --git a/packs-system/lf-skills/CURRENT b/packs-system/lf-skills/CURRENT index 8b15215..59611b0 100644 --- a/packs-system/lf-skills/CURRENT +++ b/packs-system/lf-skills/CURRENT @@ -1 +1 @@ -MANIFEST-000026 +MANIFEST-000038 diff --git a/packs-system/lf-skills/LOG b/packs-system/lf-skills/LOG index da807fe..c0c4c35 100644 --- a/packs-system/lf-skills/LOG +++ b/packs-system/lf-skills/LOG @@ -1,7 +1,7 @@ -2025/01/12-15:30:20.563784 7f0abb7fe6c0 Recovering log #24 -2025/01/12-15:30:20.573785 7f0abb7fe6c0 Delete type=3 #22 -2025/01/12-15:30:20.573842 7f0abb7fe6c0 Delete type=0 #24 -2025/01/12-15:39:07.059698 7f0ab9bff6c0 Level-0 table #29: started -2025/01/12-15:39:07.059727 7f0ab9bff6c0 Level-0 table #29: 0 bytes OK -2025/01/12-15:39:07.066010 7f0ab9bff6c0 Delete type=0 #27 -2025/01/12-15:39:07.066153 7f0ab9bff6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) +2025/01/15-11:51:12.456025 7f473effd6c0 Recovering log #36 +2025/01/15-11:51:12.465565 7f473effd6c0 Delete type=3 #34 +2025/01/15-11:51:12.465623 7f473effd6c0 Delete type=0 #36 +2025/01/15-12:14:13.082075 7f473e7fc6c0 Level-0 table #41: started +2025/01/15-12:14:13.082096 7f473e7fc6c0 Level-0 table #41: 0 bytes OK +2025/01/15-12:14:13.089104 7f473e7fc6c0 Delete type=0 #39 +2025/01/15-12:14:13.095226 7f473e7fc6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) diff --git a/packs-system/lf-skills/LOG.old b/packs-system/lf-skills/LOG.old index f523be5..6586acd 100644 --- a/packs-system/lf-skills/LOG.old +++ b/packs-system/lf-skills/LOG.old @@ -1,7 +1,7 @@ -2025/01/12-10:35:21.813613 7f0abbfff6c0 Recovering log #20 -2025/01/12-10:35:21.823625 7f0abbfff6c0 Delete type=3 #18 -2025/01/12-10:35:21.823728 7f0abbfff6c0 Delete type=0 #20 -2025/01/12-10:41:38.447331 7f0ab9bff6c0 Level-0 table #25: started -2025/01/12-10:41:38.447362 7f0ab9bff6c0 Level-0 table #25: 0 bytes OK -2025/01/12-10:41:38.476202 7f0ab9bff6c0 Delete type=0 #23 -2025/01/12-10:41:38.546247 7f0ab9bff6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) +2025/01/13-23:50:18.525256 7fc3417fa6c0 Recovering log #32 +2025/01/13-23:50:18.534648 7fc3417fa6c0 Delete type=3 #30 +2025/01/13-23:50:18.534721 7fc3417fa6c0 Delete type=0 #32 +2025/01/14-00:20:04.936609 7fc340bff6c0 Level-0 table #37: started +2025/01/14-00:20:04.936640 7fc340bff6c0 Level-0 table #37: 0 bytes OK +2025/01/14-00:20:04.946142 7fc340bff6c0 Delete type=0 #35 +2025/01/14-00:20:04.964964 7fc340bff6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) diff --git a/packs-system/lf-skills/MANIFEST-000026 b/packs-system/lf-skills/MANIFEST-000026 deleted file mode 100644 index 07b73bf..0000000 Binary files a/packs-system/lf-skills/MANIFEST-000026 and /dev/null differ diff --git a/packs-system/lf-skills/MANIFEST-000038 b/packs-system/lf-skills/MANIFEST-000038 new file mode 100644 index 0000000..b8d6ce3 Binary files /dev/null and b/packs-system/lf-skills/MANIFEST-000038 differ diff --git a/packs-system/lf-vulnerabilities/000028.log b/packs-system/lf-vulnerabilities/000040.log similarity index 100% rename from packs-system/lf-vulnerabilities/000028.log rename to packs-system/lf-vulnerabilities/000040.log diff --git a/packs-system/lf-vulnerabilities/CURRENT b/packs-system/lf-vulnerabilities/CURRENT index 8b15215..59611b0 100644 --- a/packs-system/lf-vulnerabilities/CURRENT +++ b/packs-system/lf-vulnerabilities/CURRENT @@ -1 +1 @@ -MANIFEST-000026 +MANIFEST-000038 diff --git a/packs-system/lf-vulnerabilities/LOG b/packs-system/lf-vulnerabilities/LOG index e9c9bb4..bc3784c 100644 --- a/packs-system/lf-vulnerabilities/LOG +++ b/packs-system/lf-vulnerabilities/LOG @@ -1,7 +1,7 @@ -2025/01/12-15:30:20.600324 7f0aba7fc6c0 Recovering log #24 -2025/01/12-15:30:20.611083 7f0aba7fc6c0 Delete type=3 #22 -2025/01/12-15:30:20.611177 7f0aba7fc6c0 Delete type=0 #24 -2025/01/12-15:39:07.039586 7f0ab9bff6c0 Level-0 table #29: started -2025/01/12-15:39:07.039637 7f0ab9bff6c0 Level-0 table #29: 0 bytes OK -2025/01/12-15:39:07.046575 7f0ab9bff6c0 Delete type=0 #27 -2025/01/12-15:39:07.066113 7f0ab9bff6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) +2025/01/15-11:51:12.492006 7f473f7fe6c0 Recovering log #36 +2025/01/15-11:51:12.501404 7f473f7fe6c0 Delete type=3 #34 +2025/01/15-11:51:12.501471 7f473f7fe6c0 Delete type=0 #36 +2025/01/15-12:14:13.089202 7f473e7fc6c0 Level-0 table #41: started +2025/01/15-12:14:13.089224 7f473e7fc6c0 Level-0 table #41: 0 bytes OK +2025/01/15-12:14:13.095112 7f473e7fc6c0 Delete type=0 #39 +2025/01/15-12:14:13.095235 7f473e7fc6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) diff --git a/packs-system/lf-vulnerabilities/LOG.old b/packs-system/lf-vulnerabilities/LOG.old index b677d48..ebf1a23 100644 --- a/packs-system/lf-vulnerabilities/LOG.old +++ b/packs-system/lf-vulnerabilities/LOG.old @@ -1,7 +1,7 @@ -2025/01/12-10:35:21.852854 7f0aba7fc6c0 Recovering log #20 -2025/01/12-10:35:21.862623 7f0aba7fc6c0 Delete type=3 #18 -2025/01/12-10:35:21.862679 7f0aba7fc6c0 Delete type=0 #20 -2025/01/12-10:41:38.411505 7f0ab9bff6c0 Level-0 table #25: started -2025/01/12-10:41:38.411560 7f0ab9bff6c0 Level-0 table #25: 0 bytes OK -2025/01/12-10:41:38.447140 7f0ab9bff6c0 Delete type=0 #23 -2025/01/12-10:41:38.546228 7f0ab9bff6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) +2025/01/13-23:50:18.560230 7fc341ffb6c0 Recovering log #32 +2025/01/13-23:50:18.569958 7fc341ffb6c0 Delete type=3 #30 +2025/01/13-23:50:18.570014 7fc341ffb6c0 Delete type=0 #32 +2025/01/14-00:20:04.946243 7fc340bff6c0 Level-0 table #37: started +2025/01/14-00:20:04.946272 7fc340bff6c0 Level-0 table #37: 0 bytes OK +2025/01/14-00:20:04.955531 7fc340bff6c0 Delete type=0 #35 +2025/01/14-00:20:04.964977 7fc340bff6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end) diff --git a/packs-system/lf-vulnerabilities/MANIFEST-000026 b/packs-system/lf-vulnerabilities/MANIFEST-000026 deleted file mode 100644 index 07b73bf..0000000 Binary files a/packs-system/lf-vulnerabilities/MANIFEST-000026 and /dev/null differ diff --git a/packs-system/lf-vulnerabilities/MANIFEST-000038 b/packs-system/lf-vulnerabilities/MANIFEST-000038 new file mode 100644 index 0000000..b8d6ce3 Binary files /dev/null and b/packs-system/lf-vulnerabilities/MANIFEST-000038 differ diff --git a/styles/fvtt-lethal-fantasy.less b/styles/fvtt-lethal-fantasy.less index 0beac77..a8313f1 100644 --- a/styles/fvtt-lethal-fantasy.less +++ b/styles/fvtt-lethal-fantasy.less @@ -4,7 +4,7 @@ .lethalfantasy { @import "mixins.less"; @import "character.less"; - @import "opponent.less"; + @import "monster.less"; @import "skill.less"; @import "gift.less"; @import "weapon.less"; diff --git a/styles/monster.less b/styles/monster.less new file mode 100644 index 0000000..c9592ff --- /dev/null +++ b/styles/monster.less @@ -0,0 +1,483 @@ +.monster-content { + .sheet-common(); + .character-sheet-common(); + overflow: scroll; +} + +.monster-main { + display: flex; + + .monster-pc { + display: flex; + gap: 10px; + flex: 1; + + .monster-left { + min-width: 220px; + max-width: 220px; + display: flex; + flex-direction: column; + + .monster-left-image { + display: flex; + justify-content: center; + align-items: center; + padding-bottom: 8px; + .monster-img { + height: 140px; + width: 140px; + width: auto; + border: none; + } + } + .monster-hp { + display: flex; + gap: 2px; + align-items: center; + .monster-hp-value { + .form-fields input { + flex: none; + min-width: 3rem; + max-width: 3rem; + margin-left: 10px; + font-size: calc(var(--font-size-standard) * 1.4); + } + } + .monster-hp-max { + clear: both; + display: flex; + flex-direction: row; + flex-wrap: wrap; + margin: 3px 0; + align-items: center; + input { + width: 50px; + text-align: center; + font-size: calc(var(--font-size-standard) * 1.4); + } + } + } + } + + .monster-right { + display: flex; + flex-direction: column; + gap: 4px; + + .monster-name { + display: flex; + input { + width: 400px; + } + } + } + } + + .monster-pc-play { + min-width: 400px; + } + + .monster-pc-edit { + min-width: 400px; + } + + .monster-characteristics { + display: flex; + flex-direction: column; + gap: 4px; + flex: 1; + + .monster-characteristic { + display: flex; + align-items: center; + .rollable:hover, + .rollable:focus { + text-shadow: 0 0 8px var(--color-shadow-primary); + cursor: pointer; + } + .form-group { + flex: 1; + padding-left: 4px; + .form-fields { + flex: none; + width: 3rem; + } + } + } + } + .monster-resists { + display: flex; + flex-direction: column; + gap: 4px; + flex: 1; + + .monster-resist { + display: flex; + align-items: center; + margin-right: 0.5rem; + .rollable:hover, + .rollable:focus { + text-shadow: 0 0 8px var(--color-shadow-primary); + cursor: pointer; + } + .name { + flex: 1; + min-width: 3rem; + margin-left: 0.5rem; + } + .form-group { + flex: 1; + padding-left: 4px; + .form-fields { + flex: none; + width: 50px; + } + } + } + } + + .monster-movements { + display: flex; + flex-direction: column; + gap: 4px; + flex: 1; + + .monster-movement { + display: flex; + align-items: center; + margin-right: 0.5rem; + .rollable:hover, + .rollable:focus { + text-shadow: 0 0 8px var(--color-shadow-primary); + cursor: pointer; + } + .name { + flex: 1; + min-width: 3rem; + margin-left: 0.5rem; + } + .form-group { + flex: 1; + padding-left: 4px; + .form-fields { + flex: none; + width: 50px; + } + } + } + } + + .monster-saves { + display: flex; + flex-direction: column; + gap: 4px; + flex: 1; + + .monster-save { + display: flex; + align-items: center; + margin-right: 0.5rem; + .rollable:hover, + .rollable:focus { + text-shadow: 0 0 8px var(--color-shadow-primary); + cursor: pointer; + } + .name { + flex: 0; + min-width: 5rem; + max-width: 5rem; + margin-left: 0.5rem; + } + .name-pain { + flex: 0; + min-width: 3rem; + max-width: 3rem; + margin-left: 0.5rem; + } + .form-group { + flex: 0; + padding-left: 4px; + .form-fields { + flex: none; + width: 50px; + } + } + } + } + + .monster-characteristics-play { + min-width: 225px; + } + + .monster-characteristic-edit { + min-width: 400px; + } +} + +.tab.monster-biography { + .biodata { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 4px; + .biodata-elem { + display: flex; + align-items: center; + gap: 4px; + .name { + min-width: 8rem; + } + } + } +} + +.monster-biography { + prose-mirror.inactive { + min-height: 40px; + } + prose-mirror.active { + min-height: 150px; + } +} + +.tab.monster-skills { + display: grid; + grid-template-columns: 1fr; + legend { + a { + font-size: calc(var(--font-size-standard) * 1); + padding-left: 4px; + } + } + .skills { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 4px; + .skill { + display: flex; + align-items: center; + gap: 4px; + .item-img { + width: 24px; + height: 24px; + } + .name { + min-width: 12rem; + } + } + } + .gifts { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 4px; + .gift { + display: flex; + align-items: center; + gap: 4px; + .item-img { + width: 24px; + height: 24px; + } + .name { + min-width: 12rem; + } + } + } + .vulnerabilities { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 4px; + .vulnerability { + display: flex; + align-items: center; + gap: 4px; + .item-img { + width: 24px; + height: 24px; + } + .name { + min-width: 12rem; + } + } + } +} + +.tab.monster-equipment { + display: grid; + grid-template-columns: 1fr; + legend { + a { + font-size: calc(var(--font-size-standard) * 1.4); + padding-left: 4px; + } + } + + .moneys { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 4px; + } + + .equipments { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 4px; + .equipment { + display: flex; + align-items: center; + gap: 4px; + .item-img { + width: 24px; + height: 24px; + margin: 4px 0 0 0; + } + .name { + min-width: 12rem; + } + } + .name { + min-width: 12rem; + } + } +} + +.tab.monster-combat { + display: grid; + grid-template-columns: 1fr; + legend { + a { + font-size: calc(var(--font-size-standard) * 1.4); + padding-left: 4px; + } + } + + .combat-details { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 4px; + .combat-detail { + display: flex; + align-items: center; + gap: 4px; + button { + min-width: 10rem; + } + .armor-hp { + min-width: 20rem; + max-width: 20rem; + .input { + min-width: 3rem; + max-width: 3rem; + } + } + } + } + + .attacks { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 4px; + .attack { + display: flex; + align-items: center; + gap: 4px; + .numeric { + width: 3rem; + } + .attack-icons a { + margin-left: 8px; + margin-right: 8px; + } + } + } + + .armors { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 4px; + .armor { + display: flex; + align-items: center; + gap: 4px; + .item-img { + width: 24px; + height: 24px; + margin: 4px 0 0 0; + } + } + .name { + min-width: 12rem; + } + } +} + +.tab.monster-spells { + display: grid; + grid-template-columns: 1fr; + legend { + a { + font-size: calc(var(--font-size-standard) * 1.4); + padding-left: 4px; + } + } + + .spells { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 4px; + .spell { + display: flex; + align-items: center; + gap: 4px; + .item-img { + width: 24px; + height: 24px; + } + .name { + min-width: 12rem; + } + } + } + + prose-mirror.inactive { + min-height: 40px; + } + prose-mirror.active { + min-height: 150px; + } +} + +.tab.monster-miracles { + display: grid; + grid-template-columns: 1fr; + legend { + a { + font-size: calc(var(--font-size-standard) * 1.4); + padding-left: 4px; + } + } + + .miracles { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 4px; + .miracle { + display: flex; + align-items: center; + gap: 4px; + .item-img { + width: 24px; + height: 24px; + } + .name { + min-width: 12rem; + } + } + } + + prose-mirror.inactive { + min-height: 40px; + } + prose-mirror.active { + min-height: 150px; + } +} diff --git a/styles/opponent.less b/styles/opponent.less deleted file mode 100644 index b00f549..0000000 --- a/styles/opponent.less +++ /dev/null @@ -1,86 +0,0 @@ -.opponent-content { - .sheet-common(); - overflow: scroll; - label { - font-family: var(--font-secondary); - font-size: calc(var(--font-size-standard) * 1.4); - } -} - -.opponent-header { - display: flex; - align-items: center; - justify-content: center; - .opponent-img { - width: 100px; - height: auto; - margin: 10px; - } - .character-name { - display: flex; - width: 100%; - } -} - -.opponent-main { - display: flex; - flex-direction: row; - gap: 10px; - - .opponent-gauche { - display: flex; - flex-direction: column; - min-width: 250px; - - .opponent-caracteristiques { - display: flex; - flex-direction: column; - .form-fields { - flex: none; - input { - width: 50px; - } - } - } - - .opponent-attacks { - legend { - a { - font-size: calc(var(--font-size-standard) * 1.6); - } - } - .opponent-attack { - display: flex; - justify-content: space-between; - } - } - - .opponent-spells { - legend { - a { - font-size: calc(var(--font-size-standard) * 1.6); - } - } - .opponent-spell { - display: flex; - justify-content: space-between; - } - } - } - - .opponent-droite { - .opponent-description { - flex: 1; /* Prend également l'espace disponible */ - min-width: 200px; /* Pour éviter que le contenu ne déborde */ - prose-mirror.inactive { - min-height: 40px; - } - prose-mirror.active { - min-height: 450px; - height: 100%; - min-width: 200px; - width: 100%; - } - } - } -} diff --git a/system.json b/system.json index 32ade9d..e4edc8a 100644 --- a/system.json +++ b/system.json @@ -6,7 +6,7 @@ "download": "#{DOWNLOAD}#", "url": "#{URL}#", "license": "LICENSE", - "version": "12.0.16", + "version": "12.0.17", "authors": [ { "name": "Uberwald", @@ -29,7 +29,7 @@ "documentTypes": { "Actor": { "character": { "htmlFields": ["description", "notes"] }, - "opponent": { "htmlFields": ["description"] } + "monster": { "htmlFields": ["description"] } }, "Item": { "skill": { "htmlFields": ["description"] }, @@ -49,7 +49,7 @@ }, "primaryTokenAttribute": "hp", "socket": true, - "background": "systems/fvtt-lethal-fantasy/assets/background.webp", + "background": "systems/fvtt-lethal-fantasy/assets/ui/lethal_fatansy_main_picture.webp", "packs": [ { "name": "lf-skills", diff --git a/templates/chat-message.hbs b/templates/chat-message.hbs index 217ad69..191cab8 100644 --- a/templates/chat-message.hbs +++ b/templates/chat-message.hbs @@ -1,65 +1,72 @@ {{!log 'chat-message' this}}
-
-
- -
- -
- {{upperFirst rollName}} : {{upperCase rollTarget.rollKey}} - - {{#if badResult}} - {{localize "LETHALFANTASY.Label.otherResult"}} : {{badResult}} - {{/if}} - - - {{#if rollTarget.weapon}} - {{rollTarget.weapon.name}} - {{/if}} - - Formula : {{titleFormula}} - - {{#each diceResults as |result|}} - {{result.dice}} : {{result.value}} - {{/each}} - -
+
+
+
- {{#if isSave}} -
- {{#if (eq resultType "success")}} - {{#if isPrivate}}?{{else}}{{localize "LETHALFANTASY.Roll.success"}}{{/if}} - {{else}} - {{#if isPrivate}}?{{else}}{{localize "LETHALFANTASY.Roll.failure"}}{{/if}} - {{/if}} -
- {{/if}} - {{#if isResource}} -
- {{#if (eq resultType "success")}} - {{#if isPrivate}}?{{else}}{{localize "LETHALFANTASY.Roll.success"}}{{/if}} - {{else}} - {{#if isPrivate}}?{{else}}{{localize "LETHALFANTASY.Roll.failure"}}{{#if isFailure}} ({{localize "LETHALFANTASY.Roll.resourceLost"}}){{/if}}{{/if}} - {{/if}} -
- {{/if}} - {{#if isDamage}} -
- {{#if (and isGM hasTarget)}} - {{{localize "LETHALFANTASY.Roll.displayArmor" targetName=targetName targetArmor=targetArmor realDamage=realDamage}}} - {{/if}} -
- {{/if}} - {{#unless isPrivate}} -
-

{{total}}

-
- {{#if D30result}} -
-

D30 result: {{D30result}}

-
- {{/if}} +
+ {{upperFirst rollName}} - {{/unless}} -
+ {{#if (match rollType "attack")}} + Attack roll ! + {{/if}} + {{#if (match rollType "defense")}} + Defense roll ! + {{/if}} + + {{#if badResult}} + {{localize "LETHALFANTASY.Label.otherResult"}} : {{badResult}} + {{/if}} + + {{#if rollTarget.weapon}} + {{rollTarget.weapon.name}} + {{/if}} + + Formula : {{titleFormula}} + + {{#each diceResults as |result|}} + {{result.dice}} : {{result.value}} + {{/each}} + +
+
+ {{#if isSave}} +
+ {{#if (eq resultType "success")}} + {{#if isPrivate}}?{{else}}{{localize "LETHALFANTASY.Roll.success"}}{{/if}} + {{else}} + {{#if isPrivate}}?{{else}}{{localize "LETHALFANTASY.Roll.failure"}}{{/if}} + {{/if}} +
+ {{/if}} + {{#if isResource}} +
+ {{#if (eq resultType "success")}} + {{#if isPrivate}}?{{else}}{{localize "LETHALFANTASY.Roll.success"}}{{/if}} + {{else}} + {{#if isPrivate}}?{{else}}{{localize "LETHALFANTASY.Roll.failure"}}{{#if isFailure}} ({{localize + "LETHALFANTASY.Roll.resourceLost"}}){{/if}}{{/if}} + {{/if}} +
+ {{/if}} + {{#if isDamage}} +
+ {{#if (and isGM hasTarget)}} + {{{localize "LETHALFANTASY.Roll.displayArmor" targetName=targetName targetArmor=targetArmor realDamage=realDamage}}} + {{/if}} +
+ {{/if}} + + {{#unless isPrivate}} +
+

{{total}}

+
+ {{#if D30result}} +
+

D30 result: {{D30result}}

+
+ {{/if}} + + {{/unless}} +
\ No newline at end of file diff --git a/templates/monster-biography.hbs b/templates/monster-biography.hbs new file mode 100644 index 0000000..5db62ad --- /dev/null +++ b/templates/monster-biography.hbs @@ -0,0 +1,39 @@ +
+ +
+ {{localize "LETHALFANTASY.Label.biodata"}} + +
+ +
+ Alignment + {{formInput systemFields.biodata.fields.alignment value=system.biodata.alignment }} +
+
+ Height + {{formInput systemFields.biodata.fields.height value=system.biodata.height }} +
+
+ Weight + {{formInput systemFields.biodata.fields.weight value=system.biodata.weight }} +
+
+ Length + {{formInput systemFields.biodata.fields.length value=system.biodata.length }} +
+
+ Vision + {{formInput systemFields.biodata.fields.vision value=system.biodata.vision }} +
+ +
+ +
+ +
+ {{localize "LETHALFANTASY.Label.description"}} + {{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description" + toggled=true}} +
+ +
\ No newline at end of file diff --git a/templates/monster-combat.hbs b/templates/monster-combat.hbs new file mode 100644 index 0000000..f8b4417 --- /dev/null +++ b/templates/monster-combat.hbs @@ -0,0 +1,59 @@ +
+ +
+ {{localize "LETHALFANTASY.Label.combatDetails"}} +
+
+ + + + +
+
+
+ +
+ {{localize "LETHALFANTASY.Label.attacks"}} +
+ {{#each system.attacks as |item key|}} +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+ + + + + + + + + + + + + +
+ +
+ {{/each}} +
+
+
\ No newline at end of file diff --git a/templates/monster-main.hbs b/templates/monster-main.hbs new file mode 100644 index 0000000..1452e6f --- /dev/null +++ b/templates/monster-main.hbs @@ -0,0 +1,152 @@ +
+ {{log "monster-main" this}} + +
+ {{localize "LETHALFANTASY.Label.monster"}} +
+
+
+ +
+ +
+
+ {{localize "LETHALFANTASY.Label.HP"}} + {{formInput systemFields.hp.fields.value value=system.hp.value disabled=isPlayMode classes="monster-hp-value"}} +  /  + {{formInput systemFields.hp.fields.max value=system.hp.max disabled=isPlayMode classes="monster-hp-value"}} +
+
+ {{localize "LETHALFANTASY.Label.perception"}} + {{formInput systemFields.perception.fields.value value=system.perception.value disabled=isPlayMode + classes="monster-hp"}} +
+
+ +
+
+
+ {{formInput fields.name value=source.name rootId=partId disabled=isPlayMode}} + + + +
+ +
+ {{localize "LETHALFANTASY.Label.Saves"}} +
+
+ + {{localize "LETHALFANTASY.Label.saves.will"}} + + {{formField systemFields.saves.fields.will.fields.value value=system.saves.will.value disabled=isPlayMode }} + + + {{localize "LETHALFANTASY.Label.saves.dodge"}} + + + {{formField systemFields.saves.fields.dodge.fields.value value=system.saves.dodge.value + disabled=isPlayMode}} + + + {{localize "LETHALFANTASY.Label.saves.toughness"}} + + + {{formField systemFields.saves.fields.toughness.fields.value value=system.saves.toughness.value + disabled=isPlayMode}} +
+
+ + + {{localize "LETHALFANTASY.Label.saves.contagion"}} + + + {{formField systemFields.saves.fields.contagion.fields.value value=system.saves.contagion.value + disabled=isPlayMode}} + + + + {{localize "LETHALFANTASY.Label.saves.poison"}} + + + {{formField systemFields.saves.fields.poison.fields.value value=system.saves.poison.value + disabled=isPlayMode }} + +
+
+
+ +
+ {{localize "LETHALFANTASY.Label.Resist"}} +
+
+ {{localize + "LETHALFANTASY.Label.resistTorture"}} + {{formField systemFields.resists.fields.resistTorture.fields.value value=system.resists.resistTorture.value + disabled=isPlayMode + }} + {{localize + "LETHALFANTASY.Label.resistPerformance"}} + {{formField systemFields.resists.fields.resistPerformance.fields.value value=system.resists.resistPerformance.value + disabled=isPlayMode + }} + {{localize + "LETHALFANTASY.Label.resistIntimidation"}} + {{formField systemFields.resists.fields.resistIntimidation.fields.value value=system.resists.resistIntimidation.value + disabled=isPlayMode }} +
+
+
+ + +
+ {{localize "LETHALFANTASY.Label.Movement"}} +
+
+ {{localize "LETHALFANTASY.Label.movement.walk"}} + {{formField systemFields.movement.fields.walk value=system.movement.walk disabled=isPlayMode}} + {{localize "LETHALFANTASY.Label.movement.jog"}} + {{formField systemFields.movement.fields.jog value=system.challenges.movement.jog disabled=isPlayMode}} + {{localize "LETHALFANTASY.Label.movement.run"}} + {{formField systemFields.movement.fields.run value=system.movement.run disabled=isPlayMode}} + {{localize "LETHALFANTASY.Label.movement.sprint"}} + {{formField systemFields.movement.fields.sprint value=system.movement.sprint disabled=isPlayMode}} +
+
+
+ +
+
+
+ +
+ {{localize "LETHALFANTASY.Label.characteristics"}} +
+ {{localize "LETHALFANTASY.Label.int"}} + {{formField systemFields.characteristics.fields.int.fields.value value=system.characteristics.int.value + disabled=isPlayMode data-char-id="int" }} + + {{formField systemFields.characteristics.fields.int.fields.percent value=system.characteristics.int.percent + disabled=isPlayMode type="number" }} +
+
+ {{localize "LETHALFANTASY.Label.dex"}} + {{formField systemFields.characteristics.fields.dex.fields.value value=system.characteristics.dex.value + disabled=isPlayMode data-char-id="wis" }} + + {{formField systemFields.characteristics.fields.dex.fields.percent value=system.characteristics.dex.percent + disabled=isPlayMode type="number" }} +
+
+ +
\ No newline at end of file diff --git a/templates/roll-dialog.hbs b/templates/roll-dialog.hbs index 4e211a5..94c3197 100644 --- a/templates/roll-dialog.hbs +++ b/templates/roll-dialog.hbs @@ -1,55 +1,63 @@
- -
- {{localize (concat "LETHALFANTASY.Label." rollType)}} - {{#if hasModifier}} -
{{upperCase rollName}} : {{baseFormula}} + {{baseValue}}
- {{else}} -
{{upperCase rollName}} : {{baseFormula}}
- {{/if}} - {{#if rollTarget.weapon}} -
{{localize "LETHALFANTASY.Label.baseModifier"}} : {{rollTarget.charModifier}}
-
{{localize "LETHALFANTASY.Label.weapon"}} : {{rollTarget.weapon.name}}
-
{{localize "LETHALFANTASY.Label.skill"}} : {{rollTarget.name}}
-
{{localize "LETHALFANTASY.Label.skillBonus"}} : {{rollTarget.weaponSkillModifier}}
- {{/if}} -
- - {{#if hasFavor}} -
- {{localize "LETHALFANTASY.Roll.favorDisfavor"}} - -
+ +
+ {{localize (concat "LETHALFANTASY.Label." rollType)}} + + {{#if (match rollType "attack")}} +
Attack roll !
{{/if}} - + {{#if (match rollType "defense")}} +
Attack roll !
+ {{/if}} + {{#if hasModifier}} -
- {{localize "LETHALFANTASY.Roll.modifierBonusMalus"}} - - - {{#if (eq rollType "save")}} - {{#if rollTarget.magicUser}} -
- Save against spell (+{{rollTarget.actorModifiers.saveModifier}}) ? - -
- {{/if}} - {{/if}} -
+
{{upperCase rollName}} : {{baseFormula}} + {{baseValue}}
+ {{else}} +
{{upperCase rollName}} : {{baseFormula}}
{{/if}} - - {{#if hasChangeDice}} -
- {{localize "LETHALFANTASY.Roll.changeDice"}} - -
+ {{#if rollTarget.weapon}} +
{{localize "LETHALFANTASY.Label.baseModifier"}} : {{rollTarget.charModifier}}
+
{{localize "LETHALFANTASY.Label.weapon"}} : {{rollTarget.weapon.name}}
+
{{localize "LETHALFANTASY.Label.skill"}} : {{rollTarget.name}}
+
{{localize "LETHALFANTASY.Label.skillBonus"}} : {{rollTarget.weaponSkillModifier}}
{{/if}} +
+ + {{#if hasFavor}} +
+ {{localize "LETHALFANTASY.Roll.favorDisfavor"}} + +
+ {{/if}} + + {{#if hasModifier}} +
+ {{localize "LETHALFANTASY.Roll.modifierBonusMalus"}} + + + {{#if (eq rollType "save")}} + {{#if rollTarget.magicUser}} +
+ Save against spell (+{{rollTarget.actorModifiers.saveModifier}}) ? + +
+ {{/if}} + {{/if}} +
+ {{/if}} + + {{#if hasChangeDice}} +
+ {{localize "LETHALFANTASY.Roll.changeDice"}} + +
+ {{/if}}
{{localize "LETHALFANTASY.Roll.visibility"}}