diff --git a/README.md b/README.md index 1f4bacf..92478fc 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,19 @@ +# Token Magic FX - Update v0.4.4-alpha (Mess edition PART I) + +*News :* +- Integration of Mess Moerill's Supersuit(e) (for templates) part I : + - Video support in templates : + - You can set webm, mp4, etc. as video texture. + - You can define videos in the automatic spell templates options (for dd5 only). + - Texture autoresize : + - The choosen texture is resized to fit the template. +- TMFX now supports the Spanish language! + +Many thanks to Lozalojo (Spanish translation) and Moerill (video) for this release! + +*Fixed Issues :* +- Template effect tint was badly formatted during an automatic template creation. + # Token Magic FX - Update v0.4.3-alpha *News :* diff --git a/tokenmagic/lang/en.json b/tokenmagic/lang/en.json index 622d290..7543472 100644 --- a/tokenmagic/lang/en.json +++ b/tokenmagic/lang/en.json @@ -31,6 +31,8 @@ "TMFX.settings.disableAnimations.hint": "Check this option if you want to disable FX animations. Requires a foundry reload.", "TMFX.settings.disableCaching.name": "Disable filter caching on startup", "TMFX.settings.disableCaching.hint": "Check this option if you want to disable filter caching on foundry startup.", + "TMFX.settings.disableVideo.name": "Disable video support in templates", + "TMFX.settings.disableVideo.hint": "Check this option if you want to disable video support and texture autoresize in templates.", "TMFX.settings.autoTemplateEnabled.name": "Enable automatic template effects", "TMFX.settings.autoTemplateEnabled.hint": "Disabling this option will disable all automatic template effects. NOTE: Only affects newly placed templates.", "TMFX.settings.defaultTemplateOnHover.name": "Default template grid on hover", diff --git a/tokenmagic/lang/fr.json b/tokenmagic/lang/fr.json index 1634dbe..f23662a 100644 --- a/tokenmagic/lang/fr.json +++ b/tokenmagic/lang/fr.json @@ -19,6 +19,8 @@ "TMFX.template.tint": "Teinte de l'Effet:", "TMFX.settings.disableCaching.name": "Désactiver la mise en cache des filtres", "TMFX.settings.disableCaching.hint": "Cochez cette option pour désactiver la mise en cache des filtres au démarrage de Foundry.", + "TMFX.settings.disableVideo.name": "Désactiver la prise en charge des vidéos", + "TMFX.settings.disableVideo.hint": "Cochez cette option si vous souhaitez désactiver la prise en charge des vidéos et le recadrage automatique des textures dans les templates.", "TMFX.settings.autoTemplateEnabled.name": "Activation automatique des effets", "TMFX.settings.autoTemplateEnabled.hint": "Décocher cette option désactivera tous les effets automatiques sur les templates. REMARQUE: affecte uniquement les templates nouvellement placés.", "TMFX.settings.defaultTemplateOnHover.name": "Grille sur demande", diff --git a/tokenmagic/module.json b/tokenmagic/module.json index ec92732..cf5dfd1 100644 --- a/tokenmagic/module.json +++ b/tokenmagic/module.json @@ -2,7 +2,7 @@ "name": "tokenmagic", "title": "Token Magic FX", "description": "
Add special effects and animations on your tokens, tiles, drawings and templates.
", - "version": "0.4.3", + "version": "0.4.4", "compatibleCoreVersion": "0.6.6", "minimumCoreVersion": "0.6.0", "author": "SecretFire and sPOiDar (Auto-Templates)", @@ -15,6 +15,9 @@ }, { "name": "Zimm" + }, + { + "name": "Lozalojo" } ], "scripts": [ @@ -100,5 +103,5 @@ "socket": true, "url": "https://github.com/Feu-Secret/Tokenmagic", "manifest": "https://raw.githubusercontent.com/Feu-Secret/Tokenmagic/master/tokenmagic/module.json", - "download": "https://github.com/Feu-Secret/Tokenmagic/releases/download/v0.4.3-alpha/Tokenmagic.zip" + "download": "https://github.com/Feu-Secret/Tokenmagic/releases/download/v0.4.4-alpha/Tokenmagic.zip" } diff --git a/tokenmagic/module/proto/PlaceableObjectProto.js b/tokenmagic/module/proto/PlaceableObjectProto.js index d492b26..ca9d0e0 100644 --- a/tokenmagic/module/proto/PlaceableObjectProto.js +++ b/tokenmagic/module/proto/PlaceableObjectProto.js @@ -1,4 +1,4 @@ -import { PlaceableType, Magic, broadcast, SocketAction, mustBroadCast, isZOrderConfig } from "../tokenmagic.js"; +import { PlaceableType, Magic, broadcast, SocketAction, mustBroadCast, isZOrderConfig, isVideoDisabled } from "../tokenmagic.js"; import { emptyPreset, autoMinRank } from '../constants.js'; export var gMaxRank = autoMinRank; @@ -204,14 +204,118 @@ MeasuredTemplate.prototype.update = (function () { }; })(); -export function enableMeasuredTemplatePrototypeRefresh() { - MeasuredTemplate.prototype.refresh = (function () { - const cachedMTR = MeasuredTemplate.prototype.refresh; - return function () { - if (this.template && !this.template._destroyed) { +MeasuredTemplate.prototype.refresh = (function () { + const cachedMTR = MeasuredTemplate.prototype.refresh; + return function () { + if (this.template && !this.template._destroyed) { + if (isVideoDisabled()) { return cachedMTR.apply(this); + } else { + // INTEGRATION FROM MESS + // THANKS TO MOERILL !! + let d = canvas.dimensions; + this.position.set(this.data.x, this.data.y); + + // Extract and prepare data + let { direction, distance, angle, width } = this.data; + distance *= (d.size / d.distance); + width *= (d.size / d.distance); + direction = toRadians(direction); + + // Create ray and bounding rectangle + this.ray = Ray.fromAngle(this.data.x, this.data.y, direction, distance); + + // Get the Template shape + switch (this.data.t) { + case "circle": + this.shape = this._getCircleShape(distance); + break; + case "cone": + this.shape = this._getConeShape(direction, angle, distance); + break; + case "rect": + this.shape = this._getRectShape(direction, distance); + break; + case "ray": + this.shape = this._getRayShape(direction, distance, width); + } + + // Draw the Template outline + this.template.clear() + .lineStyle(this._borderThickness, this.borderColor, 0.75) + .beginFill(0x000000, 0.0); + + // Fill Color or Texture + if (this.texture) { + let mat = PIXI.Matrix.IDENTITY; + // rectangle + if (this.shape.width && this.shape.height) + mat.scale(this.shape.width / this.texture.width, this.shape.height / this.texture.height); + else if (this.shape.radius) { + mat.scale(this.shape.radius * 2 / this.texture.height, this.shape.radius * 2 / this.texture.width) + // Circle center is texture start... + mat.translate(-this.shape.radius, -this.shape.radius); + } else if (this.data.t === "ray") { + const d = canvas.dimensions, + height = this.data.width * d.size / d.distance, + width = this.data.distance * d.size / d.distance; + mat.scale(width / this.texture.width, height / this.texture.height); + mat.translate(0, -height * 0.5); + + mat.rotate(toRadians(this.data.direction)); + } else {// cone + const d = canvas.dimensions; + + // Extract and prepare data + let { direction, distance, angle } = this.data; + distance *= (d.size / d.distance); + direction = toRadians(direction); + const width = this.data.distance * d.size / d.distance; + + const angles = [(angle / -2), (angle / 2)]; + distance = distance / Math.cos(toRadians(angle / 2)); + + // Get the cone shape as a polygon + const rays = angles.map(a => Ray.fromAngle(0, 0, direction + toRadians(a), distance + 1)); + const height = Math.sqrt((rays[0].B.x - rays[1].B.x) * (rays[0].B.x - rays[1].B.x) + + (rays[0].B.y - rays[1].B.y) * (rays[0].B.y - rays[1].B.y)); + mat.scale(width / this.texture.width, height / this.texture.height); + mat.translate(0, -height / 2) + mat.rotate(toRadians(this.data.direction)); + } + this.template.beginTextureFill({ + texture: this.texture, + matrix: mat, + alpha: 1.0 + }); + // move into draw or so + const source = getProperty(this.texture, "baseTexture.resource.source") + if (source && (source.tagName === "VIDEO")) { + source.loop = true; + source.muted = true; + game.video.play(source); + } + } + else this.template.beginFill(0x000000, 0.0); + + // Draw the shape + this.template.drawShape(this.shape); + + // Draw origin and destination points + this.template.lineStyle(this._borderThickness, 0x000000) + .beginFill(0x000000, 0.5) + .drawCircle(0, 0, 6) + .drawCircle(this.ray.dx, this.ray.dy, 6); + + // Update visibility + this.controlIcon.visible = this.layer._active; + this.controlIcon.border.visible = this._hover; + + // Draw ruler text + this._refreshRulerText(); + return this; } - return this; - }; - })(); -} \ No newline at end of file + } + return this; + }; +})(); diff --git a/tokenmagic/module/settings.js b/tokenmagic/module/settings.js index d31f754..de2cdc0 100644 --- a/tokenmagic/module/settings.js +++ b/tokenmagic/module/settings.js @@ -132,6 +132,15 @@ export class TokenMagicSettings extends FormApplication { type: Boolean }); + game.settings.register("tokenmagic", "disableVideo", { + name: game.i18n.localize("TMFX.settings.disableVideo.name"), + hint: game.i18n.localize("TMFX.settings.disableVideo.hint"), + scope: "world", + config: true, + default: false, + type: Boolean + }); + game.settings.register("tokenmagic", "presets", { name: "Token Magic FX presets", hint: "Token Magic FX presets", diff --git a/tokenmagic/module/tokenmagic.js b/tokenmagic/module/tokenmagic.js index fbd7669..bf76d89 100644 --- a/tokenmagic/module/tokenmagic.js +++ b/tokenmagic/module/tokenmagic.js @@ -38,7 +38,6 @@ import { Anime } from "../fx/Anime.js"; import { allPresets, PresetsLibrary } from "../fx/presets/defaultpresets.js"; import { tmfxDataMigration } from "../migration/migration.js"; import { emptyPreset } from './constants.js'; -import { enableMeasuredTemplatePrototypeRefresh } from "./proto/PlaceableObjectProto.js"; import "./proto/PlaceableObjectProto.js"; /* @@ -151,6 +150,10 @@ export function isFilterCachingDisabled() { return game.settings.get("tokenmagic", "disableCaching"); } +export function isVideoDisabled() { + return game.settings.get("tokenmagic", "disableVideo"); +} + export var isFurnaceDrawingsActive = () => { // module exeption for the Furnace by KaKaRoTo if (isActiveModule("furnace") @@ -1400,6 +1403,10 @@ function getAnchor(direction, angle, shapeType) { function onMeasuredTemplateConfig(data, html) { + if (!isVideoDisabled()) { + html[0].querySelector('.file-picker').dataset.type = 'imagevideo'; + } + function compare(a, b) { if (a.name < b.name) return -1; if (a.name > b.name) return 1; @@ -1465,8 +1472,8 @@ Hooks.on("ready", () => { tmfxDataMigration(); initSocketListener(); initFurnaceDrawingsException(); - enableMeasuredTemplatePrototypeRefresh(); window.TokenMagic = Magic; + Hooks.on("renderMeasuredTemplateConfig", onMeasuredTemplateConfig); }); @@ -1811,6 +1818,11 @@ Hooks.on("preCreateMeasuredTemplate", (scene, data, options, user) => { data.flags = {}; } + // normalizing color to value if needed + if (hasTint && typeof data.tmfxTint !== "number") { + data.tmfxTint = colorStringToHex(data.tmfxTint); + } + // FX to add ? if (hasPreset) { @@ -1827,9 +1839,7 @@ Hooks.on("preCreateMeasuredTemplate", (scene, data, options, user) => { }; // Adding tint if needed - let tint = data.tmfxTint; - if (hasTint && typeof data.tmfxTint !== "number") tint = colorStringToHex(tint); - if (hasTint) pstSearch.color = tint; + if (hasTint) pstSearch.color = data.tmfxTint; // Retrieving the preset let preset = Magic.getPreset(pstSearch); @@ -1889,7 +1899,7 @@ Hooks.on("preCreateMeasuredTemplate", (scene, data, options, user) => { if (!hasOpacity) data.tmfxTextureAlpha = 1; if (!hasTint) data.tmfxTint = null; - + let tmfxTemplateData = { templateData: { opacity: data.tmfxTextureAlpha, diff --git a/tokenmagic/templates/settings/dnd5e/categories.html b/tokenmagic/templates/settings/dnd5e/categories.html index c78ae0b..4eb195d 100644 --- a/tokenmagic/templates/settings/dnd5e/categories.html +++ b/tokenmagic/templates/settings/dnd5e/categories.html @@ -1,85 +1,85 @@