From 37b77673e572b8628394715475022f0fdb4bc680 Mon Sep 17 00:00:00 2001 From: Lionel Zuber Date: Wed, 7 Jun 2023 15:19:19 +0200 Subject: [PATCH] Feat/resume (#127) * feat: add resume application * feat: switch resume to line template * feat: allow to open/close resume application --- css/svnsea2e.css | 57 +++++++++++++ lang/de.json | 5 +- lang/en.json | 5 +- lang/es.json | 5 +- lang/fr.json | 5 +- lang/it.json | 5 +- lang/pt-BR.json | 5 +- module/actor/sheets/hero.js | 2 +- module/svnsea2e.js | 41 +++++++--- module/toolbox/socket.js | 32 ++++++++ module/toolbox/toolbox.js | 144 +++++++++++++++++++++++++++++++++ scss/components/_toolbox.scss | 41 ++++++++++ scss/svnsea2e.scss | 4 + templates/toolbox/toolbox.html | 30 +++++++ 14 files changed, 363 insertions(+), 18 deletions(-) create mode 100644 module/toolbox/socket.js create mode 100644 module/toolbox/toolbox.js create mode 100644 scss/components/_toolbox.scss create mode 100644 templates/toolbox/toolbox.html diff --git a/css/svnsea2e.css b/css/svnsea2e.css index 9925d1e..56b2aa6 100644 --- a/css/svnsea2e.css +++ b/css/svnsea2e.css @@ -939,4 +939,61 @@ input[type=range]::-webkit-slider-thumb { .item .sheet-header .item-subtitle { font-size: 0.8em; opacity: 0.5; +} + +.toolbox { + font-size: 0.8em; +} +.toolbox .window-content { + background: none; + color: white; +} +.toolbox .items { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + gap: 8px; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + padding-bottom: 16px; + padding-right: 8px; +} +.toolbox .actor-resume { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-flex: 1; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} +.toolbox .actor-resume h3 { + text-align: center; + padding: 0; + margin: 0; + color: white; +} +.toolbox .score-container { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-pack: distribute; + justify-content: space-around; + gap: 16px; +} +.toolbox .score-container > div { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } \ No newline at end of file diff --git a/lang/de.json b/lang/de.json index 1f86c50..9a2f008 100644 --- a/lang/de.json +++ b/lang/de.json @@ -333,5 +333,8 @@ "SVNSEA2E.UseForMe": "Use for me", "SVNSEA2E.UseForHelpMe": "Use for help me", "SVNSEA2E.NotEnoughHero": "Not enough hero points", - "SVNSEA2E.JoieDeVivre": "Joie de Vivre" + "SVNSEA2E.JoieDeVivre": "Joie de Vivre", + "SVNSEA2E.Toolbox": "Toolbox", + "SVNSEA2E.EmptyToolbox": "Drop some actor here", + "SVNSEA2E.OpenToolbox": "Open toolbox" } diff --git a/lang/en.json b/lang/en.json index 50df79c..b0d780c 100644 --- a/lang/en.json +++ b/lang/en.json @@ -341,5 +341,8 @@ "SVNSEA2E.UseForMe": "Use for me", "SVNSEA2E.UseForHelpMe": "Use for help me", "SVNSEA2E.NotEnoughHero": "Not enough hero points", - "SVNSEA2E.JoieDeVivre": "Joie de Vivre" + "SVNSEA2E.JoieDeVivre": "Joie de Vivre", + "SVNSEA2E.Toolbox": "Toolbox", + "SVNSEA2E.EmptyToolbox": "Drop some actor here", + "SVNSEA2E.OpenToolbox": "Open toolbox" } diff --git a/lang/es.json b/lang/es.json index 658092e..0d88500 100644 --- a/lang/es.json +++ b/lang/es.json @@ -342,5 +342,8 @@ "SVNSEA2E.UseForMe": "Use for me", "SVNSEA2E.UseForHelpMe": "Use for help me", "SVNSEA2E.NotEnoughHero": "Not enough hero points", - "SVNSEA2E.JoieDeVivre": "Joie de Vivre" + "SVNSEA2E.JoieDeVivre": "Joie de Vivre", + "SVNSEA2E.Toolbox": "Toolbox", + "SVNSEA2E.EmptyToolbox": "Drop some actor here", + "SVNSEA2E.OpenToolbox": "Open toolbox" } diff --git a/lang/fr.json b/lang/fr.json index c632459..eb44c89 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -337,5 +337,8 @@ "SVNSEA2E.UseForMe": "Utilisé pour moi", "SVNSEA2E.UseForHelpMe": "Utilisé pour m'aider", "SVNSEA2E.NotEnoughHero": "Pas assez de points d'héroïsme", - "SVNSEA2E.JoieDeVivre": "Joie de Vivre" + "SVNSEA2E.JoieDeVivre": "Joie de Vivre", + "SVNSEA2E.EmptyToolbox": "Glisser des acteurs ici", + "SVNSEA2E.Toolbox": "Boite à outils", + "SVNSEA2E.OpenToolbox": "Ouvrir la boite à outils" } diff --git a/lang/it.json b/lang/it.json index 42b83c8..faeec3c 100644 --- a/lang/it.json +++ b/lang/it.json @@ -332,5 +332,8 @@ "SVNSEA2E.AddOneToDice": "Aggiungi 1 al dado", "SVNSEA2E.AddOneToDiced": "+1 aggiunto a tutti i dadi.", "SVNSEA2E.AddToInitiativeTracker": "Aggiungi all'iniziativa", - "SVNSEA2E.Initiative": "Incrementi" + "SVNSEA2E.Initiative": "Incrementi", + "SVNSEA2E.Toolbox": "Toolbox", + "SVNSEA2E.EmptyToolbox": "Drop some actor here", + "SVNSEA2E.OpenToolbox": "Open toolbox" } diff --git a/lang/pt-BR.json b/lang/pt-BR.json index 8945fcd..b13ce99 100644 --- a/lang/pt-BR.json +++ b/lang/pt-BR.json @@ -341,5 +341,8 @@ "SVNSEA2E.UseForMe": "Usado por mim", "SVNSEA2E.UseForHelpMe": "Usado para me ajudar", "SVNSEA2E.NotEnoughHero": "Pontos heroicos insuficientes", - "SVNSEA2E.JoieDeVivre": "Joie de Vivre" + "SVNSEA2E.JoieDeVivre": "Joie de Vivre", + "SVNSEA2E.Toolbox": "Toolbox", + "SVNSEA2E.EmptyToolbox": "Drop some actor here", + "SVNSEA2E.OpenToolbox": "Open toolbox" } diff --git a/module/actor/sheets/hero.js b/module/actor/sheets/hero.js index 19767b0..f45a9ea 100644 --- a/module/actor/sheets/hero.js +++ b/module/actor/sheets/hero.js @@ -1,5 +1,5 @@ -import ActorSheetSS2e from './base.js'; import { getItems, skillsToSheetData } from '../../helpers.js'; +import ActorSheetSS2e from './base.js'; /** * Extend the basic ActorSheet with some very simple modifications * @ext'../../dice.js't} diff --git a/module/svnsea2e.js b/module/svnsea2e.js index 28d2386..4008bd6 100644 --- a/module/svnsea2e.js +++ b/module/svnsea2e.js @@ -1,36 +1,36 @@ // Import Modules import { SVNSEA2E } from './config.js'; -import { preloadHandlebarsTemplates } from './templates.js'; import { registerSystemSettings } from './settings.js'; +import { preloadHandlebarsTemplates } from './templates.js'; // Import Applications import { SvnSea2EActor } from './actor/actor.js'; -import { ActorSheetSS2ePlayerCharacter } from './actor/sheets/playercharacter.js'; -import { ActorSheetSS2eHero } from './actor/sheets/hero.js'; import { ActorSheetSS2eBrute } from './actor/sheets/brute.js'; +import { ActorSheetSS2eDangerPts } from './actor/sheets/dangerpts.js'; +import { ActorSheetSS2eHero } from './actor/sheets/hero.js'; import { ActorSheetSS2eMonster } from './actor/sheets/monster.js'; -import { ActorSheetSS2eVillain } from './actor/sheets/villain.js'; +import { ActorSheetSS2ePlayerCharacter } from './actor/sheets/playercharacter.js'; import { ActorSheetSS2eShip } from './actor/sheets/ship.js'; -import { ActorSheetSS2eDangerPts } from './actor/sheets/dangerpts.js'; +import { ActorSheetSS2eVillain } from './actor/sheets/villain.js'; import { SvnSea2EItem } from './item/item.js'; import { ItemSheetSS2eAdvantage } from './item/sheets/advantage.js'; import { ItemSheetSS2eArtifact } from './item/sheets/artifact.js'; import { ItemSheetSS2eBackground } from './item/sheets/background.js'; import { ItemSheetSS2eDuelStyle } from './item/sheets/duelstyle.js'; import { ItemSheetSS2eMonsterQuality } from './item/sheets/monsterquality.js'; -import { ItemSheetSS2eSecretSociety } from './item/sheets/secretsociety.js'; import { ItemSheetSS2eScheme } from './item/sheets/scheme.js'; +import { ItemSheetSS2eSecretSociety } from './item/sheets/secretsociety.js'; import { ItemSheetSS2eShipAdventure } from './item/sheets/shipadventure.js'; import { ItemSheetSS2eShipBackground } from './item/sheets/shipbackground.js'; import { ItemSheetSS2eSorcery } from './item/sheets/sorcery.js'; import { ItemSheetSS2eStory } from './item/sheets/story.js'; -import LanguageSelector from './apps/language-selector.js'; -import SkillSelector from './apps/skill-selector.js'; -import * as migrations from './migration.js'; -import { ItemSheetSS2eVirtue } from './item/sheets/virtue.js'; -import { ItemSheetSS2eHubris } from './item/sheets/hubris.js'; import { chatEventHandler } from './eventhandler.js'; +import { ItemSheetSS2eHubris } from './item/sheets/hubris.js'; +import { ItemSheetSS2eVirtue } from './item/sheets/virtue.js'; +import * as migrations from './migration.js'; +import { emitCharacterChange } from './toolbox/socket.js'; +import { Toolbox } from './toolbox/toolbox.js'; Hooks.once('init', async function () { console.log(`7th Sea 2E | Initializing 7th Sea Second Edition System @@ -42,6 +42,7 @@ Hooks.once('init', async function () { }, config: SVNSEA2E, migrations: migrations, + toolbox: new Toolbox(), }; /** @@ -240,6 +241,8 @@ Hooks.once('ready', async function () { } chatEventHandler(); + + game.svnsea2e.toolbox.render(true); }); /* -------------------------------------------- */ @@ -306,6 +309,22 @@ Hooks.on('preCreateActor', function (document, entity, options, userId) { }); }); +Hooks.on('updateActor', function () { + emitCharacterChange(); +}); + +Hooks.on('renderActorDirectory', (app, html, data) => { + if (game.user.isGM) { + const button = document.createElement('button'); + button.style.width = '95%'; + button.innerHTML = game.i18n.localize('SVNSEA2E.OpenToolbox'); + button.addEventListener('click', () => { + game.svnsea2e.toolbox.render(true); + }); + html.find('.header-actions').after(button); + } +}); + async function getAllPackAdvantages() { const advantages = []; const packs = game.packs.entries; diff --git a/module/toolbox/socket.js b/module/toolbox/socket.js new file mode 100644 index 0000000..f04d6e8 --- /dev/null +++ b/module/toolbox/socket.js @@ -0,0 +1,32 @@ +export const SOCKET_NAME = 'system.svnsea2e'; +export const actions = { + characterChange: 'characterChanged', +}; + +export function registerSocketListeners() { + game.socket.on(SOCKET_NAME, (payload) => { + switch (payload.type) { + case actions.characterChange: + refreshToolbox(payload); + break; + + default: + console.warn( + new Error('L5R5E | This socket event is not supported'), + payload, + ); + break; + } + }); +} + +export function emitCharacterChange() { + game.svnsea2e.toolbox.refresh(); + game.socket.emit(SOCKET_NAME, { + type: actions.characterChange, + }); +} + +function refreshToolbox() { + game.svnsea2e.toolbox.refresh(); +} diff --git a/module/toolbox/toolbox.js b/module/toolbox/toolbox.js new file mode 100644 index 0000000..5abb0b2 --- /dev/null +++ b/module/toolbox/toolbox.js @@ -0,0 +1,144 @@ +/** + * L5R GM Toolbox dialog + * @extends {FormApplication} + */ +export class Toolbox extends FormApplication { + /** + * Settings + */ + object = {}; + + /** + * Assign the default options + * @override + */ + static get defaultOptions() { + const x = $(window).width(); + const y = $(window).height(); + return mergeObject(super.defaultOptions, { + id: 'svnsea-toolbox', + classes: ['toolbox'], + template: 'systems/svnsea2e/templates/toolbox/toolbox.html', + title: game.i18n.localize('SVNSEA2E.Toolbox'), + left: x - 650, + top: 20, + width: 300, + closeOnSubmit: false, + submitOnClose: false, + submitOnChange: true, + minimizable: true, + resizable: true, + dragDrop: [{ dropSelector: '.items' }], + }); + } + + /** + * Constructor + * @param {ApplicationOptions} options + */ + constructor(options = {}) { + super(options); + this._initialize(); + } + + /** + * Refresh data (used from socket) + */ + async refresh() { + if (!game.user.isGM) { + return; + } + this.object.showActors = this.object.items.map((it) => + game.actors.find((a) => `Actor.${a.id}` === it), + ); + this.render(false); + } + + /** + * Initialize the values + * @private + */ + _initialize() { + this.object = { + items: [], + }; + } + + /** + * @override + */ + render(force = false, options = {}) { + if (!game.user.isGM) { + return; + } + this.position.height = 'auto'; + return super.render(force, options); + } + + /** + * Construct and return the data object used to render the HTML template for this form application. + * @param options + * @return {Object} + * @override + */ + async getData(options = null) { + const data = await super.getData(options); + return { + ...data, + }; + } + + /** + * Listen to html elements + * @param {jQuery} html HTML content of the sheet. + * @override + */ + activateListeners(html) { + super.activateListeners(html); + } + + /** + * This method is called upon form submission after form data is validated + * @param event The initial triggering submission event + * @param formData The object of validated form data with which to update the object + * @returns A Promise which resolves once the update operation has completed + * @override + */ + async _updateObject(event, formData) { + this.render(false); + } + + /** @override */ + async _onDrop(event) { + if (!game.user.isGM) { + return; + } + event.preventDefault(); + + // Get dropped data + let data; + try { + data = JSON.parse(event.dataTransfer.getData('text/plain')); + } catch (err) { + return false; + } + if (!data) return false; + + // Case 2 - Dropped Actor + if (data.type === 'Actor') { + return this._onDropActor(event, data); + } + } + + /** + * Handle dropping an Actor on the sheet to trigger a Polymorph workflow + * @param {DragEvent} event The drop event + * @param {Object} data The data transfer + * @return {Object} OwnedItem data _getIndexeso create + * @private + */ + async _onDropActor(event, data) { + this.object.items.push(data.uuid); + this.refresh(); + } +} diff --git a/scss/components/_toolbox.scss b/scss/components/_toolbox.scss new file mode 100644 index 0000000..aa1a856 --- /dev/null +++ b/scss/components/_toolbox.scss @@ -0,0 +1,41 @@ +& { + font-size: 0.8em; +} + +.window-content { + background: none; + color: white; +} + +.items { + display: flex; + gap: $margin-md; + flex-direction: column; + padding-bottom: $padding-lg; + padding-right: $padding-md; +} + +.actor-resume { + display: flex; + flex: 1 1 100%; + justify-content: space-between; + + h3 { + text-align: center; + padding: 0; + margin: 0; + color: white; + } +} + +.score-container { + display: flex; + justify-content: space-around; + gap: $margin-lg; + + & > div { + display: flex; + flex-direction: column; + align-items: center; + } +} diff --git a/scss/svnsea2e.scss b/scss/svnsea2e.scss index ccb6107..d6d299d 100644 --- a/scss/svnsea2e.scss +++ b/scss/svnsea2e.scss @@ -36,3 +36,7 @@ .item { @import 'components/items'; } + +.toolbox { + @import 'components/toolbox'; +} diff --git a/templates/toolbox/toolbox.html b/templates/toolbox/toolbox.html new file mode 100644 index 0000000..dffaf66 --- /dev/null +++ b/templates/toolbox/toolbox.html @@ -0,0 +1,30 @@ +
+ {{#each object.showActors as |actor|}} +
+

{{actor.name}}

+ {{#iff actor.type '!=' 'dangerpts'}} +
+
+ + {{actor.system.initiative}} + +
+
+ + {{actor.system.heropts}} + +
+
+ {{else}} +
+
+ + {{actor.system.points}} + +
+
+ {{/iff}} +
+ {{else}} +

{{ localize 'SVNSEA2E.EmptyToolbox' }}

+ {{/each}} \ No newline at end of file