diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..8fdf0fc Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index 4e80e80..af4eff0 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,6 @@ node_modules/ packs/* !packs/_source/ !packs/*.db +.DS_Store .idea/* diff --git a/src/activeLighting.js b/src/activeLighting.js index b42eb28..8033652 100644 --- a/src/activeLighting.js +++ b/src/activeLighting.js @@ -1,7 +1,7 @@ import { PresetConfig } from "./preset-config.js"; import { ATLUpdate } from "./updateManager.js"; -const { deepClone, duplicate, flattenObject, getProperty, hasProperty, mergeObject, setProperty } = foundry.utils; +const { deepClone, duplicate, flattenObject, getProperty, hasProperty, mergeObject, setProperty, getType } = foundry.utils; class ATL { @@ -91,6 +91,15 @@ class ATL { } static async ready() { + //Update Workaround + let presets = await game.settings.get("ATL", "presets") + for (let preset of presets) { + if (!preset.name) { + preset.name = preset.label + delete preset.label + } + } + const newTransferral = game.release.generation >= 11 && !CONFIG.ActiveEffect.legacyTransferral; const getEffects = (actor) => { if (!actor) return []; @@ -204,68 +213,66 @@ class ATL { static async UpdatePresets() { let presets = await game.settings.get("ATL", "presets") - let content = `
` - let presetSelector = new Dialog({ - title: "Preset Selector", + let presetSelector = new foundry.applications.api.DialogV2({ + window: { + title: "Preset Selector" + }, content: ` -
- - ${content} -
`, - content, - buttons: { - one: { - label: "Update", - icon: ``, - callback: (html) => { - let updatePreset = html.find("[name=presets]")[0].value; - let preset = presets.find(p => p.id === updatePreset) - new PresetConfig(preset).render(true); - } - }, - two: { - label: "Create Copy", - icon: ``, - callback: (html) => { - let updatePreset = html.find("[name=presets]")[0].value; - let preset = presets.find(p => p.id === updatePreset) - // copy and remove ID so it's created as new - preset = deepClone(preset); - delete preset.id; - new PresetConfig(preset).render(true); - } - }, - three: { - label: "Delete", - icon: ``, - callback: (html) => { - let updatePreset = html.find("[name=presets]")[0].value; - let index = presets.findIndex(p => p.id === updatePreset); - new Dialog({ - title: "Conformation", - content: `Are you sure you want to remove this preset`, - buttons: { - one: { - label: "Confirm", - icon: ``, - callback: () => { - presets.splice(index, 1); - game.settings.set("ATL", "presets", presets); - } - }, - two: { - label: "Return", - icon: ``, - callback: () => presetSelector.render(true) - } - } - }).render(true) +
+ + +
`, + buttons: [{ + action: "update", + label: "Update", + icon: ``, + }, + { + action: "copy", + label: "Create Copy", + icon: ``, + }, + { + action: "delete", + label: "Delete", + icon: ``, + }, + { + action: "new", + label: "Add New", + icon: ``, + }], + submit: async result => { + if (result === "update") { + let updatePreset = document.getElementById("presets").value + let preset = presets.find(p => p.id === updatePreset) + + new PresetConfig({}, preset).render(true); + } + else if (result === "copy") { + let updatePreset = document.getElementById("presets").value + let preset = presets.find(p => p.id === updatePreset) + preset = deepClone(preset); + delete preset.id; + new PresetConfig({}, preset).render(true); + } + else if (result === "delete") { + let preset = document.getElementById("presets").value + let index = presets.findIndex(p => p.id === preset); + const proceed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: "Conformation" + }, + content: `Are you sure you want to remove this preset`, + }) + if (proceed) { + presets.splice(index, 1); + game.settings.set("ATL", "presets", presets); } - }, - four: { - label: "Add New", - icon: ``, - callback: () => new PresetConfig().render(true) + //else presetSelector.render(true); + } + else if (result === "new") { + new PresetConfig().render(true); } } }); @@ -340,6 +347,7 @@ class ATL { console.log("ATE | apply preset", change.value, preset); Object.entries(preset) .forEach(([key, value]) => { + if (key === "tokenName") key = "name" //workaround for DAE const originalValue = getProperty(originals, key); applyOverride(key, value, originalValue); }); diff --git a/src/preset-config.js b/src/preset-config.js index c2a9f65..37b6bf5 100644 --- a/src/preset-config.js +++ b/src/preset-config.js @@ -1,20 +1,21 @@ +const { HandlebarsApplicationMixin, ApplicationV2, DocumentSheetV2, Base } = foundry.applications.api; /** * The Application used for defining a preset configuration that can be used by the `ATL.preset` * active effect key. It can handle updating an existing preset as well as creating a new one. */ -export class PresetConfig extends FormApplication { +export class PresetConfig extends HandlebarsApplicationMixin(ApplicationV2) { /** - * Create a new application to add/edit a preset. + * Create a new application to add/edit a preset. + * @param {ApplicationConfiguration} options Options used to configure the Application instance * @param {Object} object The ATL preset, or `undefined` if creating a new one from scratch - * @param {FormApplicationOptions} options Application configuration options */ - constructor(object = {}, options = {}) { - super(object, options); + constructor(options, object = {}) { + super(options); /** * The token change preset */ - this.preset = this.object; + this.preset = object; /** * Whether this app is creating a new preset or not @@ -27,29 +28,183 @@ export class PresetConfig extends FormApplication { this.fieldsChanged = []; } - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["sheet", "preset-config"], + /** @inheritDoc */ + static DEFAULT_OPTIONS = { + classes: ["preset-config"], + tag: "form", + position: { + width: 560 + }, + window: { title: "ATL Light Editor", - template: "modules/ATL/templates/preset-config.hbs", - width: 480, - height: "auto", + icon: "fas fa-plus-circle", + contentClasses: ["standard-form"], + }, + form: { + handler: PresetConfig.#onSubmit, + //submitOnChange: false, + closeOnSubmit: true + }, + actions: { + addDetectionMode: PresetConfig.#onAddDetectionMode, + removeDetectionMode: PresetConfig.#onRemoveDetectionMode + } + } + + /** @override */ + static PARTS = { + header: { + template: "modules/ATL/templates/header.hbs", + }, + tabs: { + template: "templates/generic/tab-navigation.hbs" + }, + appearance: { + template: "modules/ATL/templates/appearance.hbs", scrollable: [""] + }, + identity: { + template: "modules/ATL/templates/identity.hbs", scrollable: [""] + }, + vision: { + template: "modules/ATL/templates/vision.hbs", scrollable: [""] + }, + light: { + template: "modules/ATL/templates/light.hbs", scrollable: [""] + }, + //resources: { + // template: "modules/ATL/templates/resources.hbs", scrollable: [""] + //}, + footer: { + template: "templates/generic/form-footer.hbs", + }, + }; + + /** @override */ + static TABS = { + sheet: { tabs: [ - { - navSelector: '.tabs[data-group="main"]', - contentSelector: "form", - initial: "appearance", - }, - { - navSelector: '.tabs[data-group="light"]', - contentSelector: '.tab[data-tab="light"]', - initial: "basic", - }, + { id: "identity", icon: "fa-solid fa-memo-pad" }, + { id: "appearance", icon: "fa-solid fa-square-user" }, + { id: "vision", icon: "fa-solid fa-eye" }, + { id: "light", icon: "fa-solid fa-lightbulb" } + //{ id: "resources", icon: "fa-solid fa-heart" } ], - closeOnSubmit: true, - }); + initial: "appearance", + labelPrefix: "TOKEN.TABS" + } + }; + + /** + * Localized Token Display Modes + * @returns {Record} + */ + static get DISPLAY_MODES() { + PresetConfig.#DISPLAY_MODES ??= Object.entries(CONST.TOKEN_DISPLAY_MODES).reduce((modes, [key, value]) => { + modes[value] = game.i18n.localize(`TOKEN.DISPLAY_${key}`); + return modes; + }, {}); + return PresetConfig.#DISPLAY_MODES; + } + + static #DISPLAY_MODES; + + /** + * Localized Token Dispositions + * @returns {Record} + */ + static get TOKEN_DISPOSITIONS() { + PresetConfig.#TOKEN_DISPOSITIONS ??= Object.entries(CONST.TOKEN_DISPOSITIONS) + .reduce((dispositions, [key, value]) => { + dispositions[value] = game.i18n.localize(`TOKEN.DISPOSITION.${key}`); + return dispositions; + }, {}); + return PresetConfig.#TOKEN_DISPOSITIONS; + } + + static #TOKEN_DISPOSITIONS; + + /** + * Localized Token Turn Marker modes + * @returns {Record} + */ + static get TURN_MARKER_MODES() { + PresetConfig.#TURN_MARKER_MODES ??= Object.entries(CONST.TOKEN_TURN_MARKER_MODES) + .reduce((modes, [key, value]) => { + modes[value] = game.i18n.localize(`TOKEN.TURNMARKER.MODES.${key}`); + return modes; + }, {}); + return PresetConfig.#TURN_MARKER_MODES; + } + + static #TURN_MARKER_MODES; + + /** + * Localized Token Shapes + * @returns {Record} + */ + static get TOKEN_SHAPES() { + PresetConfig.#TOKEN_SHAPES ??= Object.entries(CONST.TOKEN_SHAPES) + .reduce((shapes, [key, value]) => { + shapes[value] = game.i18n.localize(`TOKEN.SHAPES.${key}.label`); + return shapes; + }, {}); + return PresetConfig.#TOKEN_SHAPES; + } + + static #TOKEN_SHAPES; + /* -------------------------------------------- */ + /** + * Maintain a copy of the original to show a real-time preview of changes. + * @type {TokenDocument|PrototypeToken|null} + * @protected + */ + _preview = null; + + /* -------------------------------------------- */ + + /** + * Process form submission for the sheet + * @this {PresetConfig} The handler is called with the application as its bound scope + * @param {SubmitEvent} event The originating form submission event + * @param {HTMLFormElement} form The form element that was submitted + * @param {FormDataExtended} formData Processed data for the submitted form + * @returns {Promise} + */ + static async #onSubmit(event, form, formData) { + console.log("ATL |", "_updateObject called with formData:", formData.object); + // Mirror token scale + if ("scale" in formData.object) { + formData.object["texture.scaleX"] = formData.object.scale * (formData.object.mirrorX ? -1 : 1); + formData.object["texture.scaleY"] = formData.object.scale * (formData.object.mirrorY ? -1 : 1); + } + + if (this.fieldsChanged.includes("scale" || "mirrorX" || "mirrorY")) this.fieldsChanged.push("texture.scaleX", "texture.scaleY"); + for (const key of ["scale", "mirrorX", "mirrorY"]) delete formData.object[key]; + + // Set default name if creating a new preset with no name + if (this.newMode && !formData.object.name) { + const presets = game.settings.get("ATL", "presets"); + const count = presets?.length; + formData.object.name = `New Preset (${count + 1})`; + this.fieldsChanged.push("name") + } + + // Remove name change if updating a preset and trying to clear the name + if (!this.newMode && "name" in formData.object && !formData.object.name) delete formData.object.name; + + // apply the changes to the original preset + Object.entries(formData.object) + .filter(([k, _]) => this.fieldsChanged.includes(k)) + .forEach(([k, v]) => { + if (v === "" || v === null) this._clearProperty(this.preset, k); + else foundry.utils.setProperty(this.preset, k, v); + }); + console.log("updated preset:", this.preset); + + PresetConfig.savePreset(this.preset); } + static savePreset(preset) { // put all the presets into a collection const collection = new Collection(); @@ -59,23 +214,28 @@ export class PresetConfig extends FormApplication { // add or update in collection if (!preset.id) preset.id = foundry.utils.randomID(); collection.set(preset.id, preset); - // save collection presets = collection.toJSON(); game.settings.set("ATL", "presets", presets); } - getData(options) { - const gridUnits = game.system.gridUnits; + /* -------------------------------------------- */ + + /** @inheritDoc */ + async _prepareContext(options) { + const context = await super._prepareContext(options); // prepare Preset data - const preset = foundry.utils.deepClone(this.object); + const preset = foundry.utils.deepClone(this.preset); - return { + return Object.assign(context, { + rootId: this.id, object: preset, - gridUnits: gridUnits || game.i18n.localize("GridUnits"), - colorationTechniques: AdaptiveLightingShader.SHADER_TECHNIQUES, + gridUnits: game.i18n.localize("GridUnits"), + displayModes: PresetConfig.DISPLAY_MODES, visionModes: Object.values(CONFIG.Canvas.visionModes).filter((f) => f.tokenConfig), + detectionModes: Object.values(CONFIG.Canvas.detectionModes).filter(f => f.tokenConfig), + preparedDetectionModes: this.preset?.detectionModes, lightAnimations: Object.entries(CONFIG.Canvas.lightAnimations).reduce( (obj, e) => { obj[e[0]] = game.i18n.localize(e[1].label); @@ -83,59 +243,144 @@ export class PresetConfig extends FormApplication { }, { "": game.i18n.localize("None") } ), - scale: Math.abs(this.object.texture?.scaleX || 1), - }; + shapes: PresetConfig.TOKEN_SHAPES, + colorationTechniques: foundry.canvas.rendering.shaders.AdaptiveLightingShader.SHADER_TECHNIQUES, + scale: (Math.abs(this.preset.texture?.scaleX) || 1), + mirrorX: this.preset.texture?.scaleX < 0, + mirrorY: this.preset.texture?.scaleY < 0, + textureFitModes: CONST.TEXTURE_DATA_FIT_MODES.reduce((obj, fit) => { + obj[fit] = game.i18n.localize(`TEXTURE_DATA.FIT.${fit}`); + return obj; + }, {}), + movementActions: Object.entries(CONFIG.Token.movement.actions).reduce( + (choices, [action, { label, canSelect }]) => { + if (canSelect(this.token)) choices[action] = label; + return choices; + }, {}), + dispositions: PresetConfig.TOKEN_DISPOSITIONS, + buttons: [ + { type: "submit", icon: "fa-solid fa-save", label: "SETTINGS.Save" }, + ] + }); } + /* -------------------------------------------- */ - _getSubmitData(updateData = {}) { - const formData = super._getSubmitData(updateData); + /** @inheritDoc */ + async _preFirstRender(context, options) { + await super._preFirstRender(context, options); + } - // Mirror token scale - if ("scale" in formData) { - formData["texture.scaleX"] = formData.scale * (formData.mirrorX ? -1 : 1); - formData["texture.scaleY"] = formData.scale * (formData.mirrorY ? -1 : 1); - } - ["scale", "mirrorX", "mirrorY"].forEach((k) => delete formData[k]); - if (this.fieldsChanged.includes("scale")) this.fieldsChanged.push("texture.scaleX", "texture.scaleY"); + /* -------------------------------------------- */ - // Set default name if creating a new preset with no name - if (this.newMode && !formData.name) { - const presets = game.settings.get("ATL", "presets"); - const count = presets?.length; - formData.name = `New Preset (${count + 1})`; - } + /** + * Mimic changes to the Token document as if they were true document updates. + * @param {object} [changes] The changes to preview. + * @returns {void} + * @protected + */ + _previewChanges(changes) { + if (!changes || !this._preview) return; + const deletions = { "-=actorId": null, "-=actorLink": null }; + const mergeOptions = { inplace: false, performDeletions: true }; + this._preview.updateSource(mergeObject(changes, deletions, mergeOptions)); + } - // Remove name change if updating a preset and trying to clear the name - if (!this.newMode && "name" in formData && !formData.name) delete formData.name; + /* -------------------------------------------- */ + + /** @inheritDoc */ + async _preparePartContext(partId, context, options) { + context = await super._preparePartContext(partId, context, options); - return formData; + const tab = context.tabs[partId]; + if (tab) { + context.tab = tab; + } + return context; } - async _onChangeInput(event) { - super._onChangeInput(event); + /* -------------------------------------------- */ + + + /** @inheritDoc */ + _onChangeForm(formConfig, event) { + super._onChangeForm(formConfig, event); // save the field's name that was changed const el = event.target; if (el.name) this.fieldsChanged.push(el.name); // colorPicker has matching name in the dataset else if (el.dataset.edit) this.fieldsChanged.push(el.dataset.edit); + console.log(this.fieldsChanged) } - async _updateObject(event, formData) { - console.log("ATL |", "_updateObject called with formData:", formData); - // apply the changes to the original preset - Object.entries(formData) - .filter(([k, _]) => this.fieldsChanged.includes(k)) - .forEach(([k, v]) => { - if (v === "" || v === null) this._clearProperty(this.preset, k); - else foundry.utils.setProperty(this.preset, k, v); - }); - console.log("updated preset:", this.preset); + /* -------------------------------------------- */ - PresetConfig.savePreset(this.preset); + /** + * Add a new detection mode to the Token preview. + * @this {PresetConfig} + * @type {ApplicationClickAction} + */ + static async #onAddDetectionMode() { + const formData = new FormDataExtended(this.form); + const modes = Object.values(this._processFormData(event, this.form, formData).detectionModes ?? {}); + modes.push({ id: "", range: 0, enabled: true }); + this._previewChanges({ detectionModes: modes }); + await this.render({ parts: ["vision"], resetPreview: false }); + } + + /* -------------------------------------------- */ + + /** + * Remove a detection mode from the Token preview. + * @this {PresetConfig} + * @type {ApplicationClickAction} + */ + static async #onRemoveDetectionMode(_event, target) { + const formData = new FormDataExtended(this.form); + const modes = Object.values(this._processFormData(event, this.form, formData).detectionModes ?? {}); + const index = Number(target.closest("[data-index]")?.dataset.index); + modes.splice(index, 1); + this._previewChanges({ detectionModes: modes }); + await this.render({ parts: ["vision"], resetPreview: false }); + + } + + /** @inheritDoc */ + _processFormData(event, form, formData) { + //const submitData = super._processFormData(event, form, formData); + const submitData = foundry.utils.expandObject(formData.object); + submitData.detectionModes ??= []; // Clear detection modes array + this._processChanges(submitData); + return submitData; + } + + /** + * Process several fields from form submission data into proper model changes. + * @param {object} submitData Form submission data passed through {@link foundry.applications.ux.FormDataExtended} + * @protected + */ + _processChanges(submitData) { + // Convert scale and mirror data from the form submission to TextureData changes + // if (typeof submitData.scale === "number") { + // submitData.texture.scaleX = submitData.scale * (submitData.mirrorX ? -1 : 1); + // submitData.texture.scaleY = submitData.scale * (submitData.mirrorY ? -1 : 1); + // } + // for (const key of ["scale", "mirrorX", "mirrorY"]) delete submitData[key]; + + // // Process token ring effects from the form submission + // if (Array.isArray(submitData.ring?.effects)) { + // const TRE = CONFIG.Token.ring.ringClass.effects; + // let effects = submitData.ring.enabled ? TRE.ENABLED : TRE.DISABLED; + // for (const effectName of submitData.ring.effects) { + // const v = TRE[effectName] ?? 0; + // effects |= v; + // } + // submitData.ring.effects = effects; + // } } + _clearProperty(object, key) { let target = object; let cleared = false; @@ -155,7 +400,7 @@ export class PresetConfig extends FormApplication { // recursivly call to remove empty objects if (parts) { const remainingKey = parts.join("."); - if (object[remainingKey] && isEmpty(object[remainingKey])) + if (object[remainingKey] && foundry.utils.isEmpty(object[remainingKey])) this._clearProperty(object, remainingKey); } } @@ -163,4 +408,5 @@ export class PresetConfig extends FormApplication { // Return changed status return cleared; } -} + +} diff --git a/src/updateManager.js b/src/updateManager.js index 47f6bc1..51477af 100644 --- a/src/updateManager.js +++ b/src/updateManager.js @@ -64,7 +64,7 @@ export class ATLUpdate { if (change.key.includes("ATL")) { changeFound = true; updates.push(this.v9UpdateEffect(duplicate(change))) - } + } else updates.push(change); } if (changeFound) { @@ -209,16 +209,16 @@ export class ATLUpdate { return newData } - static async flagBuster(actor){ + static async flagBuster(actor) { console.warn(`Updating ${actor.name}`) let flag = actor.getFlag("ATL", "originals") - if(!flag) return ui.notifications.notify(`No Flag for ${actor.name}`) - let updates = mergeObject(actor.data.token, flag, {inplace: false}) - await actor.update({token : updates}) + if (!flag) return ui.notifications.notify(`No Flag for ${actor.name}`) + let updates = mergeObject(actor.data.token, flag, { inplace: false }) + await actor.update({ token: updates }) } - static async massFlagUpdate(){ - for(let actor of game.actors){ + static async massFlagUpdate() { + for (let actor of game.actors) { await this.flagBuster(actor) } } @@ -351,7 +351,7 @@ export class ATLUpdate { if (brightness !== 0) changes.push({ key: "ATL.sight.brightness", value: brightness, mode, priority }); } - + if (changeFound) return changes; } } diff --git a/templates/appearance.hbs b/templates/appearance.hbs new file mode 100644 index 0000000..97358e4 --- /dev/null +++ b/templates/appearance.hbs @@ -0,0 +1,148 @@ +
+
+ +
+ + + +
+
+ +
+ +
+ + + + +
+
+ + {{!--
+ +
+ +
+

{{localize "TOKEN.FIELDS.shape.hint"}}

+
--}} +
+ +
+ +
+

{{localize "TOKEN.FIELDS.texture.fit.hint"}}

+
+ +
+ +
+ + + + +
+

{{localize "TOKEN.AnchorHint"}}

+
+ +
+ +
+ +
+
+ +
+ +
+ + +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+

{{localize "TOKEN.FIELDS.lockRotation.hint"}}

+
+ +
+ {{localize "TOKEN.RING.SHEET.legend"}} +
+ +
+ +
+
+ +
+ +
+ +
+

{{localize "TOKEN.FIELDS.ring.colors.ring.hint"}}

+
+ +
+ +
+ +
+

{{localize "TOKEN.FIELDS.ring.colors.background.hint"}}

+
+ +
+ +
+ + + +
+

{{localize "TOKEN.FIELDS.ring.subject.texture.hint"}}

+
+ +
+ +
+ +
+

{{localize "TOKEN.FIELDS.ring.subject.scale.hint"}}

+
+ {{!--
+ +
+ {{formGroup fields.ring.fields.effects value=source.ring.effects input=ringEffectsInput stacked=true + rootId=rootId}} +
+
--}} +
+
\ No newline at end of file diff --git a/templates/header.hbs b/templates/header.hbs new file mode 100644 index 0000000..f34b6c6 --- /dev/null +++ b/templates/header.hbs @@ -0,0 +1,6 @@ +
+ +
+ +
+
\ No newline at end of file diff --git a/templates/identity.hbs b/templates/identity.hbs new file mode 100644 index 0000000..20440f5 --- /dev/null +++ b/templates/identity.hbs @@ -0,0 +1,63 @@ +
+
+ +
+ +
+
+
+ +
+ + {{numberInput fields.x name="x" value="{{object.x}}" id=(concat rootId "-x") + placeholder=(localize "Pixels")}} + + {{numberInput fields.y name="y" value="{{object.y}}" id=(concat rootId "-y") + placeholder=(localize "Pixels")}} +
+
+
+ +
+ {{numberInput fields.elevation name="elevation" value="{{object.elevation}}" placeholder="0" units=gridUnits + step="any"}} +
+
+
+ +
+ {{numberInput fields.sort name="sort" value="{{object.sort}}" placeholder="0"}} +
+
+
+ +
+ {{numberInput fields.rotation name="rotation" value="{{object.rotation}}" placeholder="0"}} +
+
+
+ +
+ +
+
+
+ +
+ {{numberInput fields.occludable.fields.radius name="occludable.radius" value="{{object.occludable.radius}}" + placeholder="0" units=gridUnits + step="any"}} +
+

{{localize "TOKEN.FIELDS.occludable.radius.hint"}}

+
+
+ +
+ +
+
+
\ No newline at end of file diff --git a/templates/light.hbs b/templates/light.hbs new file mode 100644 index 0000000..3ca865f --- /dev/null +++ b/templates/light.hbs @@ -0,0 +1,150 @@ +
+
+ {{localize "TOKEN.SECTIONS.basic"}} +
+ +
+ + + + +
+

{{localize "AMBIENT_LIGHT.LABELS.radiusHint"}}

+
+
+ +
+ +
+

{{localize "LIGHT.FIELDS.angle.hint"}}

+
+
+ +
+ +
+

{{localize "LIGHT.FIELDS.color.hint"}}

+
+
+ +
+ +
+

{{ localize 'LIGHT.FIELDS.alpha.hint' }}

+
+
+ +
+ +
+

{{localize "LIGHT.FIELDS.priority.hint"}}

+
+
+ +
+ +
+

{{localize "LIGHT.FIELDS.negative.hint"}}

+
+
+ +
+ {{localize "TOKEN.SECTIONS.animation"}} +
+ +
+ +
+
+ {{!-- {{formGroup lightFields.animation.fields.type value=source.light.animation.type + choices=lightAnimations labelAttr="label" blank="None" sort=true rootId=rootId localize=true}} --}} +
+ +
+ +
+

{{localize "LIGHT.FIELDS.animation.speed.hint"}}

+
+
+ +
+ +
+
+
+ +
+ +
+

{{localize "LIGHT.FIELDS.animation.intensity.hint"}}

+
+
+ +
+ {{localize "TOKEN.SECTIONS.advanced"}} +
+ +
+ +
+

{{ localize 'LIGHT.FIELDS.coloration.hint' }}

+
+ +
+ +
+ +
+

{{ localize 'LIGHT.FIELDS.luminosity.hint' }}

+
+ +
+ +
+ +
+

{{ localize 'LIGHT.FIELDS.attenuation.hint' }}

+
+ +
+ +
+ +
+

{{ localize 'LIGHT.FIELDS.saturation.hint' }}

+
+ +
+ +
+ +
+

{{ localize 'LIGHT.FIELDS.contrast.hint' }}

+
+ +
+ +
+ +
+

{{ localize 'LIGHT.FIELDS.shadows.hint' }}

+
+
+
\ No newline at end of file diff --git a/templates/resources.hbs b/templates/resources.hbs new file mode 100644 index 0000000..f44fe2a --- /dev/null +++ b/templates/resources.hbs @@ -0,0 +1,10 @@ +
+ {{!--
+ {{localize "TOKEN.TURNMARKER.SHEET.legend"}} + {{formGroup fields.turnMarker.fields.mode value=source.turnMarker.mode choices=turnMarkerModes}} + {{formGroup fields.turnMarker.fields.animation value=source.turnMarker.animation options=turnMarkerAnimations + blank=""}} + {{formGroup fields.turnMarker.fields.src value=source.turnMarker.src}} + {{formGroup fields.turnMarker.fields.disposition value=source.turnMarker.disposition}} +
--}} +
diff --git a/templates/vision.hbs b/templates/vision.hbs new file mode 100644 index 0000000..8383384 --- /dev/null +++ b/templates/vision.hbs @@ -0,0 +1,97 @@ +
+
+ {{localize "TOKEN.SightHeaderBasic"}} + +
+ +
+ +
+

{{localize "TOKEN.FIELDS.sight.range.hint"}}

+
+
+ +
+ {{numberInput object.sight.angle name="sight.angle" placeholder=(localize "Degrees")}} +
+

{{ localize 'TOKEN.FIELDS.sight.angle.hint' }}

+
+
+ +
+ +
+

{{ localize 'TOKEN.FIELDS.sight.visionMode.hint' }}

+
+
+ +
+ {{localize "TOKEN.SightHeaderDetection"}} +
+
{{localize "TOKEN.DetectionMode"}}
+
{{localize "TOKEN.DetectionRange"}} ({{gridUnits}})
+
{{localize "TOKEN.DetectionEnabled"}}
+
+ +
+
+ + {{#each preparedDetectionModes as |mode|}} +
+
+ +
+
+ +
+
+ +
+
+
+ {{/each}} + + {{!-- {{#each sourceDetectionModes as |mode i|}} +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ {{/each}} --}} +
+ +{{!--
+ {{localize "TOKEN.SightHeaderAdvanced"}} + {{formGroup sightFields.color value=source.sight.color rootId=rootId}} + {{formGroup sightFields.attenuation value=source.sight.attenuation step=0.05 rootId=rootId}} + {{formGroup sightFields.brightness value=source.sight.brightness step=0.05 rootId=rootId}} + {{formGroup sightFields.saturation value=source.sight.saturation step=0.05 rootId=rootId}} + {{formGroup sightFields.contrast value=source.sight.contrast step=0.05 rootId=rootId}} +
--}} +
\ No newline at end of file