From 6fdf26365ebfc6fba9df297e1443a16b03b05ed9 Mon Sep 17 00:00:00 2001 From: averrin Date: Wed, 29 Jun 2022 19:02:17 +0200 Subject: [PATCH] sequencer beta --- module.json | 10 +- package-lock.json | 63 +++ package.json | 4 + src/constants.js | 120 +++++- src/init.js | 1 + src/modules/helpers.js | 69 +-- src/modules/settings.js | 122 +++--- src/modules/stores.js | 32 ++ src/styles/main.scss | 14 + src/view/MainApplication.js | 137 +++--- src/view/MainUI.svelte | 487 +++++----------------- src/view/components/ActionItem.svelte | 6 +- src/view/components/ActionsTab.svelte | 131 ++++++ src/view/components/ArgInput.svelte | 180 ++++++++ src/view/components/SelectionTab.svelte | 165 ++++++++ src/view/components/SequenceEditor.svelte | 226 ++++++++++ src/view/components/SequencerTab.js | 187 +++++++++ src/view/components/SequencerTab.svelte | 87 ++++ src/view/components/Sortable.svelte | 161 +++++++ src/view/components/TagsBar.svelte | 26 ++ src/view/components/Variable.svelte | 36 ++ 21 files changed, 1712 insertions(+), 552 deletions(-) create mode 100644 src/view/components/ActionsTab.svelte create mode 100644 src/view/components/ArgInput.svelte create mode 100644 src/view/components/SelectionTab.svelte create mode 100644 src/view/components/SequenceEditor.svelte create mode 100644 src/view/components/SequencerTab.js create mode 100644 src/view/components/SequencerTab.svelte create mode 100644 src/view/components/Sortable.svelte create mode 100644 src/view/components/TagsBar.svelte create mode 100644 src/view/components/Variable.svelte diff --git a/module.json b/module.json index 9d9414c..5baf9a6 100644 --- a/module.json +++ b/module.json @@ -14,7 +14,7 @@ "readme": "https://github.com/averrin/director/blob/master/README.md", "bugs": "https://github.com/averrin/director/issues", "changelog": "https://github.com/averrin/director/releases/latest/", - "version": "0.1.0", + "version": "0.2.0", "minimumCoreVersion": "0.8.6", "compatibleCoreVersion": "9", "esmodules": [ @@ -32,10 +32,14 @@ ], "system": [ ], - "dependencies": [{"name": "colorsettings"}, {"name": "tagger"}], + "dependencies": [ + {"name": "tagger"}, + {"name": "sequencer"}, + {"name": "warpgate"} + ], "socket": false, "manifest": "https://github.com/averrin/director/releases/latest/download/module.json", - "download": "https://github.com/averrin/director/releases/download/0.1.0/module.zip", + "download": "https://github.com/averrin/director/releases/download/0.2.0/module.zip", "protected": false, "coreTranslation": false, "library": false diff --git a/package-lock.json b/package-lock.json index 71005ef..dc01358 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,15 @@ "license": "MIT", "dependencies": { "@typhonjs-fvtt/runtime": "^0.0.14", + "class-transformer": "^0.5.1", "consola": "^2.15.3", "daisyui": "^2.17.0", + "reflect-metadata": "^0.1.13", "svelte": "^3.46.0", "svelte-color-picker": "^1.0.7", + "svelte-icons": "^2.1.0", "svelte-select": "^5.0.0-beta.14", + "svelte-sortable": "^0.1.0", "svelte-sortable-list": "^1.1.0", "svelte-tags-input": "^2.9.2", "uuid": "^8.3.2" @@ -2391,6 +2395,11 @@ "node": ">= 6" } }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -5229,6 +5238,11 @@ "node": ">=8.10.0" } }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -5722,6 +5736,11 @@ "sorcery": "bin/index.js" } }, + "node_modules/sortablejs": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz", + "integrity": "sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==" + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -5902,6 +5921,11 @@ "sirv-cli": "^0.4.4" } }, + "node_modules/svelte-icons": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/svelte-icons/-/svelte-icons-2.1.0.tgz", + "integrity": "sha512-rHPQjweEc9fGSnvM0/4gA3pDHwyZyYsC5KhttCZRhSMJfLttJST5Uq0B16Czhw+HQ+HbSOk8kLigMlPs7gZtfg==" + }, "node_modules/svelte-preprocess": { "version": "4.10.7", "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.7.tgz", @@ -5972,6 +5996,17 @@ "resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-5.0.0-beta.14.tgz", "integrity": "sha512-ICs0gjTZDv8yMnUjJY53DYGEV8xVbXa93WIWlczEOujRgEzIMAxAA2m7aIv9GCu7VWh2Pd/7gDJZUF5Dy/FSbw==" }, + "node_modules/svelte-sortable": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/svelte-sortable/-/svelte-sortable-0.1.0.tgz", + "integrity": "sha512-6NFFPJFyaI/vZiA2ceaB5pPG91m3+TkIKx/2HSkucpFkFlFpYTWF3XooR/dPxai++k1Lj3c5VVMtXhfAG/OycA==", + "dependencies": { + "sortablejs": "^1.10.1" + }, + "peerDependencies": { + "svelte": "^3.0.0" + } + }, "node_modules/svelte-sortable-list": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/svelte-sortable-list/-/svelte-sortable-list-1.1.0.tgz", @@ -7932,6 +7967,11 @@ } } }, + "class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + }, "color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -9876,6 +9916,11 @@ "picomatch": "^2.2.1" } }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -10235,6 +10280,11 @@ "sourcemap-codec": "^1.3.0" } }, + "sortablejs": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz", + "integrity": "sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==" + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -10377,6 +10427,11 @@ "sirv-cli": "^0.4.4" } }, + "svelte-icons": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/svelte-icons/-/svelte-icons-2.1.0.tgz", + "integrity": "sha512-rHPQjweEc9fGSnvM0/4gA3pDHwyZyYsC5KhttCZRhSMJfLttJST5Uq0B16Czhw+HQ+HbSOk8kLigMlPs7gZtfg==" + }, "svelte-preprocess": { "version": "4.10.7", "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.7.tgz", @@ -10395,6 +10450,14 @@ "resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-5.0.0-beta.14.tgz", "integrity": "sha512-ICs0gjTZDv8yMnUjJY53DYGEV8xVbXa93WIWlczEOujRgEzIMAxAA2m7aIv9GCu7VWh2Pd/7gDJZUF5Dy/FSbw==" }, + "svelte-sortable": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/svelte-sortable/-/svelte-sortable-0.1.0.tgz", + "integrity": "sha512-6NFFPJFyaI/vZiA2ceaB5pPG91m3+TkIKx/2HSkucpFkFlFpYTWF3XooR/dPxai++k1Lj3c5VVMtXhfAG/OycA==", + "requires": { + "sortablejs": "^1.10.1" + } + }, "svelte-sortable-list": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/svelte-sortable-list/-/svelte-sortable-list-1.1.0.tgz", diff --git a/package.json b/package.json index 247ede7..d17b379 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,15 @@ ], "dependencies": { "@typhonjs-fvtt/runtime": "^0.0.14", + "class-transformer": "^0.5.1", "consola": "^2.15.3", "daisyui": "^2.17.0", + "reflect-metadata": "^0.1.13", "svelte": "^3.46.0", "svelte-color-picker": "^1.0.7", + "svelte-icons": "^2.1.0", "svelte-select": "^5.0.0-beta.14", + "svelte-sortable": "^0.1.0", "svelte-sortable-list": "^1.1.0", "svelte-tags-input": "^2.9.2", "uuid": "^8.3.2" diff --git a/src/constants.js b/src/constants.js index 73eda86..86adeb5 100644 --- a/src/constants.js +++ b/src/constants.js @@ -4,32 +4,110 @@ export const moduleId = "director"; // import { moduleId, SETTINGS } from '../constants.js'; export const SETTINGS = { - GLOBAL_TAGS: "global-tags", - TAG_COLORS: "tag-colors", - SHOW: "show", - UI_SCALE: "ui-scale", - RESOLUTION: "resolution", + GLOBAL_TAGS: "global-tags", + TAG_COLORS: "tag-colors", + SHOW: "show", + UI_SCALE: "ui-scale", + RESOLUTION: "resolution", + SEQUENCES: "sequences", + SHOW_SEQUENCER: "show-sequencer", }; export const HOOKS = [ - 'controlToken', - 'updateToken', - 'updateActor', - 'targetToken', - - 'canvasReady', - 'createToken', - 'deleteToken', - 'deleteActor', - // 'renderTokenActionHUD', + 'controlToken', + 'updateToken', + 'updateActor', + 'targetToken', + + 'canvasReady', + 'createToken', + 'deleteToken', + 'deleteActor', + // 'renderTokenActionHUD', ]; export const actionTypes = [ - {value: 'execute', label: 'Execute trigger', group: 'Active Tiles', require: "matt"}, - {value: 'toggle', label: 'Toggle visibility', group: 'Common'}, - {value: 'hide', label: 'Hide', group: 'Common'}, - {value: 'show', label: 'Show', group: 'Common'}, - {value: 'kill', label: 'Kill', group: 'Tokens'}, - {value: 'revive', label: 'Revive', group: 'Tokens'}, + { id: 'execute', label: 'Execute trigger', group: 'Active Tiles', require: "matt" }, + { id: 'toggle', label: 'Toggle visibility', group: 'Common' }, + { id: 'hide', label: 'Hide', group: 'Common' }, + { id: 'show', label: 'Show', group: 'Common' }, + { id: 'kill', label: 'Kill', group: 'Tokens' }, + { id: 'revive', label: 'Revive', group: 'Tokens' }, +]; + +export const stepSpecs = [ + { id: 'effect', label: 'Effect' }, + { id: 'wait', label: 'Wait', args: [{ type: 'int', label: 'ms' }] }, + { id: 'macro', label: 'Macro', args: [{ type: 'macro', label: 'name' }] }, +]; +export const modifierSpecs = [ + { id: 'file', group: 'effect', args: [{ type: 'effect_file', label: 'file' }], cat: "Required" }, + { id: 'atLocation', group: 'effect', args: [{ type: 'position', label: 'pos' }], cat: "Required" }, + + { id: 'scaleToObject', group: 'effect', args: [{ type: 'float', label: 'scale' }], cat: "Scale" }, + { id: 'scale', group: 'effect', args: [{ type: 'float', label: 'scale' }], cat: "Scale" }, + { id: 'scaleIn', group: 'effect', args: [{ type: 'float', label: 'scale' }, { type: 'int', label: 'ms' }], cat: "Scale" }, // {ease: "easeInOutCubic"}) + { id: 'scaleOut', group: 'effect', args: [{ type: 'float', label: 'scale' }, { type: 'int', label: 'ms' }], cat: "Scale" }, // {ease: "easeInCubic"}) + + { id: 'stretchTo', group: 'effect', args: [{ type: 'position', label: 'pos' }] }, + { id: 'attachTo', group: 'effect', args: [{ type: 'token', label: 'pos' }] }, + { id: 'rotateTowards', group: 'effect', args: [{ type: 'position', label: 'pos' }] }, + { id: 'moveTowards', group: 'effect', args: [{ type: 'position', label: 'pos' }] }, + { id: 'moveSpeed', group: 'effect', args: [{ type: 'int', label: 'speed' }] }, + + { id: 'repeats', group: 'effect', args: [{ type: 'int', label: 'count' }, { type: 'int', label: 'delay min' }, { type: 'int', label: 'delay max' }] }, + { id: 'randomizeMirrorY', group: 'effect', args: [] }, + { id: 'belowTokens', group: 'effect', args: [] }, + { id: 'duration', group: 'effect', args: [{ type: 'int', label: 'ms' }] }, //1500, {ease: "easeOutCubic", delay: 500}) + { id: 'fadeIn', group: 'effect', args: [{ type: 'int', label: 'ms' }] }, //1500, {ease: "easeOutCubic", delay: 500}) + { id: 'fadeOut', group: 'effect', args: [{ type: 'int', label: 'ms' }] }, + { id: 'rotate', group: 'effect', args: [{ type: 'int', label: 'deg' }, { type: 'int', label: 'ms' }] }, // {ease: "easeInOutCubic"}) + { id: 'rotateIn', group: 'effect', args: [{ type: 'int', label: 'deg' }, { type: 'int', label: 'ms' }] }, // {ease: "easeInOutCubic"}) + { id: 'rotateOut', group: 'effect', args: [{ type: 'int', label: 'deg' }, { type: 'int', label: 'ms' }] }, // {ease: "easeInCubic"}) + { id: 'waitUntilFinished', group: 'effect', args: [{ type: 'int', label: 'ms' }] }, + { id: 'async', group: 'effect', args: [] }, + { id: 'locally', group: 'effect', args: [] }, + { id: 'noLoop', group: 'effect', args: [{ type: 'bool', label: 'val' }] }, + { id: 'snapToGrid', group: 'effect', args: [{ type: 'bool', label: 'val' }] }, + { id: 'zeroSpriteRotation', group: 'effect', args: [{ type: 'bool', label: 'val' }] }, + { id: 'persist', group: 'effect', args: [{ type: 'bool', label: 'val' }] }, + { id: 'delay', group: 'effect', args: [{ type: 'int', label: 'ms' }] }, + { id: 'startTime', group: 'effect', args: [{ type: 'int', label: 'ms' }] }, + { id: 'startTimePerc', group: 'effect', args: [{ type: 'float', label: 'val' }] }, + { id: 'endTime', group: 'effect', args: [{ type: 'int', label: 'ms' }] }, + { id: 'endTimePerc', group: 'effect', args: [{ type: 'float', label: 'val' }] }, + { id: 'extraEndDuration', group: 'effect', args: [{ type: 'int', label: 'ms' }] }, + { id: 'timeRange', group: 'effect', args: [{ type: 'int', label: 'ms' }, { type: 'int', label: 'ms' }] }, + +]; + +export const argSpecs = [ + { + id: "position", options: [ + { value: "#controlled.first", label: "First Controlled" }, + { value: "#controlled.last", label: "Last Controlled" }, + { value: "#target.first", label: "First Target" }, + { value: "#target.last", label: "Last Target" }, + { value: "#manual", label: "Manual" }, + ] + }, + { + id: "token", options: [ + { value: "#controlled.first", label: "First Controlled" }, + { value: "#controlled.last", label: "Last Controlled" }, + { value: "#target.first", label: "First Target" }, + { value: "#target.last", label: "Last Target" }, + ] + }, + { + id: "bool", options: [ + { value: true, label: "True" }, + { value: false, label: "False" }, + ] + }, + { id: "effect_file" }, + { id: "int" }, + { id: "float" }, + { id: "macro" }, ]; diff --git a/src/init.js b/src/init.js index 0c8b120..078574f 100644 --- a/src/init.js +++ b/src/init.js @@ -1,4 +1,5 @@ import MainApplication from './view/MainApplication.js'; +import 'reflect-metadata'; import { moduleId, SETTINGS } from "./constants.js"; import { initSettings, setting } from "./modules/settings.js"; diff --git a/src/modules/helpers.js b/src/modules/helpers.js index 3b5c9d2..de70d61 100644 --- a/src/modules/helpers.js +++ b/src/modules/helpers.js @@ -1,54 +1,57 @@ -import {moduleId, SETTINGS} from '../constants.js'; +import { moduleId, SETTINGS } from '../constants.js'; import consola from 'consola/src/browser' +export let setting = key => { + return game.settings.get(moduleId, key); +}; export function hasTrackers() { - const showTracking = game.settings.get(moduleId, SETTINGS.SHOW_TRACKING); - if (!showTracking) return false; - return typeof globalThis.CrashTNT !== 'undefined'; + const showTracking = game.settings.get(moduleId, SETTINGS.SHOW_TRACKING); + if (!showTracking) return false; + return typeof globalThis.CrashTNT !== 'undefined'; } export function matchTrackers(token) { - if (!hasTrackers()) return []; - const trackers = JSON.parse(game.settings.get(moduleId, SETTINGS.TRACKERS)); - if (!trackers) return []; - const matched = []; - const found = globalThis.CrashTNT.getActivitiesForActor(token.document.actor.data.name); - for (const t of trackers) { - if (found.filter(f => f.name === t.name)) matched.push(t); - } - return matched; + if (!hasTrackers()) return []; + const trackers = JSON.parse(game.settings.get(moduleId, SETTINGS.TRACKERS)); + if (!trackers) return []; + const matched = []; + const found = globalThis.CrashTNT.getActivitiesForActor(token.document.actor.data.name); + for (const t of trackers) { + if (found.filter(f => f.name === t.name)) matched.push(t); + } + return matched; } export function hasResourceIcons(token) { - const showRI = game.settings.get(moduleId, SETTINGS.SHOW_RESOURCE_ICONS); - if (!showRI) return false; - const data = token.document.data.flags["resource-icons"]; - if (!data) return false; - if (data.icon1.resource !== '') return true; - if (data.icon2.resource !== '') return true; - if (data.icon3.resource !== '') return true; - return false; + const showRI = game.settings.get(moduleId, SETTINGS.SHOW_RESOURCE_ICONS); + if (!showRI) return false; + const data = token.document.data.flags["resource-icons"]; + if (!data) return false; + if (data.icon1.resource !== '') return true; + if (data.icon2.resource !== '') return true; + if (data.icon3.resource !== '') return true; + return false; } export function isAlive(token) { - return globalThis.getProperty(token?.document?.actor.getRollData(), "attributes.hp.value") > 0; + return globalThis.getProperty(token?.document?.actor.getRollData(), "attributes.hp.value") > 0; } export function isLiving(token) { - return getProperty(token?.document?.actor.getRollData(), "attributes.hp.max") > 0; + return getProperty(token?.document?.actor.getRollData(), "attributes.hp.max") > 0; } export let logger = consola.withTag(moduleId); export function findItems(token, itemsToFind) { - // logger.info(`find items for: ${token.data.name}`, token, itemsToFind); - const items = []; - const i1 = token?.document?.actor?.items.filter(i => itemsToFind.some(itf => itf == i.name || itf == i.id)); - items.push(...i1); - const containers = token?.document?.actor?.items.filter(i => i.items); - for (const c of containers) { - let i2 = c.items.filter(i => itemsToFind.some(itf => itf == i.name || itf == i.id)); - items.push(...i2); - } - return items; + // logger.info(`find items for: ${token.data.name}`, token, itemsToFind); + const items = []; + const i1 = token?.document?.actor?.items.filter(i => itemsToFind.some(itf => itf == i.name || itf == i.id)); + items.push(...i1); + const containers = token?.document?.actor?.items.filter(i => i.items); + for (const c of containers) { + let i2 = c.items.filter(i => itemsToFind.some(itf => itf == i.name || itf == i.id)); + items.push(...i2); + } + return items; } diff --git a/src/modules/settings.js b/src/modules/settings.js index c670b7d..5867f62 100644 --- a/src/modules/settings.js +++ b/src/modules/settings.js @@ -2,60 +2,78 @@ import { moduleId, SETTINGS } from '../constants.js'; import { foundry } from './foundry.js'; export let setting = key => { - return game.settings.get(moduleId, key); + return game.settings.get(moduleId, key); }; const debouncedReload = debounce(() => window.location.reload(), 100); export function initSettings(app) { - game.settings.register(moduleId, SETTINGS.SHOW, { - scope: "client", - config: false, - type: Boolean, - default: false, - }); - - game.settings.register(moduleId, SETTINGS.GLOBAL_TAGS, { - scope: "world", - config: false, - type: Array, - default: '[]', - }); - - game.settings.register(moduleId, SETTINGS.TAG_COLORS, { - scope: "world", - config: false, - type: Object, - default: '{}', - }); - - foundry.settings.register(moduleId, SETTINGS.UI_SCALE, { - name: 'UI scale', - hint: 'If ui are too big or too small for your display. Requires refresh.', - config: true, - type: Number, - default: 1, - onChange: value => { - debouncedReload(); - }, - range: { - min: 0.1, - max: 2, - step: 0.01 - } - }); - - game.settings.register(moduleId, SETTINGS.RESOLUTION, { - name: "Selected image resolution", - hint: "Higher is better quality but slower", - scope: "world", - config: true, - range: { - min: 30, - max: 600, - step: 5, - }, - default: 200, - type: Number, - onChange: debouncedReload - }); + game.settings.register(moduleId, SETTINGS.SHOW, { + scope: "client", + config: false, + type: Boolean, + default: false, + }); + + game.settings.register(moduleId, SETTINGS.GLOBAL_TAGS, { + scope: "world", + config: false, + type: Array, + default: '[]', + }); + + game.settings.register(moduleId, SETTINGS.SEQUENCES, { + scope: "world", + config: false, + type: Array, + default: '[]', + }); + + game.settings.register(moduleId, SETTINGS.TAG_COLORS, { + scope: "world", + config: false, + type: Object, + default: '{}', + }); + + foundry.settings.register(moduleId, SETTINGS.UI_SCALE, { + name: 'UI scale', + hint: 'If ui are too big or too small for your display. Requires refresh.', + config: true, + type: Number, + default: 1, + onChange: value => { + debouncedReload(); + }, + range: { + min: 0.1, + max: 2, + step: 0.01 + } + }); + + foundry.settings.register(moduleId, SETTINGS.SHOW_SEQUENCER, { + name: 'Show Sequencer tab', + hint: 'Early beta! Buggy and lacks of features. Requires refresh.', + config: true, + type: Boolean, + default: false, + onChange: value => { + debouncedReload(); + }, + }); + + game.settings.register(moduleId, SETTINGS.RESOLUTION, { + name: "Selected image resolution", + hint: "Higher is better quality but slower", + scope: "world", + config: true, + range: { + min: 30, + max: 600, + step: 5, + }, + default: 200, + type: Number, + onChange: debouncedReload + }); } diff --git a/src/modules/stores.js b/src/modules/stores.js index 1197697..a406d9a 100644 --- a/src/modules/stores.js +++ b/src/modules/stores.js @@ -1,5 +1,37 @@ +import {moduleId, SETTINGS} from '../constants.js'; import { writable } from 'svelte/store'; +import {DSequence} from '../view/components/SequencerTab.js'; export const tokensStore = writable([]); export const tilesStore = writable([]); export const currentScene = writable(null); + +export const globalTags = writable([]); +export function initGlobalTags() { + globalTags.set(JSON.parse(game.settings.get(moduleId, SETTINGS.GLOBAL_TAGS))); + globalTags.subscribe(async (tags) => { + game.settings.set(moduleId, SETTINGS.GLOBAL_TAGS, JSON.stringify(tags)); + }); +} + +export const tagColors = writable({}); +export function initTagColors() { + tagColors.set(JSON.parse(game.settings.get(moduleId, SETTINGS.TAG_COLORS))); + tagColors.subscribe(async (colors) => { + game.settings.set(moduleId, SETTINGS.TAG_COLORS, JSON.stringify(colors)); + }); +} + +export const sequences = writable([]); +export function initSequences() { + sequences.set(JSON.parse(game.settings.get(moduleId, SETTINGS.SEQUENCES)).map(s => DSequence.fromPlain(s))); + sequences.subscribe(async (seqs) => { + game.settings.set(moduleId, SETTINGS.SEQUENCES, JSON.stringify(seqs)); + }); +} + +export function initStores() { + initGlobalTags(); + initTagColors(); + initSequences(); +} diff --git a/src/styles/main.scss b/src/styles/main.scss index a082fa1..2317b3b 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -12,8 +12,22 @@ .svelte-select { min-width: 200px; + max-width: 600px; border-radius: var(--rounded-btn, 0.5rem) !important; background: #fff !important; + + input, .selection { + font-size: 1rem !important; + } + } + + .ui-input-lg { + border-color: #ddd !important; + height: 40px !important; + } + + .text-base { + font-size: 1rem !important; } .ui-drawer { diff --git a/src/view/MainApplication.js b/src/view/MainApplication.js index 2436509..7f5f653 100644 --- a/src/view/MainApplication.js +++ b/src/view/MainApplication.js @@ -3,71 +3,104 @@ import { SvelteApplication } from "@typhonjs-fvtt/runtime/svelte/application"; import { moduleId, SETTINGS } from "../constants.js"; // import { logger } from "../modules/helpers.js"; import { setting } from "../modules/settings.js"; -import { tilesStore, tokensStore, currentScene } from "../modules/stores.js"; +import { tilesStore, tokensStore, currentScene, initStores, sequences } from "../modules/stores.js"; import MainUI from "./MainUI.svelte"; +const API = {}; + export default class MainApplication extends SvelteApplication { - // #gameSettings = new TJSGameSettings(); + // #gameSettings = new TJSGameSettings(); - #HOOKS = ["controlToken", "updateToken", "controlTile", "updateTile"]; + #HOOKS = ["controlToken", "updateToken", "controlTile", "updateTile"]; - constructor() { - super({ widgetId: "selected" }); - for (const hook of this.#HOOKS) { - globalThis.Hooks.on(hook, this.onSelectionUpdate.bind(this)); - } - globalThis.Hooks.on("closeMainApplication", () => { - globalThis.game.settings.set(moduleId, SETTINGS.SHOW, false); - }); + constructor() { + super({ widgetId: "selected" }); + for (const hook of this.#HOOKS) { + globalThis.Hooks.on(hook, this.onSelectionUpdate.bind(this)); + } + globalThis.Hooks.on("closeMainApplication", () => { + globalThis.game.settings.set(moduleId, SETTINGS.SHOW, false); + }); - Hooks.on("canvasInit", () => { - Hooks.once("renderCombatTracker", this.onSelectionUpdate.bind(this)); - }); - } + Hooks.on("canvasInit", () => { + Hooks.once("renderCombatTracker", this.onSelectionUpdate.bind(this)); + }); + } - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - id: "director", - width: "600", - height: "auto", - resizable: true, - minimizable: true, - zIndex: 95, - title: "Director", + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + id: "director", + width: "600", + height: "auto", + resizable: true, + minimizable: true, + zIndex: 95, + title: "Director", - svelte: { - class: MainUI, - target: document.body, - props: function () { - return {}; - }, - }, - }); - } + svelte: { + class: MainUI, + target: document.body, + props: function() { + return {}; + }, + }, + }); + } - start() {} + start() { + initStores(); + window.Director = API; - toggle() { - if (setting(SETTINGS.SHOW)) { - this.hide(); + API.getSequence = (name, overrides) => { + let seq; + sequences.update(seqs => { + seq = seqs.find(s => s.title == name); + return seqs; + }); + if (!seq) { + logger.error(`Sequence ${name} not found`); + return null; } else { - this.show(); + const s_seq = seq.prepare(overrides); + return s_seq; } - } + }; - async show() { - await this.render(true); - globalThis.game.settings.set(moduleId, SETTINGS.SHOW, true); - } - async hide() { - await this.close(true); - globalThis.game.settings.set(moduleId, SETTINGS.SHOW, false); - } + API.playSequence = (name, overrides) => { + let seq; + sequences.update(seqs => { + seq = seqs.find(s => s.title == name); + return seqs; + }); + if (!seq) { + logger.error(`Sequence ${name} not found`); + } else { + return seq.play(overrides); + } + }; + } + + toggle() { + if (setting(SETTINGS.SHOW)) { + this.hide(); + } else { + this.show(); + } + } + + async show() { + await this.render(true); + globalThis.game.settings.set(moduleId, SETTINGS.SHOW, true); + } + async hide() { + await this.close(true); + globalThis.game.settings.set(moduleId, SETTINGS.SHOW, false); + } - onSelectionUpdate() { - tokensStore.set(canvas.tokens.controlled); - tilesStore.set(canvas.background.controlled); - currentScene.set(canvas.scene); - } + onSelectionUpdate() { + tokensStore.set(canvas.tokens.controlled); + tilesStore.set(canvas.background.controlled); + currentScene.set(canvas.scene); + } } diff --git a/src/view/MainUI.svelte b/src/view/MainUI.svelte index cfa43f1..cd1ac6b 100644 --- a/src/view/MainUI.svelte +++ b/src/view/MainUI.svelte @@ -1,397 +1,108 @@ - - - - - + -
-
- -
- -
- {#each modes as m} - mode = m} class:ui-tab-active={m == mode}>{m} - {/each} -
- - {#if mode == "selection"} - {#if selection.length > 1} -
-
- Mutual tags -
- globalThis.debounce(onTagsSelected(e, -1), 100)} - tags={tagsMutual} - colors={colors} - onTagClick={onTagClick} - /> -
-
-
- -
-
- {/if} - -
-
- {#if selection.length == 0} -
-

Selection is empty

-
- {/if} - {#each selection as tile, i} -
-
- Selected image - -
-
-

- {#if !tile.data.name} - Tile: {tile.id} - {#if tile.data.flags['monks-active-tiles']?.actions?.length > 0} - MATT - {/if} - {:else} - Token: {tile.data.name} - {/if} - {#if true} - {tile.data.width}x{tile.data.height} - {/if} - tile.document.update({hidden: !tile.data.hidden})} - > - {tile.data.hidden ? 'hidden' : 'visible'} - -

- -

- onTagsSelected(e, i)} - tags={tagsSelected[i]} - colors={colors} - onTagClick={onTagClick} - /> -

- -
- - - -
-
-
- {/each} -
-
- {/if} - {#if mode == "actions"} -
-
- -
-
- - - -
-
- {/if} +
+ + + + {#if mode == "selection"} + + {/if} + {#if mode == "actions"} + (mode = "selection")} /> + {/if} + {#if mode == "sequencer"} + + {/if}
diff --git a/src/view/components/ActionItem.svelte b/src/view/components/ActionItem.svelte index d1a95d7..e8a5f17 100644 --- a/src/view/components/ActionItem.svelte +++ b/src/view/components/ActionItem.svelte @@ -2,7 +2,8 @@ import Tags from '../components/Tags.svelte' import Select from 'svelte-select'; import { XIcon } from "@rgossiaux/svelte-heroicons/solid"; - import { moduleId, SETTINGS, actionTypes } from '../../constants.js'; + import { actionTypes } from '../../constants.js'; + import { tagColors } from '../../modules/stores.js'; export let item; @@ -14,7 +15,6 @@ export let onTagClick; export let actionTags; export let autoComplete; - export let colors; const groupBy = (i) => i.group; @@ -37,7 +37,7 @@ placeholder="Tag" {autoComplete} minChars=1 - colors={colors} + colors={$tagColors} onTagClick={onTagClick} on:tags={e => actionTags(e, item.id)} tags={item.tags} diff --git a/src/view/components/ActionsTab.svelte b/src/view/components/ActionsTab.svelte new file mode 100644 index 0000000..aa06739 --- /dev/null +++ b/src/view/components/ActionsTab.svelte @@ -0,0 +1,131 @@ + + +
+
+ +
+
+ + + +
+
diff --git a/src/view/components/ArgInput.svelte b/src/view/components/ArgInput.svelte new file mode 100644 index 0000000..af3151f --- /dev/null +++ b/src/view/components/ArgInput.svelte @@ -0,0 +1,180 @@ + + +