}
* @private
*/
async _resetLoop(firstLoop = true) {
if (this._ended)
return;
let loopWaitTime = 0;
if (this._animationTimes.loopStart) {
if (this._isEnding)
return;
this.mediaCurrentTime = (firstLoop ? 0 : this._animationTimes.loopStart) + (this._loopOffset > 0 ? this._loopOffset : 0);
loopWaitTime = (this._animationTimes.loopEnd - this.mediaCurrentTime) * 1e3;
} else {
this.mediaCurrentTime = this._startTime + this._loopOffset;
loopWaitTime = this._animationDuration - this._loopOffset * 1e3;
}
await this.playMedia();
if (this.mediaLooping) {
return;
}
this._resetTimeout = setTimeout(() => {
if (this._ended)
return;
this._loopOffset = 0;
this._resetLoop(false);
}, loopWaitTime);
}
/** @OVERRIDE */
_timeoutVisibility() {
let creationTimeDifference = this.actualCreationTime - this.data.creationTimestamp;
let timeout = creationTimeDifference === 0 && !this.data.animations ? 0 : 50;
setTimeout(() => {
this._setupHooks();
}, timeout);
}
/** @OVERRIDE */
_setEndTimeout() {
let creationTimeDifference = this.actualCreationTime - this.data.creationTimestamp;
if (!this.data.noLoop || creationTimeDifference >= this._animationDuration || !(this.hasAnimatedMedia || this.data.text))
return;
setTimeout(() => {
this.pauseMedia();
}, this._animationDuration);
}
/** @OVERRIDE */
async endEffect() {
if (this._isEnding)
return;
this._isEnding = true;
let fullWaitDuration = 0;
let extraEndDuration = this.data.extraEndDuration ?? 0;
if (this._animationTimes?.forcedEnd) {
this.mediaCurrentTime = this._animationTimes.forcedEnd;
fullWaitDuration = (this.mediaDuration - (this._animationTimes?.forcedEnd ?? 0)) * 1e3;
} else if (this._animationTimes?.loopEnd) {
fullWaitDuration = (this.mediaDuration - this.mediaCurrentTime) * 1e3;
this.mediaLooping = false;
extraEndDuration = Math.max(extraEndDuration, fullWaitDuration);
}
const fromEndCustomAnimations = this._getFromEndCustomAnimations(extraEndDuration);
const durations = [
this._fadeOut(extraEndDuration),
this._fadeOutAudio(extraEndDuration),
this._scaleOut(extraEndDuration),
this._rotateOut(extraEndDuration),
this.data.extraEndDuration,
fullWaitDuration,
...fromEndCustomAnimations.map(
(animation2) => animation2.duration + animation2.delay
)
].filter(Boolean);
SequencerAnimationEngine.addAnimation(this.id, fromEndCustomAnimations);
const waitDuration = Math.max(...durations, 0);
this._resolve(waitDuration);
return new Promise(
(resolve) => setTimeout(() => {
super.endEffect();
resolve(this.data);
}, waitDuration)
);
}
}
function createShape(shape) {
const graphic = new PIXI.LegacyGraphics();
graphic.beginFill(
shape?.fillColor ?? 16777215,
shape?.fillAlpha ?? shape?.isMask ? 1 : 0
);
graphic.lineStyle(
shape.lineSize ?? (shape?.isMask ? 1 : 0),
shape?.lineColor ?? 16777215
);
const offsetX = (shape.offset?.x ?? 0) * (shape.offset?.gridUnits ? canvas.grid.size : 1);
const offsetY = (shape.offset?.y ?? 0) * (shape.offset?.gridUnits ? canvas.grid.size : 1);
const sizeMultiplier = shape.gridUnits ? canvas.grid.size : 1;
graphic.offset = {
x: offsetX,
y: offsetY
};
switch (shape.type) {
case CONSTANTS.SHAPES.CIRC:
graphic.drawCircle(
graphic.offset.x,
graphic.offset.y,
shape.radius * sizeMultiplier
);
break;
case CONSTANTS.SHAPES.RECT:
graphic.drawRect(
graphic.offset.x,
graphic.offset.y,
shape.width * sizeMultiplier,
shape.height * sizeMultiplier
);
break;
case CONSTANTS.SHAPES.ELIP:
graphic.drawEllipse(
graphic.offset.x,
graphic.offset.y,
shape.width * sizeMultiplier,
shape.height * sizeMultiplier
);
break;
case CONSTANTS.SHAPES.RREC:
graphic.drawRoundedRect(
graphic.offset.x,
graphic.offset.y,
shape.width * sizeMultiplier,
shape.height * sizeMultiplier,
shape.radius * sizeMultiplier
);
break;
case CONSTANTS.SHAPES.POLY:
graphic.drawPolygon(
shape.points.map((point) => {
return new PIXI.Point(
point[0] * sizeMultiplier,
point[1] * sizeMultiplier
);
})
);
break;
}
graphic.alpha = shape.alpha ?? 1;
graphic.endFill();
return graphic;
}
function calculate_missed_position(source, target, twister) {
const sourcePosition = get_object_position(source);
const sourceDimensions = get_object_dimensions(source, true);
if (!target) {
const angle2 = twister.random() * Math.PI * 2;
let x2 = Math.cos(angle2) * sourceDimensions.width;
let y2 = Math.sin(angle2) * sourceDimensions.height;
return {
x: random_float_between(x2 * 1.5, x2 * 2.5, twister),
y: random_float_between(y2 * 1.5, y2 * 2.5, twister)
};
}
const targetDimensions = get_object_dimensions(target, true);
const targetPosition = get_object_position(target);
const ray = new Ray(targetPosition, sourcePosition);
let startRadians = ray.angle + Math.PI / 2;
let endRadians = ray.angle - Math.PI / 2;
const sizeCompensation = Math.max(
1,
Math.abs(sourceDimensions.width - targetDimensions.height)
);
let distance = ray.distance / canvas.grid.size - sizeCompensation;
if (distance <= 1) {
const angle2 = twister.random() > 0.5 ? ray.angle + Math.PI / 4 : ray.angle - Math.PI / 4;
const x2 = Math.cos(angle2) * targetDimensions.width;
const y2 = Math.sin(angle2) * targetDimensions.height;
return { x: x2, y: y2 };
}
distance = Math.max(Math.abs(distance - 15), 6);
endRadians -= Math.PI / distance;
startRadians += Math.PI / distance;
const angle = interpolate(startRadians, endRadians, twister.random());
const x = Math.cos(angle) * targetDimensions.width;
const y = Math.sin(angle) * targetDimensions.height;
return {
x: random_float_between(x * 1.5, x * 2.5, twister),
y: random_float_between(y * 1.5, y * 2.5, twister)
};
}
function get_object_position(obj, { measure = false, exact = false } = {}) {
if (obj instanceof CanvasEffect) {
return obj.worldPosition;
}
obj = obj?._object ?? obj.object ?? obj;
let pos = {};
if (obj instanceof MeasuredTemplate) {
if (measure) {
if (obj.document.t === "cone" || obj.document.t === "ray") {
pos.x = obj.ray.B.x;
pos.y = obj.ray.B.y;
}
}
if (obj.document.t === "rect") {
pos.x = obj.x;
pos.y = obj.y;
if (!exact) {
pos.x += Math.abs(obj.shape.width / 2) + obj.shape.x;
pos.y += Math.abs(obj.shape.height / 2) + obj.shape.y;
}
}
} else if (obj instanceof Tile) {
pos = {
x: obj.document.x,
y: obj.document.y
};
if (!exact) {
pos.x += Math.abs(obj.document.width / 2);
pos.y += Math.abs(obj.document.height / 2);
}
} else if (obj instanceof Token) {
const halfSize = get_object_dimensions(obj, true);
pos = {
x: obj.x + halfSize.width,
y: obj.y + halfSize.height
};
if (exact) {
pos.x -= halfSize.width;
pos.y -= halfSize.height;
}
} else if (obj instanceof Drawing) {
pos = {
x: obj.document.x,
y: obj.document.y
};
if (!exact) {
const halfSize = get_object_dimensions(obj, true);
pos.x += halfSize.width;
pos.y += halfSize.height;
}
}
pos = {
x: pos.x ?? obj?.x ?? obj?.position?.x ?? obj?.position?._x ?? obj?.document?.x ?? obj?.document?.position?.x ?? null,
y: pos.y ?? obj?.y ?? obj?.position?.y ?? obj?.position?._y ?? obj?.document?.y ?? obj?.document?.position?.y ?? null,
elevation: obj?.elevation ?? obj?.document?.elevation ?? null
};
if (pos.x === null)
delete pos["x"];
if (pos.y === null)
delete pos["y"];
if (pos.elevation === null)
delete pos["elevation"];
return pos;
}
function get_random_offset(target, randomOffset, twister = false) {
let { width: width2, height } = get_object_dimensions(target, true);
width2 *= randomOffset;
height *= randomOffset;
return {
x: random_float_between(width2 * -1, width2, twister),
y: random_float_between(height * -1, height, twister)
};
}
function get_object_dimensions(inObj, half = false) {
inObj = inObj?.object ?? inObj?._object ?? inObj;
let width2 = inObj?.hitArea?.width ?? inObj?.w ?? inObj?.shape?.width ?? (inObj?.shape?.radius ? inObj?.shape?.radius * 2 : void 0) ?? inObj?.width ?? canvas.grid.size;
let height = inObj?.hitArea?.height ?? inObj?.h ?? inObj?.shape?.height ?? (inObj?.shape?.radius ? inObj?.shape?.radius * 2 : void 0) ?? inObj?.height ?? canvas.grid.size;
return {
width: width2 / (half ? 2 : 1),
height: height / (half ? 2 : 1)
};
}
const alignments = {
"top-left": { x: 0.5, y: 0.5 },
top: { x: 0, y: 0.5 },
"top-right": { x: -0.5, y: 0.5 },
left: { x: 0.5, y: 0 },
center: { x: 0, y: 0 },
right: { x: -0.5, y: 0 },
"bottom-left": { x: 0.5, y: -0.5 },
bottom: { x: 0, y: -0.5 },
"bottom-right": { x: -0.5, y: -0.5 }
};
function align({
context,
spriteWidth,
spriteHeight,
align: align2,
edge
} = {}) {
let { width: width2, height } = get_object_dimensions(context);
const alignRatio = alignments[align2];
const offset2 = {
x: interpolate(width2 * -0.5, width2 * 0.5, alignRatio.x + 0.5),
y: interpolate(height * -0.5, height * 0.5, alignRatio.y + 0.5)
};
return {
x: offset2.x + (edge && edge !== "on" ? spriteWidth * alignRatio.x * (edge === "outer" ? 1 : -1) : 0),
y: offset2.y + (edge && edge !== "on" ? spriteHeight * alignRatio.y * (edge === "outer" ? 1 : -1) : 0)
};
}
function is_object_canvas_data(inObj) {
if (typeof inObj !== "object")
return false;
const keys = Object.keys(inObj);
keys.sort();
return keys.includes("x") && keys.includes("y") || keys.includes("height") && keys.includes("width") && keys.includes("x") && keys.includes("y");
}
function get_object_canvas_data(inObject, measure = false) {
inObject = inObject?.object ?? inObject;
return {
...get_object_position(inObject, { measure }),
...get_object_dimensions(inObject?.mesh ?? inObject?.tile ?? inObject),
elevation: get_object_elevation(inObject),
uuid: inObject?.document?.uuid ?? inObject?.uuid,
cachedLocation: true
};
}
function get_object_elevation(inObject) {
return inObject?.document?.elevation ?? inObject?.elevation ?? 0;
}
function get_mouse_position(snapToGrid = false, gridSnap = 2) {
const pos = getCanvasMouse().getLocalPosition(canvas.app.stage);
return !snapToGrid ? new PIXI.Point(pos.x, pos.y) : canvas.grid.getSnappedPosition(pos.x, pos.y, gridSnap);
}
function distance_between(p1, p2) {
return new Ray(p1, p2).distance;
}
function is_position_within_bounds(inPosition, inElement, relativeTo) {
const localPosition = inElement.toLocal(inPosition, relativeTo);
return inElement.getLocalBounds().contains(localPosition.x, localPosition.y);
}
function rotate_coordinate(p1, p2, radians) {
let cos = Math.cos(radians);
let sin = Math.sin(radians);
let nx = cos * (p2.x - p1.x) + sin * (p2.y - p1.y) + p1.x;
let ny = cos * (p2.y - p1.y) - sin * (p2.x - p1.x) + p1.y;
return [nx, ny];
}
function get_closest_token(inPosition, { minimumDistance = false } = {}) {
let tokens = Array.from(canvas.scene.tokens);
if (minimumDistance) {
tokens = tokens.filter(
(token) => distance_between(get_object_position(token), inPosition) <= minimumDistance
);
}
tokens.sort((a, b) => {
return distance_between(get_object_position(a), inPosition) - distance_between(get_object_position(b), inPosition);
});
return tokens?.[0] ?? false;
}
function rotateAroundPoint(cx, cy, x, y, radians) {
const cos = Math.cos(radians);
const sin = Math.sin(radians);
const nx = cos * (x - cx) + sin * (y - cy) + cx;
const ny = cos * (y - cy) - sin * (x - cx) + cy;
return { x: nx, y: ny };
}
function validateAnimation(inTarget, inPropertyName, inOptions) {
if (typeof inPropertyName !== "string") {
return `inPropertyName must be of type string`;
}
if (typeof inTarget !== "string") {
return `inTarget must be of type string`;
}
if (!is_real_number(inOptions.from)) {
return `inOptions.from must be of type number`;
}
if (!is_real_number(inOptions.to)) {
return `inOptions.to must be of type number`;
}
if (!is_real_number(inOptions.duration)) {
return `inOptions.duration must be of type number`;
}
if (inOptions?.delay !== void 0 && !is_real_number(inOptions.delay)) {
return `inOptions.delay must be of type number`;
}
if (inOptions?.ease !== void 0 && typeof inOptions.ease !== "string") {
return `inOptions.ease must be of type string`;
}
if (inOptions?.fromEnd !== void 0 && typeof inOptions.fromEnd !== "boolean") {
return `inOptions.fromEnd must be of type boolean`;
}
if (inOptions?.gridUnits !== void 0) {
if (typeof inOptions.gridUnits !== "boolean") {
return `inOptions.gridUnits must be of type boolean`;
}
if (inOptions.gridUnits && ![
"position.x",
"position.y",
"scale.x",
"scale.y",
"height",
"width"
].includes(inPropertyName)) {
return `if inOptions.gridUnits is true, inPropertyName must be position.x, position.y, scale.x, scale.y, width, or height`;
}
}
return {
target: inTarget,
propertyName: inPropertyName,
from: inOptions?.from,
to: inOptions?.to,
duration: inOptions?.duration ?? 0,
delay: inOptions?.delay ?? 0,
ease: inOptions?.ease ?? "linear",
looping: false,
fromEnd: inOptions?.fromEnd ?? false,
gridUnits: inOptions?.gridUnits ?? false
};
}
function validateLoopingAnimation(inTarget, inPropertyName, inOptions) {
if (typeof inPropertyName !== "string") {
return `inPropertyName must be of type string`;
}
if (typeof inTarget !== "string") {
return `inTarget must be of type string`;
}
if (!inOptions?.values) {
if (!inOptions?.from === void 0 || !inOptions?.to === void 0) {
return `if inOptions.values is not set, you must provide inOptions.from and inOptions.to`;
}
if (!is_real_number(inOptions.from)) {
return `inOptions.from must be of type number`;
}
if (!is_real_number(inOptions.to)) {
return `inOptions.to must be of type number`;
}
inOptions.values = [inOptions?.from, inOptions?.to];
delete inOptions.from;
delete inOptions.to;
} else {
if (!Array.isArray(inOptions.values)) {
return `inOptions.values must be of type array`;
}
inOptions.values.forEach((value) => {
if (!is_real_number(value)) {
return `values in inOptions.keys must be of type number`;
}
});
}
if (!is_real_number(inOptions.duration)) {
return `inOptions.duration must be of type number`;
}
if (inOptions?.delay !== void 0 && !is_real_number(inOptions.delay)) {
return `inOptions.delay must be of type number`;
}
if (inOptions?.ease !== void 0 && typeof inOptions.ease !== "string") {
return `inOptions.ease must be of type string`;
}
if (inOptions?.loops !== void 0 && !is_real_number(inOptions.loops)) {
return `inOptions.loops must be of type number`;
}
if (inOptions?.pingPong !== void 0 && typeof inOptions.pingPong !== "boolean") {
return `inOptions.pingPong must be of type boolean`;
}
if (inOptions?.gridUnits !== void 0) {
if (typeof inOptions.gridUnits !== "boolean") {
return `inOptions.gridUnits must be of type boolean`;
}
if (inOptions.gridUnits && ![
"position.x",
"position.y",
"scale.x",
"scale.y",
"height",
"width"
].includes(inPropertyName)) {
return `if inOptions.gridUnits is true, inPropertyName must be position.x, position.y, scale.x, scale.y, width, or height`;
}
}
return {
target: inTarget,
propertyName: inPropertyName,
values: inOptions?.values,
duration: inOptions?.duration ?? 0,
delay: inOptions?.delay ?? 0,
ease: inOptions?.ease ?? "linear",
looping: true,
loops: inOptions?.loops,
indefinite: inOptions?.loops === void 0 || !is_real_number(inOptions?.loops),
pingPong: inOptions?.pingPong ?? false,
gridUnits: inOptions?.gridUnits ?? false
};
}
const PlayerSettings = {
file: {
label: "SEQUENCER.Player.Option.File",
store: writable$1(""),
default: ""
},
scale: {
label: "SEQUENCER.Player.Option.Scale",
store: writable$1(1),
default: 1
},
users: {
label: "SEQUENCER.Player.Option.ForUsers",
store: writable$1([]),
default: []
},
belowTokens: {
label: "SEQUENCER.Player.Option.BelowTokens",
store: writable$1(false),
default: false
},
snapToGrid: {
label: "SEQUENCER.Player.Option.SnapToGrid",
store: writable$1(false),
default: false
},
rotation: {
label: "SEQUENCER.Player.Option.Rotation",
store: writable$1(0),
default: 0
},
randomRotation: {
label: "SEQUENCER.Player.Option.Randomize",
store: writable$1(false),
default: false
},
fadeIn: {
label: "SEQUENCER.Player.Option.FadeIn",
store: writable$1(0),
default: 0
},
fadeOut: {
label: "SEQUENCER.Player.Option.FadeOut",
store: writable$1(0),
default: 0
},
scaleIn: {
label: "SEQUENCER.Player.Option.ScaleIn",
store: writable$1(0),
default: 0
},
scaleOut: {
label: "SEQUENCER.Player.Option.ScaleOut",
store: writable$1(0),
default: 0
},
mirrorX: {
label: "SEQUENCER.Player.Option.MirrorX",
store: writable$1(false),
default: false
},
mirrorY: {
label: "SEQUENCER.Player.Option.MirrorY",
store: writable$1(false),
default: false
},
randomMirrorX: {
label: "SEQUENCER.Player.Option.Randomize",
store: writable$1(false),
default: false
},
randomMirrorY: {
label: "SEQUENCER.Player.Option.Randomize",
store: writable$1(false),
default: false
},
offsetX: {
label: "SEQUENCER.Player.Option.OffsetX",
store: writable$1(0),
default: 0
},
offsetY: {
label: "SEQUENCER.Player.Option.OffsetY",
store: writable$1(0),
default: 0
},
offsetGridUnits: {
label: "SEQUENCER.Player.Option.GridUnits",
store: writable$1(false),
default: false
},
randomOffsetAmount: {
label: "SEQUENCER.Player.Option.RandomOffset",
store: writable$1(0),
default: 0
},
randomOffset: {
label: "SEQUENCER.Player.Option.Randomize",
store: writable$1(false),
default: false
},
preload: {
label: "SEQUENCER.Player.Option.Preload",
store: writable$1(false),
default: false
},
moveTowards: {
label: "SEQUENCER.Player.Option.DragBehavior",
label_off: "SEQUENCER.Player.Option.DragStretch",
label_on: "SEQUENCER.Player.Option.DragMove",
store: writable$1(false),
default: false
},
moveSpeed: {
label: "SEQUENCER.Player.Option.MoveSpeed",
store: writable$1(0),
default: 0
},
attachTo: {
label: "SEQUENCER.Player.Option.AttachTo",
store: writable$1(false),
default: false,
callback: (e) => {
EffectPlayer.sourceAttach = e.target.checked;
}
},
stretchToAttach: {
label: "SEQUENCER.Player.Option.StretchToAttach",
store: writable$1(false),
default: false,
callback: (e) => {
EffectPlayer.targetAttach = e.target.checked;
}
},
persist: {
label: "SEQUENCER.Player.Option.Persist",
store: writable$1(false),
default: false
},
repeat: {
label: "SEQUENCER.Player.Option.Repeat",
store: writable$1(false),
default: false
},
repetitions: {
label: "SEQUENCER.Player.Option.Repetitions",
store: writable$1(1),
default: 1
},
repeatDelayMin: {
label: "SEQUENCER.Player.Option.DelayMin",
store: writable$1(200),
default: 200
},
repeatDelayMax: {
label: "SEQUENCER.Player.Option.DelayMax",
store: writable$1(400),
default: 400
}
};
PlayerSettings.export = () => {
return Object.fromEntries(
Object.entries(PlayerSettings).map((entry) => {
return [entry[0], get_store_value(entry[1].store)];
})
);
};
PlayerSettings.import = (settings) => {
Object.entries(PlayerSettings).forEach((entry) => {
if (settings?.[entry[0]] !== void 0) {
entry[1].store.set(settings?.[entry[0]]);
} else if (entry[1]?.default !== void 0) {
entry[1].store.set(entry[1].default);
}
});
};
PlayerSettings.getPresets = () => {
return Object.keys(game.settings.get(CONSTANTS.MODULE_NAME, "effectPresets"));
};
PlayerSettings.loadPreset = (name) => {
const effectPresets = game.settings.get(
CONSTANTS.MODULE_NAME,
"effectPresets"
);
return PlayerSettings.import(effectPresets[name]);
};
PlayerSettings.savePreset = async (name) => {
const newName = await promptNewPresetName(name);
if (!newName)
return;
const effectPresets = game.settings.get(
CONSTANTS.MODULE_NAME,
"effectPresets"
);
effectPresets[newName] = PlayerSettings.export();
return game.settings.set(
CONSTANTS.MODULE_NAME,
"effectPresets",
effectPresets
);
};
PlayerSettings.copyPreset = async (name) => {
const newName = await promptNewPresetName(name, true);
if (!newName)
return;
const effectPresets = game.settings.get(
CONSTANTS.MODULE_NAME,
"effectPresets"
);
effectPresets[newName] = PlayerSettings.export();
return game.settings.set(
CONSTANTS.MODULE_NAME,
"effectPresets",
effectPresets
);
};
PlayerSettings.deletePreset = (name) => {
const effectPresets = game.settings.get(
CONSTANTS.MODULE_NAME,
"effectPresets"
);
delete effectPresets[name];
return game.settings.set(
CONSTANTS.MODULE_NAME,
"effectPresets",
effectPresets
);
};
async function promptNewPresetName(inName, copy = false) {
const effectPresets = game.settings.get(
CONSTANTS.MODULE_NAME,
"effectPresets"
);
let title = copy ? game.i18n.localize("SEQUENCER.Player.CopyPresetTitle") : game.i18n.localize("SEQUENCER.Player.CreateNewPresetTitle");
let presetName = await new Promise((resolve) => {
new Dialog({
title,
content: ``,
buttons: {
okay: {
icon: '',
label: game.i18n.localize("SEQUENCER.OK"),
callback: async (html) => {
let name = html.find("#newPresetName").val();
if (name === "" || !name) {
name = false;
}
resolve(name);
}
},
cancel: {
icon: '',
label: game.i18n.localize("SEQUENCER.Cancel"),
callback: () => {
resolve(false);
}
}
},
close: () => {
},
render: (html) => {
html.find("#newPresetName").val(inName).focus();
}
}).render(true);
});
if (presetName) {
if (presetName.toLowerCase() === "default") {
Dialog.prompt({
title: game.i18n.localize("SEQUENCER.Player.DefaultErrorTitle"),
content: `${game.i18n.localize(
"SEQUENCER.Player.DefaultErrorContent"
)}
`,
label: game.i18n.localize("SEQUENCER.OK"),
callback: () => {
}
});
return false;
}
if (effectPresets[presetName]) {
const overwrite = await Dialog.confirm({
title: game.i18n.localize("SEQUENCER.Player.OverwritePresetTitle"),
content: `${game.i18n.format(
"SEQUENCER.Player.OverwritePresetContent",
{ preset_name: presetName }
)}
`
});
if (!overwrite) {
presetName = await promptPresetName(presetName);
}
}
}
return presetName;
}
PlayerSettings.migrateOldPresets = () => {
if (!game.user.isGM)
return;
const effectPresets = game.settings.get(
CONSTANTS.MODULE_NAME,
"effectPresets"
);
const presetsToUpdate = Object.values(effectPresets).filter((preset) => {
return !preset?.version;
});
if (!presetsToUpdate.length)
return;
const newEffectPresets = Object.fromEntries(
Object.entries(effectPresets).map(([name, preset]) => {
if (preset?.version)
return [name, preset];
preset.version = game.modules.get(CONSTANTS.MODULE_NAME).version;
if (preset.repetitions > 1) {
preset.repeat = true;
}
if (preset.randomMirrorY) {
preset.mirrorY = true;
}
if (preset.randomOffset) {
preset.randomOffsetAmount = 1;
preset.offsetGridUnits = true;
}
return [name, preset];
})
);
return game.settings.set(
CONSTANTS.MODULE_NAME,
"effectPresets",
newEffectPresets
);
};
const InteractionManager = {
startDragPosition: false,
state: {
LeftMouseDown: false,
RightMouseDown: false,
Dragging: false
},
get isLayerActive() {
return canvas.sequencerInterfaceLayer.active;
},
initialize() {
window.addEventListener("mousedown", (event) => {
if (!canvas.ready)
return;
if (!this.isLayerActive)
return;
const hover = document.elementFromPoint(event.clientX, event.clientY);
if (!hover || hover.id !== "board")
return;
const button = event.button;
if (!(button === 0 || button === 2))
return;
if (button === 0) {
this.state.LeftMouseDown = true;
this._propagateEvent("mouseLeftDown");
}
if (button === 2) {
this.state.RightMouseDown = true;
this._propagateEvent("mouseRightDown");
}
});
window.addEventListener("mouseup", (event) => {
if (!canvas.ready)
return;
if (!this.isLayerActive)
return;
const hover = document.elementFromPoint(event.clientX, event.clientY);
if (!hover || hover.id !== "board")
return;
if (document.activeElement.tagName !== "BODY")
return;
const button = event.button;
if (!(button === 0 || button === 2))
return;
if (button === 0) {
this.state.LeftMouseDown = false;
this._propagateEvent("mouseLeftUp");
this.state.Dragging = false;
this.startDragPosition = false;
}
if (button === 2) {
this.state.RightMouseDown = false;
this._propagateEvent("mouseRightUp");
this.state.Dragging = false;
this.startDragPosition = false;
}
});
window.addEventListener("mousemove", (event) => {
if (!canvas.ready)
return;
const hover = document.elementFromPoint(event.clientX, event.clientY);
if (!hover || hover.id !== "board")
return;
if (!this.isLayerActive)
return;
this._propagateEvent("mouseMove");
if (this.state.LeftMouseDown && !this.startDragPosition) {
this.startDragPosition = get_mouse_position();
}
if (this.state.LeftMouseDown && !this.state.Dragging) {
const distance = distance_between(
this.startDragPosition,
get_mouse_position()
);
this.state.Dragging = distance > 20;
}
});
EffectPlayer.initialize();
SelectionManager.initialize();
},
tearDown() {
EffectPlayer.tearDown();
SelectionManager.tearDown();
},
_propagateEvent(eventName) {
if (EffectPlayer.isActive && EffectPlayer[eventName]) {
EffectPlayer[eventName]();
}
if (SelectionManager.isActive && SelectionManager[eventName]) {
SelectionManager[eventName]();
}
}
};
const EffectPlayer = {
sequenceBuffer: [],
playMany: false,
playManySequenced: false,
cursorPos: false,
startPos: false,
endPos: false,
get snapLocationToGrid() {
return get_store_value(PlayerSettings.snapToGrid.store);
},
get sourceAttach() {
return get_store_value(PlayerSettings.attachTo.store);
},
get targetAttach() {
return get_store_value(PlayerSettings.stretchToAttach.store);
},
sourceAttachFound: false,
targetAttachFound: false,
get isActive() {
return InteractionManager.isLayerActive && game?.activeTool === "play-effect";
},
/**
* Opens the Sequencer Effects UI with the player tab open
*/
show() {
return EffectsUIApp.show({ tab: "player" });
},
initialize() {
this.layer = canvas.sequencerInterfaceLayer;
},
tearDown() {
this._reset();
},
/**
* Mouse events
*/
mouseLeftDown() {
this._evaluateStartPosition();
},
mouseLeftUp() {
if (!this.startPos)
return;
this._playEffect();
this.startPos = false;
this.endPos = false;
this.sourceAttachFound = false;
this.targetAttachFound = false;
},
mouseRightUp() {
this._reset();
},
mouseMove() {
this._evaluateCursorPosition();
if (InteractionManager.state.Dragging) {
this._evaluateEndPosition();
}
},
/**
* Hotkeys
*/
playManyUp() {
this._playEffects();
this._reset();
},
/**
* Private methods
*/
_evaluatePosition(attach = false) {
let position = get_mouse_position(this.snapLocationToGrid);
const attachToObject = attach ? get_closest_token(position, {
minimumDistance: canvas.grid.size
}) : false;
let attachFound = false;
if (attachToObject) {
attachFound = true;
position = get_object_position(attachToObject);
}
return [position, attachFound];
},
_evaluateCursorPosition() {
const attach = InteractionManager.state.Dragging ? this.targetAttach : this.sourceAttach;
[this.cursorPos] = this._evaluatePosition(attach);
},
_evaluateStartPosition() {
if (this.startPos)
return;
[this.startPos, this.sourceAttachFound] = this._evaluatePosition(
this.sourceAttach
);
},
_evaluateEndPosition() {
[this.endPos, this.targetAttachFound] = this._evaluatePosition(
this.targetAttach
);
},
_reset() {
if (!this.layer)
return;
this.startPos = false;
this.endPos = false;
this.sourceAttachFound = false;
this.targetAttachFound = false;
this.sequenceBuffer = [];
this._evaluateCursorPosition();
},
async _playEffect() {
const settings = foundry.utils.mergeObject(PlayerSettings.export(), {
...InteractionManager.state,
startPos: this.startPos,
endPos: this.endPos
});
if (!settings.users.length || settings.users?.[0] === "all")
settings.users = [];
if (settings.file === "")
return;
if (!(Sequencer.Database.entryExists(settings.file) || await SequencerFileCache.srcExists(settings.file))) {
throw custom_error(
"Sequencer",
`Sequencer Player | Could not find file or database entry: ${settings.file}`
);
}
if (settings.preload) {
await Sequencer.Preloader.preloadForClients(settings.file);
}
const sequence = this.sequenceBuffer.length > 0 && this.playManySequenced ? this.sequenceBuffer[this.sequenceBuffer.length - 1] : new Sequence();
const effect = sequence.effect().file(settings.file).forUsers(settings.users).mirrorX(settings.mirrorX && !settings.randomMirrorX).mirrorY(settings.mirrorY && !settings.randomMirrorY).randomizeMirrorX(settings.randomMirrorX).randomizeMirrorY(settings.randomMirrorY).persist(settings.persist).belowTokens(settings.belowTokens);
if (settings.repeat) {
effect.repeats(
settings.repetitions,
settings.repeatDelayMin,
settings.repeatDelayMax
);
}
if (settings.fadeIn > 0)
effect.fadeIn(settings.fadeIn);
if (settings.fadeOut > 0)
effect.fadeOut(settings.fadeOut);
const offsetData = settings.randomOffset ? {
offset: { x: settings.offsetX, y: settings.offsetY },
gridUnits: settings.offsetGridUnits
} : {};
const offsetSource = !settings.Dragging ? offsetData : {};
const attachToObject = settings.attachTo ? get_closest_token(settings.startPos, {
minimumDistance: canvas.grid.size
}) : false;
if (attachToObject) {
effect.attachTo(attachToObject, {
randomOffset: settings.randomOffset && !settings.Dragging ? settings.randomOffsetAmount : false,
...offsetSource
});
} else {
effect.atLocation(settings.startPos, {
randomOffset: settings.randomOffset && !settings.Dragging ? settings.randomOffsetAmount : false,
...offsetSource
});
}
if (settings.persist && settings.name && settings.name !== "" && settings.name !== "default" && settings.name !== "new") {
effect.name("Preset: " + settings.name);
}
if (settings.Dragging) {
if (settings.moveTowards) {
effect.moveTowards(settings.endPos, {
randomOffset: settings.randomOffset ? settings.randomOffsetAmount : false,
...offsetData
});
if (settings.moveSpeed) {
effect.moveSpeed(settings.moveSpeed);
}
} else {
let target = settings.stretchToAttach ? get_closest_token(settings.endPos, {
minimumDistance: canvas.grid.size
}) : settings.endPos;
effect.stretchTo(target, {
attachTo: settings.stretchToAttach,
randomOffset: settings.randomOffset ? settings.randomOffsetAmount : false,
...offsetData
});
}
}
if (!settings.Dragging || settings.Dragging && settings.moveTowards) {
effect.scale(settings.scale);
if (settings.scaleIn > 0)
effect.scaleIn(0, settings.scaleIn, { ease: "easeInOutSine" });
if (settings.scaleOut > 0)
effect.scaleOut(0, settings.scaleOut, { ease: "easeInOutSine" });
effect.randomRotation(settings.randomRotation);
}
if (this.playManySequenced) {
effect.waitUntilFinished();
}
if (!this.playManySequenced || this.sequenceBuffer.length === 0) {
this.sequenceBuffer.push(sequence);
}
if (!this.playMany && !this.playManySequenced)
this._playEffects();
},
_playEffects() {
this.sequenceBuffer.forEach((sequence) => sequence.play());
this.sequenceBuffer = [];
}
};
const SelectionManager = {
selectedEffect: false,
hoveredEffects: /* @__PURE__ */ new Set(),
suggestedProperties: false,
sourceOrTarget: false,
dragOffset: false,
hoveredEffectUI: false,
get snapToGrid() {
return get_store_value(PlayerSettings.snapToGrid.store);
},
set snapToGrid(bool) {
PlayerSettings.snapToGrid.store.set(bool);
},
set attachToTarget(bool) {
PlayerSettings.stretchToAttach.store.set(bool);
},
get attachToTarget() {
return get_store_value(PlayerSettings.stretchToAttach.store);
},
get isActive() {
return InteractionManager.isLayerActive && game?.activeTool === "select-effect";
},
get effects() {
return SequencerEffectManager.effects.filter(
(effect) => effect.userCanDelete
);
},
initialize() {
this.layer = canvas.sequencerInterfaceLayer;
},
tearDown() {
this._reset();
this.hoveredEffects = /* @__PURE__ */ new Set();
},
sourcePointSelected() {
this.sourceOrTarget = "source";
},
targetPointSelected() {
this.sourceOrTarget = "target";
},
/**
* Mouse Events
*/
mouseLeftDown() {
if (!this.selectedEffect) {
return this._selectEffects();
}
if (!this.hoveredEffects.size) {
this._reset();
}
},
mouseRightDown() {
},
mouseLeftUp() {
if (!InteractionManager.state.Dragging) {
return this._selectEffects();
}
if (!InteractionManager.state.Dragging || !this.selectedEffect || !this.suggestedProperties)
return;
this._updateEffect();
},
mouseRightUp() {
InteractionManager.state.LeftMouseDown = false;
this.suggestedProperties = false;
this.sourceOrTarget = false;
this.dragOffset = false;
},
mouseMove() {
this._evaluateHoveredEffects();
if (InteractionManager.state.LeftMouseDown && !InteractionManager.state.RightMouseDown) {
this._evaluateEffectPositionUpdate();
}
},
/**
* Hotkeys
*/
async delete() {
if (!this.selectedEffect)
return;
await SequencerEffectManager.endEffects({ effects: this.selectedEffect });
this.selectedEffect = false;
},
attachToTargetDown() {
if (InteractionManager.state.LeftMouseDown && !InteractionManager.state.RightMouseDown) {
this._evaluateEffectPositionUpdate();
}
},
/**
* Private methods
*/
_selectEffects() {
this._reset();
if (!this.hoveredEffects.size)
return;
const firstElement = Array.from(this.hoveredEffects)[0];
this.selectedEffect = !firstElement.selected ? firstElement : false;
},
_evaluateHoveredEffects() {
const position = get_mouse_position();
this.hoveredEffects = this.effects.filter(
(effect) => effect.isPositionWithinBounds(position)
);
this.hoveredEffects.sort((a, b) => {
return a.data.layer !== b.data.zIndex ? a.data.zIndex - b.data.zIndex : a.data.layer - b.data.zIndex;
});
this.hoveredEffects = new Set(this.hoveredEffects);
},
_evaluateEffectPositionUpdate() {
if (!this.selectedEffect)
return;
if (this.selectedEffect.data.stretchTo && !this.sourceOrTarget) {
return;
}
let showCursor = false;
let showPoint = this.snapToGrid;
let position = get_mouse_position(this.snapToGrid);
if (!this.selectedEffect.data.stretchTo && !this.dragOffset) {
this.dragOffset = {
x: position.x - this.selectedEffect.position.x,
y: position.y - this.selectedEffect.position.y
};
}
if (this.attachToTarget) {
const obj = get_closest_token(position, {
minimumDistance: canvas.grid.size
});
if (obj) {
position = get_object_position(obj);
showCursor = true;
showPoint = false;
}
}
if (this.dragOffset && !showCursor && !this.snapToGrid) {
position.x -= this.dragOffset.x;
position.y -= this.dragOffset.y;
}
const color = (this.sourceOrTarget || "source") === "source" ? CONSTANTS.COLOR.PRIMARY : CONSTANTS.COLOR.SECONDARY;
this.suggestedProperties = {
position,
showCursor,
showPoint,
color
};
},
_updateEffect() {
if (!this.selectedEffect)
return;
const updates = {
attachTo: this.selectedEffect.data.attachTo,
stretchTo: this.selectedEffect.data.stretchTo
};
const obj = this.attachToTarget ? get_closest_token(this.suggestedProperties.position, {
minimumDistance: canvas.grid.size,
type: TokenDocument
}) : false;
let objUuid = obj ? get_object_identifier(obj) : false;
if (this.sourceOrTarget === "source") {
if (!updates.attachTo) {
updates.attachTo = {
active: false,
align: "center",
rotation: true,
bindVisibility: true,
bindAlpha: true
};
}
updates.attachTo = foundry.utils.mergeObject(updates.attachTo || {}, {
active: this.attachToTarget && !!objUuid
});
if (this.attachToTarget && objUuid) {
updates.source = objUuid;
} else {
updates["source"] = this.suggestedProperties.position;
}
} else if (this.sourceOrTarget === "target") {
updates.stretchTo.attachTo = this.attachToTarget && !!objUuid;
if (this.attachToTarget && objUuid) {
updates.target = objUuid;
} else {
updates["target"] = this.suggestedProperties.position;
}
} else {
updates["source"] = this.suggestedProperties.position;
}
this.selectedEffect.update(updates);
this.suggestedProperties = false;
this.sourceOrTarget = false;
this.dragOffset = false;
},
_reset() {
this.selectedEffect = false;
this.suggestedProperties = false;
this.sourceOrTarget = false;
this.dragOffset = false;
}
};
const EffectEntry_svelte_svelte_type_style_lang = "";
function create_fragment$c(ctx) {
let div1;
let button;
let t0;
let div0;
let t1_value = (
/*getEffectName*/
ctx[1](
/*effect*/
ctx[0]
) + ""
);
let t1;
let mounted;
let dispose;
return {
c() {
div1 = element("div");
button = element("button");
button.innerHTML = ``;
t0 = space();
div0 = element("div");
t1 = text$1(t1_value);
attr(button, "class", "btn_end svelte-ese-1fyjvdk");
attr(button, "type", "button");
attr(div0, "class", "effect-text hover-text svelte-ese-1fyjvdk");
attr(div1, "class", "effect hover-highlight svelte-ese-1fyjvdk");
},
m(target, anchor) {
insert(target, div1, anchor);
append(div1, button);
append(div1, t0);
append(div1, div0);
append(div0, t1);
if (!mounted) {
dispose = [
listen(
button,
"click",
/*endEffect*/
ctx[4]
),
listen(
div1,
"mouseleave",
/*mouseLeave*/
ctx[3]
),
listen(
div1,
"mouseover",
/*mouseOver*/
ctx[2]
)
];
mounted = true;
}
},
p(ctx2, [dirty]) {
if (dirty & /*effect*/
1 && t1_value !== (t1_value = /*getEffectName*/
ctx2[1](
/*effect*/
ctx2[0]
) + ""))
set_data(t1, t1_value);
},
i: noop,
o: noop,
d(detaching) {
if (detaching)
detach(div1);
mounted = false;
run_all(dispose);
}
};
}
function instance$c($$self, $$props, $$invalidate) {
let { effect } = $$props;
function getEffectName(effect2) {
let effectName = "Unknown effect";
if (effect2.data.file) {
effectName = effect2.data.file.split("\\").pop().split("/").pop();
} else if (effect2.data.text) {
effectName = "Text: " + effect2.data.text.text;
} else {
effectName = "Shape: " + effect2.data.shapes[0].type;
}
effectName = effect2.data.name ? `${effect2.data.name} (${effectName})` : effectName;
if (effect2.data.creatorUserId !== game.userId) {
let user_name = game.users.get(effect2.data.creatorUserId)?.name;
let formattedUsername = user_name ? localize("SEQUENCER.ManagerPlayersEffect", { user_name }) : localize("SEQUENCER.ManagerUnknownEffect");
effectName += ` (${formattedUsername})`;
}
return effectName;
}
function mouseOver() {
SelectionManager.hoveredEffectUI = effect;
}
function mouseLeave() {
SelectionManager.hoveredEffectUI = false;
}
function endEffect() {
SequencerEffectManager.endEffects({ effects: [effect] });
}
$$self.$$set = ($$props2) => {
if ("effect" in $$props2)
$$invalidate(0, effect = $$props2.effect);
};
return [effect, getEffectName, mouseOver, mouseLeave, endEffect];
}
class EffectEntry extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance$c, create_fragment$c, safe_not_equal, { effect: 0 });
}
}
const SoundEntry_svelte_svelte_type_style_lang = "";
function create_fragment$b(ctx) {
let div1;
let button;
let t0;
let div0;
let t1_value = (
/*sound*/
ctx[0].src.split("/").slice(-1) + ""
);
let t1;
let mounted;
let dispose;
return {
c() {
div1 = element("div");
button = element("button");
button.innerHTML = ``;
t0 = space();
div0 = element("div");
t1 = text$1(t1_value);
attr(button, "class", "btn_end svelte-ese-1fyjvdk");
attr(button, "type", "button");
attr(div0, "class", "effect-text hover-text svelte-ese-1fyjvdk");
attr(div1, "class", "effect hover-highlight svelte-ese-1fyjvdk");
},
m(target, anchor) {
insert(target, div1, anchor);
append(div1, button);
append(div1, t0);
append(div1, div0);
append(div0, t1);
if (!mounted) {
dispose = listen(
button,
"click",
/*endSound*/
ctx[1]
);
mounted = true;
}
},
p(ctx2, [dirty]) {
if (dirty & /*sound*/
1 && t1_value !== (t1_value = /*sound*/
ctx2[0].src.split("/").slice(-1) + ""))
set_data(t1, t1_value);
},
i: noop,
o: noop,
d(detaching) {
if (detaching)
detach(div1);
mounted = false;
dispose();
}
};
}
function instance$b($$self, $$props, $$invalidate) {
let { id } = $$props;
let { sound } = $$props;
function endSound() {
SequencerAudioHelper.stop([id], true);
}
$$self.$$set = ($$props2) => {
if ("id" in $$props2)
$$invalidate(2, id = $$props2.id);
if ("sound" in $$props2)
$$invalidate(0, sound = $$props2.sound);
};
return [sound, endSound, id];
}
class SoundEntry extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance$b, create_fragment$b, safe_not_equal, { id: 2, sound: 0 });
}
}
const Manager_svelte_svelte_type_style_lang = "";
function get_each_context$3(ctx, list, i) {
const child_ctx = ctx.slice();
child_ctx[10] = list[i][0];
child_ctx[11] = list[i][1];
return child_ctx;
}
function get_each_context_1$1(ctx, list, i) {
const child_ctx = ctx.slice();
child_ctx[14] = list[i];
return child_ctx;
}
function get_each_context_2$1(ctx, list, i) {
const child_ctx = ctx.slice();
child_ctx[14] = list[i];
return child_ctx;
}
function create_if_block_5(ctx) {
let div;
let h2;
return {
c() {
div = element("div");
h2 = element("h2");
h2.textContent = `${localize("SEQUENCER.Manager.NothingPlaying")}`;
attr(div, "class", "no-effects");
},
m(target, anchor) {
insert(target, div, anchor);
append(div, h2);
},
p: noop,
d(detaching) {
if (detaching)
detach(div);
}
};
}
function create_if_block_1(ctx) {
let button;
let t1;
let div;
let t2;
let t3;
let current;
let mounted;
let dispose;
let if_block0 = (
/*persistentEffects*/
ctx[2].length && create_if_block_4(ctx)
);
let if_block1 = (
/*temporaryEffects*/
ctx[1].length && /*persistentEffects*/
ctx[2].length && create_if_block_3()
);
let if_block2 = (
/*temporaryEffects*/
ctx[1].length && create_if_block_2(ctx)
);
return {
c() {
button = element("button");
button.textContent = `${localize("SEQUENCER.Manager.EndAllEffects")}`;
t1 = space();
div = element("div");
if (if_block0)
if_block0.c();
t2 = space();
if (if_block1)
if_block1.c();
t3 = space();
if (if_block2)
if_block2.c();
attr(button, "class", "w-100 end-all-effects mb-2 svelte-ese-nc5j73");
attr(button, "type", "button");
attr(div, "class", "effects svelte-ese-nc5j73");
},
m(target, anchor) {
insert(target, button, anchor);
insert(target, t1, anchor);
insert(target, div, anchor);
if (if_block0)
if_block0.m(div, null);
append(div, t2);
if (if_block1)
if_block1.m(div, null);
append(div, t3);
if (if_block2)
if_block2.m(div, null);
current = true;
if (!mounted) {
dispose = listen(
button,
"click",
/*endAllEffects*/
ctx[6]
);
mounted = true;
}
},
p(ctx2, dirty) {
if (
/*persistentEffects*/
ctx2[2].length
) {
if (if_block0) {
if_block0.p(ctx2, dirty);
if (dirty & /*persistentEffects*/
4) {
transition_in(if_block0, 1);
}
} else {
if_block0 = create_if_block_4(ctx2);
if_block0.c();
transition_in(if_block0, 1);
if_block0.m(div, t2);
}
} else if (if_block0) {
group_outros();
transition_out(if_block0, 1, 1, () => {
if_block0 = null;
});
check_outros();
}
if (
/*temporaryEffects*/
ctx2[1].length && /*persistentEffects*/
ctx2[2].length
) {
if (if_block1)
;
else {
if_block1 = create_if_block_3();
if_block1.c();
if_block1.m(div, t3);
}
} else if (if_block1) {
if_block1.d(1);
if_block1 = null;
}
if (
/*temporaryEffects*/
ctx2[1].length
) {
if (if_block2) {
if_block2.p(ctx2, dirty);
if (dirty & /*temporaryEffects*/
2) {
transition_in(if_block2, 1);
}
} else {
if_block2 = create_if_block_2(ctx2);
if_block2.c();
transition_in(if_block2, 1);
if_block2.m(div, null);
}
} else if (if_block2) {
group_outros();
transition_out(if_block2, 1, 1, () => {
if_block2 = null;
});
check_outros();
}
},
i(local) {
if (current)
return;
transition_in(if_block0);
transition_in(if_block2);
current = true;
},
o(local) {
transition_out(if_block0);
transition_out(if_block2);
current = false;
},
d(detaching) {
if (detaching)
detach(button);
if (detaching)
detach(t1);
if (detaching)
detach(div);
if (if_block0)
if_block0.d();
if (if_block1)
if_block1.d();
if (if_block2)
if_block2.d();
mounted = false;
dispose();
}
};
}
function create_if_block_4(ctx) {
let h2;
let t1;
let div;
let each_blocks = [];
let each_1_lookup = /* @__PURE__ */ new Map();
let current;
let each_value_2 = (
/*persistentEffects*/
ctx[2]
);
const get_key = (ctx2) => (
/*effect*/
ctx2[14].id
);
for (let i = 0; i < each_value_2.length; i += 1) {
let child_ctx = get_each_context_2$1(ctx, each_value_2, i);
let key = get_key(child_ctx);
each_1_lookup.set(key, each_blocks[i] = create_each_block_2$1(key, child_ctx));
}
return {
c() {
h2 = element("h2");
h2.textContent = `${localize("SEQUENCER.Manager.PersistentEffects")}`;
t1 = space();
div = element("div");
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].c();
}
},
m(target, anchor) {
insert(target, h2, anchor);
insert(target, t1, anchor);
insert(target, div, anchor);
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].m(div, null);
}
current = true;
},
p(ctx2, dirty) {
if (dirty & /*persistentEffects*/
4) {
each_value_2 = /*persistentEffects*/
ctx2[2];
group_outros();
each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx2, each_value_2, each_1_lookup, div, outro_and_destroy_block, create_each_block_2$1, null, get_each_context_2$1);
check_outros();
}
},
i(local) {
if (current)
return;
for (let i = 0; i < each_value_2.length; i += 1) {
transition_in(each_blocks[i]);
}
current = true;
},
o(local) {
for (let i = 0; i < each_blocks.length; i += 1) {
transition_out(each_blocks[i]);
}
current = false;
},
d(detaching) {
if (detaching)
detach(h2);
if (detaching)
detach(t1);
if (detaching)
detach(div);
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].d();
}
}
};
}
function create_each_block_2$1(key_1, ctx) {
let first;
let effectentry;
let current;
effectentry = new EffectEntry({ props: { effect: (
/*effect*/
ctx[14]
) } });
return {
key: key_1,
first: null,
c() {
first = empty();
create_component(effectentry.$$.fragment);
this.first = first;
},
m(target, anchor) {
insert(target, first, anchor);
mount_component(effectentry, target, anchor);
current = true;
},
p(new_ctx, dirty) {
ctx = new_ctx;
const effectentry_changes = {};
if (dirty & /*persistentEffects*/
4)
effectentry_changes.effect = /*effect*/
ctx[14];
effectentry.$set(effectentry_changes);
},
i(local) {
if (current)
return;
transition_in(effectentry.$$.fragment, local);
current = true;
},
o(local) {
transition_out(effectentry.$$.fragment, local);
current = false;
},
d(detaching) {
if (detaching)
detach(first);
destroy_component(effectentry, detaching);
}
};
}
function create_if_block_3(ctx) {
let hr;
return {
c() {
hr = element("hr");
},
m(target, anchor) {
insert(target, hr, anchor);
},
d(detaching) {
if (detaching)
detach(hr);
}
};
}
function create_if_block_2(ctx) {
let h2;
let t1;
let div;
let each_blocks = [];
let each_1_lookup = /* @__PURE__ */ new Map();
let current;
let each_value_1 = (
/*temporaryEffects*/
ctx[1]
);
const get_key = (ctx2) => (
/*effect*/
ctx2[14].id
);
for (let i = 0; i < each_value_1.length; i += 1) {
let child_ctx = get_each_context_1$1(ctx, each_value_1, i);
let key = get_key(child_ctx);
each_1_lookup.set(key, each_blocks[i] = create_each_block_1$1(key, child_ctx));
}
return {
c() {
h2 = element("h2");
h2.textContent = `${localize("SEQUENCER.Manager.TemporaryEffects")}`;
t1 = space();
div = element("div");
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].c();
}
},
m(target, anchor) {
insert(target, h2, anchor);
insert(target, t1, anchor);
insert(target, div, anchor);
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].m(div, null);
}
current = true;
},
p(ctx2, dirty) {
if (dirty & /*temporaryEffects*/
2) {
each_value_1 = /*temporaryEffects*/
ctx2[1];
group_outros();
each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx2, each_value_1, each_1_lookup, div, outro_and_destroy_block, create_each_block_1$1, null, get_each_context_1$1);
check_outros();
}
},
i(local) {
if (current)
return;
for (let i = 0; i < each_value_1.length; i += 1) {
transition_in(each_blocks[i]);
}
current = true;
},
o(local) {
for (let i = 0; i < each_blocks.length; i += 1) {
transition_out(each_blocks[i]);
}
current = false;
},
d(detaching) {
if (detaching)
detach(h2);
if (detaching)
detach(t1);
if (detaching)
detach(div);
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].d();
}
}
};
}
function create_each_block_1$1(key_1, ctx) {
let first;
let effectentry;
let current;
effectentry = new EffectEntry({ props: { effect: (
/*effect*/
ctx[14]
) } });
return {
key: key_1,
first: null,
c() {
first = empty();
create_component(effectentry.$$.fragment);
this.first = first;
},
m(target, anchor) {
insert(target, first, anchor);
mount_component(effectentry, target, anchor);
current = true;
},
p(new_ctx, dirty) {
ctx = new_ctx;
const effectentry_changes = {};
if (dirty & /*temporaryEffects*/
2)
effectentry_changes.effect = /*effect*/
ctx[14];
effectentry.$set(effectentry_changes);
},
i(local) {
if (current)
return;
transition_in(effectentry.$$.fragment, local);
current = true;
},
o(local) {
transition_out(effectentry.$$.fragment, local);
current = false;
},
d(detaching) {
if (detaching)
detach(first);
destroy_component(effectentry, detaching);
}
};
}
function create_if_block$2(ctx) {
let button;
let t1;
let div1;
let h2;
let t3;
let div0;
let each_blocks = [];
let each_1_lookup = /* @__PURE__ */ new Map();
let current;
let mounted;
let dispose;
let each_value = (
/*sounds*/
ctx[3]
);
const get_key = (ctx2) => (
/*id*/
ctx2[10]
);
for (let i = 0; i < each_value.length; i += 1) {
let child_ctx = get_each_context$3(ctx, each_value, i);
let key = get_key(child_ctx);
each_1_lookup.set(key, each_blocks[i] = create_each_block$3(key, child_ctx));
}
return {
c() {
button = element("button");
button.textContent = `${localize("SEQUENCER.Manager.EndAllSounds")}`;
t1 = space();
div1 = element("div");
h2 = element("h2");
h2.textContent = `${localize("SEQUENCER.Manager.Sounds")}`;
t3 = space();
div0 = element("div");
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].c();
}
attr(button, "class", "w-100 end-all-effects mb-2 svelte-ese-nc5j73");
attr(button, "type", "button");
},
m(target, anchor) {
insert(target, button, anchor);
insert(target, t1, anchor);
insert(target, div1, anchor);
append(div1, h2);
append(div1, t3);
append(div1, div0);
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].m(div0, null);
}
current = true;
if (!mounted) {
dispose = listen(
button,
"click",
/*endAllSounds*/
ctx[7]
);
mounted = true;
}
},
p(ctx2, dirty) {
if (dirty & /*sounds*/
8) {
each_value = /*sounds*/
ctx2[3];
group_outros();
each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx2, each_value, each_1_lookup, div0, outro_and_destroy_block, create_each_block$3, null, get_each_context$3);
check_outros();
}
},
i(local) {
if (current)
return;
for (let i = 0; i < each_value.length; i += 1) {
transition_in(each_blocks[i]);
}
current = true;
},
o(local) {
for (let i = 0; i < each_blocks.length; i += 1) {
transition_out(each_blocks[i]);
}
current = false;
},
d(detaching) {
if (detaching)
detach(button);
if (detaching)
detach(t1);
if (detaching)
detach(div1);
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].d();
}
mounted = false;
dispose();
}
};
}
function create_each_block$3(key_1, ctx) {
let first;
let soundentry;
let current;
soundentry = new SoundEntry({
props: {
id: (
/*id*/
ctx[10]
),
sound: (
/*sound*/
ctx[11]
)
}
});
return {
key: key_1,
first: null,
c() {
first = empty();
create_component(soundentry.$$.fragment);
this.first = first;
},
m(target, anchor) {
insert(target, first, anchor);
mount_component(soundentry, target, anchor);
current = true;
},
p(new_ctx, dirty) {
ctx = new_ctx;
const soundentry_changes = {};
if (dirty & /*sounds*/
8)
soundentry_changes.id = /*id*/
ctx[10];
if (dirty & /*sounds*/
8)
soundentry_changes.sound = /*sound*/
ctx[11];
soundentry.$set(soundentry_changes);
},
i(local) {
if (current)
return;
transition_in(soundentry.$$.fragment, local);
current = true;
},
o(local) {
transition_out(soundentry.$$.fragment, local);
current = false;
},
d(detaching) {
if (detaching)
detach(first);
destroy_component(soundentry, detaching);
}
};
}
function create_fragment$a(ctx) {
let div;
let t0;
let t1;
let current;
let if_block0 = !/*effects*/
ctx[0].length && !/*sounds*/
ctx[3].length && create_if_block_5();
let if_block1 = (
/*effects*/
ctx[0].length && create_if_block_1(ctx)
);
let if_block2 = (
/*sounds*/
ctx[3].length && create_if_block$2(ctx)
);
return {
c() {
div = element("div");
if (if_block0)
if_block0.c();
t0 = space();
if (if_block1)
if_block1.c();
t1 = space();
if (if_block2)
if_block2.c();
attr(div, "class", "effects-container svelte-ese-nc5j73");
},
m(target, anchor) {
insert(target, div, anchor);
if (if_block0)
if_block0.m(div, null);
append(div, t0);
if (if_block1)
if_block1.m(div, null);
append(div, t1);
if (if_block2)
if_block2.m(div, null);
current = true;
},
p(ctx2, [dirty]) {
if (!/*effects*/
ctx2[0].length && !/*sounds*/
ctx2[3].length) {
if (if_block0) {
if_block0.p(ctx2, dirty);
} else {
if_block0 = create_if_block_5();
if_block0.c();
if_block0.m(div, t0);
}
} else if (if_block0) {
if_block0.d(1);
if_block0 = null;
}
if (
/*effects*/
ctx2[0].length
) {
if (if_block1) {
if_block1.p(ctx2, dirty);
if (dirty & /*effects*/
1) {
transition_in(if_block1, 1);
}
} else {
if_block1 = create_if_block_1(ctx2);
if_block1.c();
transition_in(if_block1, 1);
if_block1.m(div, t1);
}
} else if (if_block1) {
group_outros();
transition_out(if_block1, 1, 1, () => {
if_block1 = null;
});
check_outros();
}
if (
/*sounds*/
ctx2[3].length
) {
if (if_block2) {
if_block2.p(ctx2, dirty);
if (dirty & /*sounds*/
8) {
transition_in(if_block2, 1);
}
} else {
if_block2 = create_if_block$2(ctx2);
if_block2.c();
transition_in(if_block2, 1);
if_block2.m(div, null);
}
} else if (if_block2) {
group_outros();
transition_out(if_block2, 1, 1, () => {
if_block2 = null;
});
check_outros();
}
},
i(local) {
if (current)
return;
transition_in(if_block1);
transition_in(if_block2);
current = true;
},
o(local) {
transition_out(if_block1);
transition_out(if_block2);
current = false;
},
d(detaching) {
if (detaching)
detach(div);
if (if_block0)
if_block0.d();
if (if_block1)
if_block1.d();
if (if_block2)
if_block2.d();
}
};
}
function instance$a($$self, $$props, $$invalidate) {
let effects;
let sounds;
let persistentEffects;
let temporaryEffects;
let $RunningSounds;
let $VisibleEffects;
const VisibleEffects = SequenceManager.VisibleEffects;
component_subscribe($$self, VisibleEffects, (value) => $$invalidate(9, $VisibleEffects = value));
const RunningSounds = SequenceManager.RunningSounds;
component_subscribe($$self, RunningSounds, (value) => $$invalidate(8, $RunningSounds = value));
function endAllEffects() {
Sequencer.EffectManager.endEffects({
effects: effects.filter((effect) => effect.userCanDelete && !effect.data.private)
});
}
function endAllSounds() {
SequencerAudioHelper.stop(RunningSounds.keys());
}
$$self.$$.update = () => {
if ($$self.$$.dirty & /*$VisibleEffects*/
512) {
$$invalidate(0, effects = Object.values($VisibleEffects));
}
if ($$self.$$.dirty & /*$RunningSounds*/
256) {
$$invalidate(3, sounds = Object.entries($RunningSounds));
}
if ($$self.$$.dirty & /*effects*/
1) {
$$invalidate(2, persistentEffects = effects.filter((effect) => effect.data.persist));
}
if ($$self.$$.dirty & /*effects*/
1) {
$$invalidate(1, temporaryEffects = effects.filter((effect) => !effect.data.persist));
}
};
return [
effects,
temporaryEffects,
persistentEffects,
sounds,
VisibleEffects,
RunningSounds,
endAllEffects,
endAllSounds,
$RunningSounds,
$VisibleEffects
];
}
class Manager extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance$a, create_fragment$a, safe_not_equal, {});
}
}
const SliderInput_svelte_svelte_type_style_lang = "";
function create_fragment$9(ctx) {
let div;
let label;
let t0_value = localize(
/*setting*/
ctx[0].label
) + "";
let t0;
let t1;
let input0;
let t2;
let input1;
let applyStyles_action;
let mounted;
let dispose;
return {
c() {
div = element("div");
label = element("label");
t0 = text$1(t0_value);
t1 = space();
input0 = element("input");
t2 = space();
input1 = element("input");
attr(label, "class", "svelte-ese-x3192w");
input0.disabled = /*$isLocked*/
ctx[6];
attr(
input0,
"max",
/*max*/
ctx[2]
);
attr(
input0,
"min",
/*min*/
ctx[1]
);
attr(
input0,
"step",
/*step*/
ctx[4]
);
attr(input0, "type", "range");
attr(input0, "class", "svelte-ese-x3192w");
input1.disabled = /*$isLocked*/
ctx[6];
attr(
input1,
"max",
/*maxInput*/
ctx[3]
);
attr(
input1,
"min",
/*min*/
ctx[1]
);
input1.required = true;
attr(
input1,
"step",
/*step*/
ctx[4]
);
attr(input1, "type", "number");
attr(input1, "class", "svelte-ese-x3192w");
set_style(div, "display", "flex");
set_style(div, "align-items", "center");
},
m(target, anchor) {
insert(target, div, anchor);
append(div, label);
append(label, t0);
append(div, t1);
append(div, input0);
set_input_value(
input0,
/*$store*/
ctx[7]
);
append(div, t2);
append(div, input1);
set_input_value(
input1,
/*$store*/
ctx[7]
);
if (!mounted) {
dispose = [
listen(
input0,
"change",
/*input0_change_input_handler*/
ctx[11]
),
listen(
input0,
"input",
/*input0_change_input_handler*/
ctx[11]
),
listen(
input1,
"input",
/*input1_input_handler*/
ctx[12]
),
action_destroyer(applyStyles_action = applyStyles.call(
null,
div,
/*styles*/
ctx[5]
))
];
mounted = true;
}
},
p(ctx2, [dirty]) {
if (dirty & /*setting*/
1 && t0_value !== (t0_value = localize(
/*setting*/
ctx2[0].label
) + ""))
set_data(t0, t0_value);
if (dirty & /*$isLocked*/
64) {
input0.disabled = /*$isLocked*/
ctx2[6];
}
if (dirty & /*max*/
4) {
attr(
input0,
"max",
/*max*/
ctx2[2]
);
}
if (dirty & /*min*/
2) {
attr(
input0,
"min",
/*min*/
ctx2[1]
);
}
if (dirty & /*step*/
16) {
attr(
input0,
"step",
/*step*/
ctx2[4]
);
}
if (dirty & /*$store*/
128) {
set_input_value(
input0,
/*$store*/
ctx2[7]
);
}
if (dirty & /*$isLocked*/
64) {
input1.disabled = /*$isLocked*/
ctx2[6];
}
if (dirty & /*maxInput*/
8) {
attr(
input1,
"max",
/*maxInput*/
ctx2[3]
);
}
if (dirty & /*min*/
2) {
attr(
input1,
"min",
/*min*/
ctx2[1]
);
}
if (dirty & /*step*/
16) {
attr(
input1,
"step",
/*step*/
ctx2[4]
);
}
if (dirty & /*$store*/
128 && to_number(input1.value) !== /*$store*/
ctx2[7]) {
set_input_value(
input1,
/*$store*/
ctx2[7]
);
}
if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*styles*/
32)
applyStyles_action.update.call(
null,
/*styles*/
ctx2[5]
);
},
i: noop,
o: noop,
d(detaching) {
if (detaching)
detach(div);
mounted = false;
run_all(dispose);
}
};
}
function instance$9($$self, $$props, $$invalidate) {
let $isLocked;
let $store;
let { setting } = $$props;
let { lock = false } = $$props;
let { min = 0.1 } = $$props;
let { max = 2 } = $$props;
let { maxInput = Infinity } = $$props;
let { step = 0.01 } = $$props;
let { styles = {} } = $$props;
const store = setting.store;
component_subscribe($$self, store, (value) => $$invalidate(7, $store = value));
const isLocked = lock ? lock.store : writable$1(false);
component_subscribe($$self, isLocked, (value) => $$invalidate(6, $isLocked = value));
function input0_change_input_handler() {
$store = to_number(this.value);
store.set($store);
}
function input1_input_handler() {
$store = to_number(this.value);
store.set($store);
}
$$self.$$set = ($$props2) => {
if ("setting" in $$props2)
$$invalidate(0, setting = $$props2.setting);
if ("lock" in $$props2)
$$invalidate(10, lock = $$props2.lock);
if ("min" in $$props2)
$$invalidate(1, min = $$props2.min);
if ("max" in $$props2)
$$invalidate(2, max = $$props2.max);
if ("maxInput" in $$props2)
$$invalidate(3, maxInput = $$props2.maxInput);
if ("step" in $$props2)
$$invalidate(4, step = $$props2.step);
if ("styles" in $$props2)
$$invalidate(5, styles = $$props2.styles);
};
return [
setting,
min,
max,
maxInput,
step,
styles,
$isLocked,
$store,
store,
isLocked,
lock,
input0_change_input_handler,
input1_input_handler
];
}
class SliderInput extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance$9, create_fragment$9, safe_not_equal, {
setting: 0,
lock: 10,
min: 1,
max: 2,
maxInput: 3,
step: 4,
styles: 5
});
}
}
function create_fragment$8(ctx) {
let div;
let input;
let input_disabled_value;
let t0;
let label_1;
let t1_value = localize(
/*setting*/
ctx[0].label
) + "";
let t1;
let applyStyles_action;
let mounted;
let dispose;
return {
c() {
div = element("div");
input = element("input");
t0 = space();
label_1 = element("label");
t1 = text$1(t1_value);
attr(
input,
"id",
/*id*/
ctx[5]
);
input.disabled = input_disabled_value = /*$isLocked*/
ctx[3] !== /*inverse*/
ctx[1];
set_style(input, "height", "15px");
attr(input, "type", "checkbox");
attr(
label_1,
"for",
/*id*/
ctx[5]
);
set_style(div, "display", "flex");
set_style(div, "align-items", "center");
},
m(target, anchor) {
insert(target, div, anchor);
append(div, input);
input.checked = /*$store*/
ctx[4];
append(div, t0);
append(div, label_1);
append(label_1, t1);
if (!mounted) {
dispose = [
listen(
input,
"change",
/*input_change_handler*/
ctx[10]
),
action_destroyer(applyStyles_action = applyStyles.call(
null,
div,
/*styles*/
ctx[2]
))
];
mounted = true;
}
},
p(ctx2, [dirty]) {
if (dirty & /*$isLocked, inverse*/
10 && input_disabled_value !== (input_disabled_value = /*$isLocked*/
ctx2[3] !== /*inverse*/
ctx2[1])) {
input.disabled = input_disabled_value;
}
if (dirty & /*$store*/
16) {
input.checked = /*$store*/
ctx2[4];
}
if (dirty & /*setting*/
1 && t1_value !== (t1_value = localize(
/*setting*/
ctx2[0].label
) + ""))
set_data(t1, t1_value);
if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*styles*/
4)
applyStyles_action.update.call(
null,
/*styles*/
ctx2[2]
);
},
i: noop,
o: noop,
d(detaching) {
if (detaching)
detach(div);
mounted = false;
run_all(dispose);
}
};
}
function instance$8($$self, $$props, $$invalidate) {
let $store;
let $isLocked;
let { setting } = $$props;
let { label = false } = $$props;
let { lock = false } = $$props;
let { inverse = false } = $$props;
let { styles = {} } = $$props;
const id = "sequencer-input-" + randomID();
const store = setting.store;
component_subscribe($$self, store, (value) => $$invalidate(4, $store = value));
const isLocked = lock ? lock.store : writable$1(inverse);
component_subscribe($$self, isLocked, (value) => $$invalidate(3, $isLocked = value));
function input_change_handler() {
$store = this.checked;
store.set($store);
}
$$self.$$set = ($$props2) => {
if ("setting" in $$props2)
$$invalidate(0, setting = $$props2.setting);
if ("label" in $$props2)
$$invalidate(8, label = $$props2.label);
if ("lock" in $$props2)
$$invalidate(9, lock = $$props2.lock);
if ("inverse" in $$props2)
$$invalidate(1, inverse = $$props2.inverse);
if ("styles" in $$props2)
$$invalidate(2, styles = $$props2.styles);
};
$$self.$$.update = () => {
if ($$self.$$.dirty & /*$isLocked, inverse*/
10) {
{
if ($isLocked !== inverse) {
set_store_value(store, $store = false, $store);
}
}
}
};
return [
setting,
inverse,
styles,
$isLocked,
$store,
id,
store,
isLocked,
label,
lock,
input_change_handler
];
}
class Checkbox extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance$8, create_fragment$8, safe_not_equal, {
setting: 0,
label: 8,
lock: 9,
inverse: 1,
styles: 2
});
}
}
const NumberInput_svelte_svelte_type_style_lang = "";
function create_if_block$1(ctx) {
let label;
let t_value = localize(
/*setting*/
ctx[0].label
) + "";
let t;
return {
c() {
label = element("label");
t = text$1(t_value);
attr(
label,
"for",
/*id*/
ctx[5]
);
attr(label, "class", "svelte-ese-ywsxq0");
},
m(target, anchor) {
insert(target, label, anchor);
append(label, t);
},
p(ctx2, dirty) {
if (dirty & /*setting*/
1 && t_value !== (t_value = localize(
/*setting*/
ctx2[0].label
) + ""))
set_data(t, t_value);
},
d(detaching) {
if (detaching)
detach(label);
}
};
}
function create_fragment$7(ctx) {
let div;
let show_if = localize(
/*setting*/
ctx[0].label
);
let t;
let input;
let input_disabled_value;
let applyStyles_action;
let mounted;
let dispose;
let if_block = show_if && create_if_block$1(ctx);
return {
c() {
div = element("div");
if (if_block)
if_block.c();
t = space();
input = element("input");
attr(
input,
"id",
/*id*/
ctx[5]
);
attr(input, "type", "number");
input.disabled = input_disabled_value = /*$isLocked*/
ctx[3] !== /*inverse*/
ctx[1];
attr(input, "class", "svelte-ese-ywsxq0");
set_style(div, "display", "flex");
set_style(div, "align-items", "center");
},
m(target, anchor) {
insert(target, div, anchor);
if (if_block)
if_block.m(div, null);
append(div, t);
append(div, input);
set_input_value(
input,
/*$store*/
ctx[4]
);
if (!mounted) {
dispose = [
listen(
input,
"input",
/*input_input_handler*/
ctx[9]
),
listen(
input,
"change",
/*change_handler*/
ctx[10]
),
action_destroyer(applyStyles_action = applyStyles.call(
null,
div,
/*styles*/
ctx[2]
))
];
mounted = true;
}
},
p(ctx2, [dirty]) {
if (dirty & /*setting*/
1)
show_if = localize(
/*setting*/
ctx2[0].label
);
if (show_if) {
if (if_block) {
if_block.p(ctx2, dirty);
} else {
if_block = create_if_block$1(ctx2);
if_block.c();
if_block.m(div, t);
}
} else if (if_block) {
if_block.d(1);
if_block = null;
}
if (dirty & /*$isLocked, inverse*/
10 && input_disabled_value !== (input_disabled_value = /*$isLocked*/
ctx2[3] !== /*inverse*/
ctx2[1])) {
input.disabled = input_disabled_value;
}
if (dirty & /*$store*/
16 && to_number(input.value) !== /*$store*/
ctx2[4]) {
set_input_value(
input,
/*$store*/
ctx2[4]
);
}
if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*styles*/
4)
applyStyles_action.update.call(
null,
/*styles*/
ctx2[2]
);
},
i: noop,
o: noop,
d(detaching) {
if (detaching)
detach(div);
if (if_block)
if_block.d();
mounted = false;
run_all(dispose);
}
};
}
function instance$7($$self, $$props, $$invalidate) {
let $isLocked;
let $store;
let { setting } = $$props;
let { lock = false } = $$props;
let { inverse = false } = $$props;
let { styles = {} } = $$props;
const id = "sequencer-input-" + randomID();
const store = setting.store;
component_subscribe($$self, store, (value) => $$invalidate(4, $store = value));
const isLocked = lock ? lock.store : writable$1(inverse);
component_subscribe($$self, isLocked, (value) => $$invalidate(3, $isLocked = value));
function input_input_handler() {
$store = to_number(this.value);
store.set($store);
}
const change_handler = () => {
if ($store === null)
set_store_value(store, $store = 0, $store);
};
$$self.$$set = ($$props2) => {
if ("setting" in $$props2)
$$invalidate(0, setting = $$props2.setting);
if ("lock" in $$props2)
$$invalidate(8, lock = $$props2.lock);
if ("inverse" in $$props2)
$$invalidate(1, inverse = $$props2.inverse);
if ("styles" in $$props2)
$$invalidate(2, styles = $$props2.styles);
};
return [
setting,
inverse,
styles,
$isLocked,
$store,
id,
store,
isLocked,
lock,
input_input_handler,
change_handler
];
}
class NumberInput extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance$7, create_fragment$7, safe_not_equal, {
setting: 0,
lock: 8,
inverse: 1,
styles: 2
});
}
}
const SwitchToggle_svelte_svelte_type_style_lang = "";
function create_fragment$6(ctx) {
let div2;
let div0;
let span0;
let t0_value = localize(
/*setting*/
ctx[0].label_off
) + "";
let t0;
let t1;
let div1;
let span1;
let t2_value = localize(
/*setting*/
ctx[0].label_on
) + "";
let t2;
let applyStyles_action;
let mounted;
let dispose;
return {
c() {
div2 = element("div");
div0 = element("div");
span0 = element("span");
t0 = text$1(t0_value);
t1 = space();
div1 = element("div");
span1 = element("span");
t2 = text$1(t2_value);
attr(div0, "class", "first svelte-ese-o0yoxs");
toggle_class(div0, "active", !/*$store*/
ctx[2]);
attr(div1, "class", "second svelte-ese-o0yoxs");
toggle_class(
div1,
"active",
/*$store*/
ctx[2]
);
set_style(div2, "display", "flex");
set_style(div2, "align-items", "center");
attr(div2, "class", "svelte-ese-o0yoxs");
},
m(target, anchor) {
insert(target, div2, anchor);
append(div2, div0);
append(div0, span0);
append(span0, t0);
append(div2, t1);
append(div2, div1);
append(div1, span1);
append(span1, t2);
if (!mounted) {
dispose = [
listen(
div0,
"click",
/*click_handler*/
ctx[5]
),
listen(
div1,
"click",
/*click_handler_1*/
ctx[6]
),
action_destroyer(applyStyles_action = applyStyles.call(
null,
div2,
/*finalStyles*/
ctx[1]
))
];
mounted = true;
}
},
p(ctx2, [dirty]) {
if (dirty & /*setting*/
1 && t0_value !== (t0_value = localize(
/*setting*/
ctx2[0].label_off
) + ""))
set_data(t0, t0_value);
if (dirty & /*$store*/
4) {
toggle_class(div0, "active", !/*$store*/
ctx2[2]);
}
if (dirty & /*setting*/
1 && t2_value !== (t2_value = localize(
/*setting*/
ctx2[0].label_on
) + ""))
set_data(t2, t2_value);
if (dirty & /*$store*/
4) {
toggle_class(
div1,
"active",
/*$store*/
ctx2[2]
);
}
if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*finalStyles*/
2)
applyStyles_action.update.call(
null,
/*finalStyles*/
ctx2[1]
);
},
i: noop,
o: noop,
d(detaching) {
if (detaching)
detach(div2);
mounted = false;
run_all(dispose);
}
};
}
const width = 5.75;
function instance$6($$self, $$props, $$invalidate) {
let finalStyles;
let $store;
let { setting } = $$props;
let { styles = {} } = $$props;
const store = setting.store;
component_subscribe($$self, store, (value) => $$invalidate(2, $store = value));
const click_handler = () => {
set_store_value(store, $store = false, $store);
};
const click_handler_1 = () => {
set_store_value(store, $store = true, $store);
};
$$self.$$set = ($$props2) => {
if ("setting" in $$props2)
$$invalidate(0, setting = $$props2.setting);
if ("styles" in $$props2)
$$invalidate(4, styles = $$props2.styles);
};
$$self.$$.update = () => {
if ($$self.$$.dirty & /*styles*/
16) {
$$invalidate(1, finalStyles = {
...styles,
"--switch-width": `${width}em`,
"--half-switch-width": `${width - 1.5}em`,
"--half-switch-width-on": `${width - 1.7}em`
});
}
};
return [setting, finalStyles, $store, store, styles, click_handler, click_handler_1];
}
class SwitchToggle extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance$6, create_fragment$6, safe_not_equal, { setting: 0, styles: 4 });
}
}
const Player_svelte_svelte_type_style_lang = "";
function get_each_context$2(ctx, list, i) {
const child_ctx = ctx.slice();
child_ctx[20] = list[i];
return child_ctx;
}
function get_each_context_1(ctx, list, i) {
const child_ctx = ctx.slice();
child_ctx[23] = list[i];
return child_ctx;
}
function get_each_context_2(ctx, list, i) {
const child_ctx = ctx.slice();
child_ctx[26] = list[i];
return child_ctx;
}
function create_each_block_2(ctx) {
let option;
let t_value = (
/*suggestion*/
ctx[26] + ""
);
let t;
let option_value_value;
return {
c() {
option = element("option");
t = text$1(t_value);
option.__value = option_value_value = /*suggestion*/
ctx[26];
option.value = option.__value;
},
m(target, anchor) {
insert(target, option, anchor);
append(option, t);
},
p(ctx2, dirty) {
if (dirty & /*suggestions*/
2 && t_value !== (t_value = /*suggestion*/
ctx2[26] + ""))
set_data(t, t_value);
if (dirty & /*suggestions*/
2 && option_value_value !== (option_value_value = /*suggestion*/
ctx2[26])) {
option.__value = option_value_value;
option.value = option.__value;
}
},
d(detaching) {
if (detaching)
detach(option);
}
};
}
function create_each_block_1(key_1, ctx) {
let option;
let t_value = (
/*user*/
ctx[23].name + ""
);
let t;
return {
key: key_1,
first: null,
c() {
option = element("option");
t = text$1(t_value);
option.__value = /*user*/
ctx[23].id;
option.value = option.__value;
this.first = option;
},
m(target, anchor) {
insert(target, option, anchor);
append(option, t);
},
p(new_ctx, dirty) {
ctx = new_ctx;
},
d(detaching) {
if (detaching)
detach(option);
}
};
}
function create_each_block$2(ctx) {
let option;
let t_value = (
/*preset*/
ctx[20] + ""
);
let t;
return {
c() {
option = element("option");
t = text$1(t_value);
option.__value = /*preset*/
ctx[20];
option.value = option.__value;
},
m(target, anchor) {
insert(target, option, anchor);
append(option, t);
},
p: noop,
d(detaching) {
if (detaching)
detach(option);
}
};
}
function create_fragment$5(ctx) {
let div16;
let fieldset0;
let legend0;
let t1;
let button0;
let t3;
let div0;
let input;
let t4;
let button1;
let t5;
let datalist;
let t6;
let div1;
let label;
let t8;
let select0;
let option0;
let each_blocks_1 = [];
let each1_lookup = /* @__PURE__ */ new Map();
let t10;
let div4;
let div2;
let t12;
let div3;
let select1;
let option1;
let t14;
let button2;
let t15;
let button3;
let i2;
let button3_disabled_value;
let t16;
let button4;
let i3;
let button4_disabled_value;
let t17;
let fieldset1;
let legend1;
let t19;
let div11;
let sliderinput0;
let t20;
let numberinput0;
let t21;
let numberinput1;
let t22;
let div5;
let t23;
let sliderinput1;
let t24;
let div6;
let t25;
let checkbox0;
let t26;
let div7;
let t27;
let div8;
let t28;
let checkbox1;
let t29;
let checkbox2;
let t30;
let checkbox3;
let t31;
let checkbox4;
let t32;
let div9;
let t33;
let numberinput2;
let t34;
let numberinput3;
let t35;
let checkbox5;
let t36;
let numberinput4;
let t37;
let checkbox6;
let t38;
let div10;
let t39;
let fieldset2;
let legend2;
let t41;
let div15;
let numberinput5;
let t42;
let numberinput6;
let t43;
let div12;
let t44;
let checkbox7;
let t45;
let numberinput7;
let t46;
let numberinput8;
let t47;
let numberinput9;
let t48;
let div13;
let t49;
let switchtoggle;
let t50;
let numberinput10;
let t51;
let div14;
let t52;
let checkbox8;
let t53;
let checkbox9;
let t54;
let checkbox10;
let t55;
let checkbox11;
let t56;
let checkbox12;
let current;
let mounted;
let dispose;
let each_value_2 = (
/*suggestions*/
ctx[1]
);
let each_blocks_2 = [];
for (let i = 0; i < each_value_2.length; i += 1) {
each_blocks_2[i] = create_each_block_2(get_each_context_2(ctx, each_value_2, i));
}
let each_value_1 = (
/*users*/
ctx[5]
);
const get_key = (ctx2) => (
/*user*/
ctx2[23].id
);
for (let i = 0; i < each_value_1.length; i += 1) {
let child_ctx = get_each_context_1(ctx, each_value_1, i);
let key = get_key(child_ctx);
each1_lookup.set(key, each_blocks_1[i] = create_each_block_1(key, child_ctx));
}
let each_value = (
/*presets*/
ctx[8]
);
let each_blocks = [];
for (let i = 0; i < each_value.length; i += 1) {
each_blocks[i] = create_each_block$2(get_each_context$2(ctx, each_value, i));
}
sliderinput0 = new SliderInput({
props: {
setting: PlayerSettings.scale,
styles: { "grid-column": `1 / 5` }
}
});
numberinput0 = new NumberInput({
props: {
setting: PlayerSettings.scaleIn,
styles: { "grid-column": `1 / 3` }
}
});
numberinput1 = new NumberInput({
props: {
setting: PlayerSettings.scaleOut,
styles: { "grid-column": `3 / 5` }
}
});
sliderinput1 = new SliderInput({
props: {
setting: PlayerSettings.rotation,
lock: PlayerSettings.randomRotation,
min: "-180",
max: "180",
styles: { "grid-column": `1 / 5` }
}
});
checkbox0 = new Checkbox({
props: {
setting: PlayerSettings.randomRotation,
styles: { "grid-column": `2 / 4` }
}
});
checkbox1 = new Checkbox({
props: {
setting: PlayerSettings.mirrorX,
styles: { "grid-column": `1 / 3` }
}
});
checkbox2 = new Checkbox({
props: {
setting: PlayerSettings.mirrorY,
styles: { "grid-column": `3 / 5` }
}
});
checkbox3 = new Checkbox({
props: {
setting: PlayerSettings.randomMirrorX,
styles: { "grid-column": `1 / 3` },
lock: PlayerSettings.mirrorX,
inverse: true
}
});
checkbox4 = new Checkbox({
props: {
setting: PlayerSettings.randomMirrorY,
styles: { "grid-column": `3 / 5` },
lock: PlayerSettings.mirrorY,
inverse: true
}
});
numberinput2 = new NumberInput({
props: {
setting: PlayerSettings.offsetX,
lock: PlayerSettings.randomOffset,
styles: { "grid-column": `1 / 3` }
}
});
numberinput3 = new NumberInput({
props: {
setting: PlayerSettings.offsetY,
lock: PlayerSettings.randomOffset,
styles: { "grid-column": `3 / 5` }
}
});
checkbox5 = new Checkbox({
props: {
setting: PlayerSettings.randomOffset,
styles: { "grid-column": `1 / 3` }
}
});
numberinput4 = new NumberInput({
props: {
setting: PlayerSettings.randomOffsetAmount,
lock: PlayerSettings.randomOffset,
inverse: true,
styles: { "grid-column": `3 / 5` }
}
});
checkbox6 = new Checkbox({
props: {
setting: PlayerSettings.offsetGridUnits,
styles: { "grid-column": `2 / 5` }
}
});
numberinput5 = new NumberInput({
props: {
setting: PlayerSettings.fadeIn,
styles: { "grid-column": `1 / 3` }
}
});
numberinput6 = new NumberInput({
props: {
setting: PlayerSettings.fadeOut,
styles: { "grid-column": `3 / 5` }
}
});
checkbox7 = new Checkbox({
props: {
setting: PlayerSettings.repeat,
styles: { "grid-column": `1 / 3` }
}
});
numberinput7 = new NumberInput({
props: {
setting: PlayerSettings.repetitions,
lock: PlayerSettings.repeat,
inverse: true,
styles: { "grid-column": `3 / 5` }
}
});
numberinput8 = new NumberInput({
props: {
setting: PlayerSettings.repeatDelayMin,
lock: PlayerSettings.repeat,
inverse: true,
styles: { "grid-column": `3 / 4` }
}
});
numberinput9 = new NumberInput({
props: {
setting: PlayerSettings.repeatDelayMax,
lock: PlayerSettings.repeat,
inverse: true,
styles: { "grid-column": `4 / 5` }
}
});
switchtoggle = new SwitchToggle({
props: {
setting: PlayerSettings.moveTowards,
styles: { "grid-column": `1 / 3` }
}
});
numberinput10 = new NumberInput({
props: {
setting: PlayerSettings.moveSpeed,
lock: PlayerSettings.moveTowards,
inverse: true,
styles: { "grid-column": `3 / 5` }
}
});
checkbox8 = new Checkbox({
props: {
setting: PlayerSettings.attachTo,
styles: { "grid-column": `1 / 5` }
}
});
checkbox9 = new Checkbox({
props: {
setting: PlayerSettings.stretchToAttach,
styles: { "grid-column": `1 / 5` }
}
});
checkbox10 = new Checkbox({
props: {
setting: PlayerSettings.snapToGrid,
styles: { "grid-column": `1 / 5` }
}
});
checkbox11 = new Checkbox({
props: {
setting: PlayerSettings.persist,
styles: { "grid-column": `1 / 5` }
}
});
checkbox12 = new Checkbox({
props: {
setting: PlayerSettings.belowTokens,
styles: { "grid-column": `1 / 5` }
}
});
return {
c() {
div16 = element("div");
fieldset0 = element("fieldset");
legend0 = element("legend");
legend0.textContent = "Effect";
t1 = space();
button0 = element("button");
button0.textContent = `${localize("SEQUENCER.Player.SwitchToLayer")}`;
t3 = space();
div0 = element("div");
input = element("input");
t4 = space();
button1 = element("button");
button1.innerHTML = ``;
t5 = space();
datalist = element("datalist");
for (let i = 0; i < each_blocks_2.length; i += 1) {
each_blocks_2[i].c();
}
t6 = space();
div1 = element("div");
label = element("label");
label.textContent = "Play for users:";
t8 = space();
select0 = element("select");
option0 = element("option");
option0.textContent = `${localize("SEQUENCER.Player.AllUsers")}`;
for (let i = 0; i < each_blocks_1.length; i += 1) {
each_blocks_1[i].c();
}
t10 = space();
div4 = element("div");
div2 = element("div");
div2.textContent = `${localize("SEQUENCER.Player.Presets")}`;
t12 = space();
div3 = element("div");
select1 = element("select");
option1 = element("option");
option1.textContent = `${localize("SEQUENCER.Player.PresetsDefault")}`;
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].c();
}
t14 = space();
button2 = element("button");
button2.innerHTML = ``;
t15 = space();
button3 = element("button");
i2 = element("i");
t16 = space();
button4 = element("button");
i3 = element("i");
t17 = space();
fieldset1 = element("fieldset");
legend1 = element("legend");
legend1.textContent = "Transform";
t19 = space();
div11 = element("div");
create_component(sliderinput0.$$.fragment);
t20 = space();
create_component(numberinput0.$$.fragment);
t21 = space();
create_component(numberinput1.$$.fragment);
t22 = space();
div5 = element("div");
t23 = space();
create_component(sliderinput1.$$.fragment);
t24 = space();
div6 = element("div");
t25 = space();
create_component(checkbox0.$$.fragment);
t26 = space();
div7 = element("div");
t27 = space();
div8 = element("div");
t28 = space();
create_component(checkbox1.$$.fragment);
t29 = space();
create_component(checkbox2.$$.fragment);
t30 = space();
create_component(checkbox3.$$.fragment);
t31 = space();
create_component(checkbox4.$$.fragment);
t32 = space();
div9 = element("div");
t33 = space();
create_component(numberinput2.$$.fragment);
t34 = space();
create_component(numberinput3.$$.fragment);
t35 = space();
create_component(checkbox5.$$.fragment);
t36 = space();
create_component(numberinput4.$$.fragment);
t37 = space();
create_component(checkbox6.$$.fragment);
t38 = space();
div10 = element("div");
t39 = space();
fieldset2 = element("fieldset");
legend2 = element("legend");
legend2.textContent = "Behavior";
t41 = space();
div15 = element("div");
create_component(numberinput5.$$.fragment);
t42 = space();
create_component(numberinput6.$$.fragment);
t43 = space();
div12 = element("div");
t44 = space();
create_component(checkbox7.$$.fragment);
t45 = space();
create_component(numberinput7.$$.fragment);
t46 = space();
create_component(numberinput8.$$.fragment);
t47 = space();
create_component(numberinput9.$$.fragment);
t48 = space();
div13 = element("div");
t49 = space();
create_component(switchtoggle.$$.fragment);
t50 = space();
create_component(numberinput10.$$.fragment);
t51 = space();
div14 = element("div");
t52 = space();
create_component(checkbox8.$$.fragment);
t53 = space();
create_component(checkbox9.$$.fragment);
t54 = space();
create_component(checkbox10.$$.fragment);
t55 = space();
create_component(checkbox11.$$.fragment);
t56 = space();
create_component(checkbox12.$$.fragment);
attr(button0, "class", "activate-layer svelte-ese-1ipnpu1");
attr(button0, "type", "button");
attr(input, "class", "file-input flex3");
attr(input, "list", "dblist");
attr(input, "placeholder", localize("SEQUENCER.Player.PathInput"));
attr(input, "type", "text");
attr(button1, "class", "custom-file-picker small-button svelte-ese-1ipnpu1");
attr(button1, "type", "button");
attr(datalist, "id", "dblist");
attr(div0, "class", "file-settings svelte-ese-1ipnpu1");
attr(label, "for", "user-select");
option0.selected = true;
option0.__value = "all";
option0.value = option0.__value;
attr(select0, "class", "user-select");
attr(select0, "id", "user-select");
select0.multiple = true;
if (
/*$userStore*/
ctx[3] === void 0
)
add_render_callback(() => (
/*select0_change_handler*/
ctx[11].call(select0)
));
attr(div1, "class", "user-settings flexcol svelte-ese-1ipnpu1");
attr(div2, "class", "row w-100");
option1.__value = "default";
option1.value = option1.__value;
attr(select1, "class", "preset-select svelte-ese-1ipnpu1");
if (
/*selectedPreset*/
ctx[2] === void 0
)
add_render_callback(() => (
/*select1_change_handler*/
ctx[12].call(select1)
));
attr(button2, "class", "save-preset small-button svelte-ese-1ipnpu1");
attr(button2, "data-tooltip", "Save Preset");
attr(button2, "type", "button");
attr(i2, "class", "fas fa-copy svelte-ese-1ipnpu1");
attr(button3, "class", "copy-preset small-button svelte-ese-1ipnpu1");
attr(button3, "data-tooltip", "Copy Preset");
button3.disabled = button3_disabled_value = /*selectedPreset*/
ctx[2] === "default";
attr(button3, "type", "button");
attr(i3, "class", "fas fa-times svelte-ese-1ipnpu1");
attr(button4, "class", "delete-preset small-button svelte-ese-1ipnpu1");
attr(button4, "data-tooltip", "Delete Preset");
button4.disabled = button4_disabled_value = /*selectedPreset*/
ctx[2] === "default";
attr(button4, "type", "button");
attr(div3, "class", "preset-container svelte-ese-1ipnpu1");
attr(div4, "class", "flexcol");
attr(div5, "class", "divider");
attr(div8, "class", "divider");
attr(div9, "class", "divider");
attr(div11, "class", "effect-transform-container");
attr(div12, "class", "divider");
attr(div13, "class", "divider");
attr(div14, "class", "divider");
attr(div15, "class", "effect-transform-container");
attr(div16, "class", "effect-player-container");
},
m(target, anchor) {
insert(target, div16, anchor);
append(div16, fieldset0);
append(fieldset0, legend0);
append(fieldset0, t1);
append(fieldset0, button0);
append(fieldset0, t3);
append(fieldset0, div0);
append(div0, input);
set_input_value(
input,
/*$fileStore*/
ctx[0]
);
append(div0, t4);
append(div0, button1);
append(div0, t5);
append(div0, datalist);
for (let i = 0; i < each_blocks_2.length; i += 1) {
each_blocks_2[i].m(datalist, null);
}
append(fieldset0, t6);
append(fieldset0, div1);
append(div1, label);
append(div1, t8);
append(div1, select0);
append(select0, option0);
for (let i = 0; i < each_blocks_1.length; i += 1) {
each_blocks_1[i].m(select0, null);
}
select_options(
select0,
/*$userStore*/
ctx[3]
);
append(fieldset0, t10);
append(fieldset0, div4);
append(div4, div2);
append(div4, t12);
append(div4, div3);
append(div3, select1);
append(select1, option1);
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].m(select1, null);
}
select_option(
select1,
/*selectedPreset*/
ctx[2]
);
append(div3, t14);
append(div3, button2);
append(div3, t15);
append(div3, button3);
append(button3, i2);
append(div3, t16);
append(div3, button4);
append(button4, i3);
append(div16, t17);
append(div16, fieldset1);
append(fieldset1, legend1);
append(fieldset1, t19);
append(fieldset1, div11);
mount_component(sliderinput0, div11, null);
append(div11, t20);
mount_component(numberinput0, div11, null);
append(div11, t21);
mount_component(numberinput1, div11, null);
append(div11, t22);
append(div11, div5);
append(div11, t23);
mount_component(sliderinput1, div11, null);
append(div11, t24);
append(div11, div6);
append(div11, t25);
mount_component(checkbox0, div11, null);
append(div11, t26);
append(div11, div7);
append(div11, t27);
append(div11, div8);
append(div11, t28);
mount_component(checkbox1, div11, null);
append(div11, t29);
mount_component(checkbox2, div11, null);
append(div11, t30);
mount_component(checkbox3, div11, null);
append(div11, t31);
mount_component(checkbox4, div11, null);
append(div11, t32);
append(div11, div9);
append(div11, t33);
mount_component(numberinput2, div11, null);
append(div11, t34);
mount_component(numberinput3, div11, null);
append(div11, t35);
mount_component(checkbox5, div11, null);
append(div11, t36);
mount_component(numberinput4, div11, null);
append(div11, t37);
mount_component(checkbox6, div11, null);
append(div11, t38);
append(div11, div10);
append(div16, t39);
append(div16, fieldset2);
append(fieldset2, legend2);
append(fieldset2, t41);
append(fieldset2, div15);
mount_component(numberinput5, div15, null);
append(div15, t42);
mount_component(numberinput6, div15, null);
append(div15, t43);
append(div15, div12);
append(div15, t44);
mount_component(checkbox7, div15, null);
append(div15, t45);
mount_component(numberinput7, div15, null);
append(div15, t46);
mount_component(numberinput8, div15, null);
append(div15, t47);
mount_component(numberinput9, div15, null);
append(div15, t48);
append(div15, div13);
append(div15, t49);
mount_component(switchtoggle, div15, null);
append(div15, t50);
mount_component(numberinput10, div15, null);
append(div15, t51);
append(div15, div14);
append(div15, t52);
mount_component(checkbox8, div15, null);
append(div15, t53);
mount_component(checkbox9, div15, null);
append(div15, t54);
mount_component(checkbox10, div15, null);
append(div15, t55);
mount_component(checkbox11, div15, null);
append(div15, t56);
mount_component(checkbox12, div15, null);
current = true;
if (!mounted) {
dispose = [
listen(
button0,
"click",
/*click_handler*/
ctx[9]
),
listen(
input,
"input",
/*input_input_handler*/
ctx[10]
),
listen(
button1,
"click",
/*handleClick*/
ctx[7]
),
listen(
select0,
"change",
/*select0_change_handler*/
ctx[11]
),
listen(
select1,
"change",
/*select1_change_handler*/
ctx[12]
),
listen(
select1,
"change",
/*change_handler*/
ctx[13]
),
listen(
button2,
"click",
/*click_handler_1*/
ctx[14]
),
listen(
button3,
"click",
/*click_handler_2*/
ctx[15]
),
listen(
button4,
"click",
/*click_handler_3*/
ctx[16]
)
];
mounted = true;
}
},
p(ctx2, [dirty]) {
if (dirty & /*$fileStore*/
1 && input.value !== /*$fileStore*/
ctx2[0]) {
set_input_value(
input,
/*$fileStore*/
ctx2[0]
);
}
if (dirty & /*suggestions*/
2) {
each_value_2 = /*suggestions*/
ctx2[1];
let i;
for (i = 0; i < each_value_2.length; i += 1) {
const child_ctx = get_each_context_2(ctx2, each_value_2, i);
if (each_blocks_2[i]) {
each_blocks_2[i].p(child_ctx, dirty);
} else {
each_blocks_2[i] = create_each_block_2(child_ctx);
each_blocks_2[i].c();
each_blocks_2[i].m(datalist, null);
}
}
for (; i < each_blocks_2.length; i += 1) {
each_blocks_2[i].d(1);
}
each_blocks_2.length = each_value_2.length;
}
if (dirty & /*users*/
32) {
each_value_1 = /*users*/
ctx2[5];
each_blocks_1 = update_keyed_each(each_blocks_1, dirty, get_key, 1, ctx2, each_value_1, each1_lookup, select0, destroy_block, create_each_block_1, null, get_each_context_1);
}
if (dirty & /*$userStore, users*/
40) {
select_options(
select0,
/*$userStore*/
ctx2[3]
);
}
if (dirty & /*presets*/
256) {
each_value = /*presets*/
ctx2[8];
let i;
for (i = 0; i < each_value.length; i += 1) {
const child_ctx = get_each_context$2(ctx2, each_value, i);
if (each_blocks[i]) {
each_blocks[i].p(child_ctx, dirty);
} else {
each_blocks[i] = create_each_block$2(child_ctx);
each_blocks[i].c();
each_blocks[i].m(select1, null);
}
}
for (; i < each_blocks.length; i += 1) {
each_blocks[i].d(1);
}
each_blocks.length = each_value.length;
}
if (dirty & /*selectedPreset, presets*/
260) {
select_option(
select1,
/*selectedPreset*/
ctx2[2]
);
}
if (!current || dirty & /*selectedPreset, presets*/
260 && button3_disabled_value !== (button3_disabled_value = /*selectedPreset*/
ctx2[2] === "default")) {
button3.disabled = button3_disabled_value;
}
if (!current || dirty & /*selectedPreset, presets*/
260 && button4_disabled_value !== (button4_disabled_value = /*selectedPreset*/
ctx2[2] === "default")) {
button4.disabled = button4_disabled_value;
}
},
i(local) {
if (current)
return;
transition_in(sliderinput0.$$.fragment, local);
transition_in(numberinput0.$$.fragment, local);
transition_in(numberinput1.$$.fragment, local);
transition_in(sliderinput1.$$.fragment, local);
transition_in(checkbox0.$$.fragment, local);
transition_in(checkbox1.$$.fragment, local);
transition_in(checkbox2.$$.fragment, local);
transition_in(checkbox3.$$.fragment, local);
transition_in(checkbox4.$$.fragment, local);
transition_in(numberinput2.$$.fragment, local);
transition_in(numberinput3.$$.fragment, local);
transition_in(checkbox5.$$.fragment, local);
transition_in(numberinput4.$$.fragment, local);
transition_in(checkbox6.$$.fragment, local);
transition_in(numberinput5.$$.fragment, local);
transition_in(numberinput6.$$.fragment, local);
transition_in(checkbox7.$$.fragment, local);
transition_in(numberinput7.$$.fragment, local);
transition_in(numberinput8.$$.fragment, local);
transition_in(numberinput9.$$.fragment, local);
transition_in(switchtoggle.$$.fragment, local);
transition_in(numberinput10.$$.fragment, local);
transition_in(checkbox8.$$.fragment, local);
transition_in(checkbox9.$$.fragment, local);
transition_in(checkbox10.$$.fragment, local);
transition_in(checkbox11.$$.fragment, local);
transition_in(checkbox12.$$.fragment, local);
current = true;
},
o(local) {
transition_out(sliderinput0.$$.fragment, local);
transition_out(numberinput0.$$.fragment, local);
transition_out(numberinput1.$$.fragment, local);
transition_out(sliderinput1.$$.fragment, local);
transition_out(checkbox0.$$.fragment, local);
transition_out(checkbox1.$$.fragment, local);
transition_out(checkbox2.$$.fragment, local);
transition_out(checkbox3.$$.fragment, local);
transition_out(checkbox4.$$.fragment, local);
transition_out(numberinput2.$$.fragment, local);
transition_out(numberinput3.$$.fragment, local);
transition_out(checkbox5.$$.fragment, local);
transition_out(numberinput4.$$.fragment, local);
transition_out(checkbox6.$$.fragment, local);
transition_out(numberinput5.$$.fragment, local);
transition_out(numberinput6.$$.fragment, local);
transition_out(checkbox7.$$.fragment, local);
transition_out(numberinput7.$$.fragment, local);
transition_out(numberinput8.$$.fragment, local);
transition_out(numberinput9.$$.fragment, local);
transition_out(switchtoggle.$$.fragment, local);
transition_out(numberinput10.$$.fragment, local);
transition_out(checkbox8.$$.fragment, local);
transition_out(checkbox9.$$.fragment, local);
transition_out(checkbox10.$$.fragment, local);
transition_out(checkbox11.$$.fragment, local);
transition_out(checkbox12.$$.fragment, local);
current = false;
},
d(detaching) {
if (detaching)
detach(div16);
destroy_each(each_blocks_2, detaching);
for (let i = 0; i < each_blocks_1.length; i += 1) {
each_blocks_1[i].d();
}
destroy_each(each_blocks, detaching);
destroy_component(sliderinput0);
destroy_component(numberinput0);
destroy_component(numberinput1);
destroy_component(sliderinput1);
destroy_component(checkbox0);
destroy_component(checkbox1);
destroy_component(checkbox2);
destroy_component(checkbox3);
destroy_component(checkbox4);
destroy_component(numberinput2);
destroy_component(numberinput3);
destroy_component(checkbox5);
destroy_component(numberinput4);
destroy_component(checkbox6);
destroy_component(numberinput5);
destroy_component(numberinput6);
destroy_component(checkbox7);
destroy_component(numberinput7);
destroy_component(numberinput8);
destroy_component(numberinput9);
destroy_component(switchtoggle);
destroy_component(numberinput10);
destroy_component(checkbox8);
destroy_component(checkbox9);
destroy_component(checkbox10);
destroy_component(checkbox11);
destroy_component(checkbox12);
mounted = false;
run_all(dispose);
}
};
}
let lastInput = "";
async function activateLayer() {
ui.controls.initialize({
control: "sequencer",
tool: "play-effect"
});
canvas.sequencerInterfaceLayer.activate({ tool: "play-effect" });
}
function instance$5($$self, $$props, $$invalidate) {
let $fileStore;
let $userStore;
const fileStore = PlayerSettings.file.store;
component_subscribe($$self, fileStore, (value) => $$invalidate(0, $fileStore = value));
const users2 = game.users.filter((user) => user.active);
const userStore = PlayerSettings.users.store;
component_subscribe($$self, userStore, (value) => $$invalidate(3, $userStore = value));
let lastResults = [];
let suggestions = [];
const searchDebounce = foundry.utils.debounce(
() => {
const fileInput = get_store_value(fileStore).toLowerCase();
if (!fileInput) {
$$invalidate(1, suggestions = SequencerDatabase.publicModules);
return;
}
if (lastInput === fileInput)
return;
let results = SequencerDatabase.searchFor(fileInput);
if (lastResults.equals(results))
return;
lastResults = foundry.utils.duplicate(results);
if (results.length === 1 && results[0].startsWith(fileInput))
return;
if (results.length > 100) {
results = results.slice(0, 100);
}
$$invalidate(1, suggestions = foundry.utils.duplicate(results));
},
200
);
let filePicker = false;
function handleClick() {
if (!filePicker) {
const file = get_store_value(fileStore);
const current = SequencerDatabase.getEntry(file, { softFail: true }) ? file : "";
filePicker = new FilePicker({
type: "imageVideo",
current,
callback: (path) => {
fileStore.set(path);
filePicker = false;
}
});
}
filePicker.render(true, { focus: true });
}
const presets = PlayerSettings.getPresets();
let selectedPreset = "default";
const click_handler = () => activateLayer();
function input_input_handler() {
$fileStore = this.value;
fileStore.set($fileStore);
}
function select0_change_handler() {
$userStore = select_multiple_value(this);
userStore.set($userStore);
$$invalidate(5, users2);
}
function select1_change_handler() {
selectedPreset = select_value(this);
$$invalidate(2, selectedPreset);
$$invalidate(8, presets);
}
const change_handler = () => PlayerSettings.loadPreset(selectedPreset);
const click_handler_1 = () => PlayerSettings.savePreset(selectedPreset);
const click_handler_2 = () => PlayerSettings.copyPreset(selectedPreset);
const click_handler_3 = () => PlayerSettings.deletePreset(selectedPreset);
$$self.$$.update = () => {
if ($$self.$$.dirty & /*$fileStore*/
1) {
searchDebounce($fileStore);
}
};
return [
$fileStore,
suggestions,
selectedPreset,
$userStore,
fileStore,
users2,
userStore,
handleClick,
presets,
click_handler,
input_input_handler,
select0_change_handler,
select1_change_handler,
change_handler,
click_handler_1,
click_handler_2,
click_handler_3
];
}
class Player extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance$5, create_fragment$5, safe_not_equal, {});
}
}
const SequenceStatus_svelte_svelte_type_style_lang = "";
function create_fragment$4(ctx) {
let i0;
let t;
let i1;
return {
c() {
i0 = element("i");
t = space();
i1 = element("i");
attr(i0, "class", "fa-solid background-circle svelte-ese-1wkm0y3");
toggle_class(
i0,
"invisible",
/*$status*/
ctx[1] === CONSTANTS.STATUS.READY
);
toggle_class(
i0,
"fa-arrow-right",
/*$status*/
ctx[1] === CONSTANTS.STATUS.RUNNING || /*$status*/
ctx[1] === CONSTANTS.STATUS.READY
);
toggle_class(
i0,
"fa-check",
/*$status*/
ctx[1] === CONSTANTS.STATUS.COMPLETE
);
toggle_class(
i0,
"fa-arrow-down",
/*$status*/
ctx[1] === CONSTANTS.STATUS.SKIPPED
);
toggle_class(
i0,
"fa-times",
/*$status*/
ctx[1] === CONSTANTS.STATUS.ABORTED
);
attr(i1, "class", "fa-solid ");
},
m(target, anchor) {
insert(target, i0, anchor);
insert(target, t, anchor);
insert(target, i1, anchor);
},
p(ctx2, [dirty]) {
if (dirty & /*$status, CONSTANTS*/
2) {
toggle_class(
i0,
"invisible",
/*$status*/
ctx2[1] === CONSTANTS.STATUS.READY
);
}
if (dirty & /*$status, CONSTANTS*/
2) {
toggle_class(
i0,
"fa-arrow-right",
/*$status*/
ctx2[1] === CONSTANTS.STATUS.RUNNING || /*$status*/
ctx2[1] === CONSTANTS.STATUS.READY
);
}
if (dirty & /*$status, CONSTANTS*/
2) {
toggle_class(
i0,
"fa-check",
/*$status*/
ctx2[1] === CONSTANTS.STATUS.COMPLETE
);
}
if (dirty & /*$status, CONSTANTS*/
2) {
toggle_class(
i0,
"fa-arrow-down",
/*$status*/
ctx2[1] === CONSTANTS.STATUS.SKIPPED
);
}
if (dirty & /*$status, CONSTANTS*/
2) {
toggle_class(
i0,
"fa-times",
/*$status*/
ctx2[1] === CONSTANTS.STATUS.ABORTED
);
}
},
i: noop,
o: noop,
d(detaching) {
if (detaching)
detach(i0);
if (detaching)
detach(t);
if (detaching)
detach(i1);
}
};
}
function instance$4($$self, $$props, $$invalidate) {
let $status, $$unsubscribe_status = noop, $$subscribe_status = () => ($$unsubscribe_status(), $$unsubscribe_status = subscribe(status, ($$value) => $$invalidate(1, $status = $$value)), status);
$$self.$$.on_destroy.push(() => $$unsubscribe_status());
let { status } = $$props;
$$subscribe_status();
$$self.$$set = ($$props2) => {
if ("status" in $$props2)
$$subscribe_status($$invalidate(0, status = $$props2.status));
};
return [status, $status];
}
class SequenceStatus extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance$4, create_fragment$4, safe_not_equal, { status: 0 });
}
}
const SequenceSection_svelte_svelte_type_style_lang = "";
function create_fragment$3(ctx) {
let div;
let span0;
let sequencestatus;
let t0;
let span1;
let t2;
let span2;
let a;
let current;
let mounted;
let dispose;
sequencestatus = new SequenceStatus({ props: { status: (
/*status*/
ctx[2]
) } });
return {
c() {
div = element("div");
span0 = element("span");
create_component(sequencestatus.$$.fragment);
t0 = space();
span1 = element("span");
span1.textContent = `${/*sectionName*/
ctx[3]}`;
t2 = space();
span2 = element("span");
a = element("a");
a.innerHTML = ``;
attr(span1, "class", "section-name svelte-ese-1ee9h3");
attr(a, "class", "svelte-ese-1ee9h3");
toggle_class(
a,
"section-done",
/*$status*/
ctx[1] > CONSTANTS.STATUS.READY
);
attr(span2, "class", "sequence-actions svelte-ese-1ee9h3");
attr(span2, "data-tooltip", localize("SEQUENCER.Sequences.AbortSection"));
attr(div, "class", "svelte-ese-1ee9h3");
},
m(target, anchor) {
insert(target, div, anchor);
append(div, span0);
mount_component(sequencestatus, span0, null);
append(div, t0);
append(div, span1);
append(div, t2);
append(div, span2);
append(span2, a);
current = true;
if (!mounted) {
dispose = listen(
a,
"click",
/*click_handler*/
ctx[4]
);
mounted = true;
}
},
p(ctx2, [dirty]) {
if (!current || dirty & /*$status, CONSTANTS*/
2) {
toggle_class(
a,
"section-done",
/*$status*/
ctx2[1] > CONSTANTS.STATUS.READY
);
}
},
i(local) {
if (current)
return;
transition_in(sequencestatus.$$.fragment, local);
current = true;
},
o(local) {
transition_out(sequencestatus.$$.fragment, local);
current = false;
},
d(detaching) {
if (detaching)
detach(div);
destroy_component(sequencestatus);
mounted = false;
dispose();
}
};
}
function instance$3($$self, $$props, $$invalidate) {
let $status;
let { section } = $$props;
const status = section.sectionStatus;
component_subscribe($$self, status, (value) => $$invalidate(1, $status = value));
const sectionName = (section?.constructor?.niceName ? section?.constructor?.niceName : section.constructor.name) + (section?._name ? " - " + section._name : "");
const click_handler = () => {
section._abortSection();
};
$$self.$$set = ($$props2) => {
if ("section" in $$props2)
$$invalidate(0, section = $$props2.section);
};
return [section, $status, status, sectionName, click_handler];
}
class SequenceSection extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance$3, create_fragment$3, safe_not_equal, { section: 0 });
}
}
const Sequence_svelte_svelte_type_style_lang = "";
function get_each_context$1(ctx, list, i) {
const child_ctx = ctx.slice();
child_ctx[6] = list[i];
return child_ctx;
}
function create_each_block$1(ctx) {
let sequencesection;
let current;
sequencesection = new SequenceSection({ props: { section: (
/*section*/
ctx[6]
) } });
return {
c() {
create_component(sequencesection.$$.fragment);
},
m(target, anchor) {
mount_component(sequencesection, target, anchor);
current = true;
},
p(ctx2, dirty) {
const sequencesection_changes = {};
if (dirty & /*sequence*/
1)
sequencesection_changes.section = /*section*/
ctx2[6];
sequencesection.$set(sequencesection_changes);
},
i(local) {
if (current)
return;
transition_in(sequencesection.$$.fragment, local);
current = true;
},
o(local) {
transition_out(sequencesection.$$.fragment, local);
current = false;
},
d(detaching) {
destroy_component(sequencesection, detaching);
}
};
}
function create_fragment$2(ctx) {
let div;
let span0;
let sequencestatus;
let t0;
let span1;
let t1;
let t2;
let t3_value = (
/*sequence*/
ctx[0].moduleName ? ` (${/*sequence*/
ctx[0].moduleName})` : ""
);
let t3;
let t4;
let span2;
let a0;
let i0;
let t5;
let a1;
let i1;
let t6;
let each_1_anchor;
let current;
let mounted;
let dispose;
sequencestatus = new SequenceStatus({ props: { status: (
/*status*/
ctx[3]
) } });
let each_value = (
/*sequence*/
ctx[0].sections
);
let each_blocks = [];
for (let i = 0; i < each_value.length; i += 1) {
each_blocks[i] = create_each_block$1(get_each_context$1(ctx, each_value, i));
}
const out = (i) => transition_out(each_blocks[i], 1, 1, () => {
each_blocks[i] = null;
});
return {
c() {
div = element("div");
span0 = element("span");
create_component(sequencestatus.$$.fragment);
t0 = space();
span1 = element("span");
t1 = text$1("Sequence ");
t2 = text$1(
/*index*/
ctx[1]
);
t3 = text$1(t3_value);
t4 = space();
span2 = element("span");
a0 = element("a");
i0 = element("i");
t5 = space();
a1 = element("a");
i1 = element("i");
t6 = space();
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].c();
}
each_1_anchor = empty();
attr(span1, "class", "sequence-name svelte-ese-1dcwqos");
attr(i0, "class", "fas fa-trash-can");
attr(a0, "class", "clear-sequence svelte-ese-1dcwqos");
attr(a0, "data-tooltip", localize("SEQUENCER.Sequences.Clear"));
toggle_class(
a0,
"sequence-done-show",
/*$status*/
ctx[2] > CONSTANTS.STATUS.RUNNING
);
attr(i1, "class", "fas fa-stop");
attr(a1, "data-tooltip", localize("SEQUENCER.Sequences.AbortSequence"));
attr(a1, "class", "svelte-ese-1dcwqos");
toggle_class(
a1,
"sequence-done-hide",
/*$status*/
ctx[2] > CONSTANTS.STATUS.RUNNING
);
attr(span2, "class", "sequence-actions svelte-ese-1dcwqos");
attr(div, "class", "sequence-name-container svelte-ese-1dcwqos");
},
m(target, anchor) {
insert(target, div, anchor);
append(div, span0);
mount_component(sequencestatus, span0, null);
append(div, t0);
append(div, span1);
append(span1, t1);
append(span1, t2);
append(span1, t3);
append(div, t4);
append(div, span2);
append(span2, a0);
append(a0, i0);
append(span2, t5);
append(span2, a1);
append(a1, i1);
insert(target, t6, anchor);
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].m(target, anchor);
}
insert(target, each_1_anchor, anchor);
current = true;
if (!mounted) {
dispose = [
listen(
a0,
"click",
/*click_handler*/
ctx[4]
),
listen(
a1,
"click",
/*click_handler_1*/
ctx[5]
)
];
mounted = true;
}
},
p(ctx2, [dirty]) {
if (!current || dirty & /*index*/
2)
set_data(
t2,
/*index*/
ctx2[1]
);
if ((!current || dirty & /*sequence*/
1) && t3_value !== (t3_value = /*sequence*/
ctx2[0].moduleName ? ` (${/*sequence*/
ctx2[0].moduleName})` : ""))
set_data(t3, t3_value);
if (!current || dirty & /*$status, CONSTANTS*/
4) {
toggle_class(
a0,
"sequence-done-show",
/*$status*/
ctx2[2] > CONSTANTS.STATUS.RUNNING
);
}
if (!current || dirty & /*$status, CONSTANTS*/
4) {
toggle_class(
a1,
"sequence-done-hide",
/*$status*/
ctx2[2] > CONSTANTS.STATUS.RUNNING
);
}
if (dirty & /*sequence*/
1) {
each_value = /*sequence*/
ctx2[0].sections;
let i;
for (i = 0; i < each_value.length; i += 1) {
const child_ctx = get_each_context$1(ctx2, each_value, i);
if (each_blocks[i]) {
each_blocks[i].p(child_ctx, dirty);
transition_in(each_blocks[i], 1);
} else {
each_blocks[i] = create_each_block$1(child_ctx);
each_blocks[i].c();
transition_in(each_blocks[i], 1);
each_blocks[i].m(each_1_anchor.parentNode, each_1_anchor);
}
}
group_outros();
for (i = each_value.length; i < each_blocks.length; i += 1) {
out(i);
}
check_outros();
}
},
i(local) {
if (current)
return;
transition_in(sequencestatus.$$.fragment, local);
for (let i = 0; i < each_value.length; i += 1) {
transition_in(each_blocks[i]);
}
current = true;
},
o(local) {
transition_out(sequencestatus.$$.fragment, local);
each_blocks = each_blocks.filter(Boolean);
for (let i = 0; i < each_blocks.length; i += 1) {
transition_out(each_blocks[i]);
}
current = false;
},
d(detaching) {
if (detaching)
detach(div);
destroy_component(sequencestatus);
if (detaching)
detach(t6);
destroy_each(each_blocks, detaching);
if (detaching)
detach(each_1_anchor);
mounted = false;
run_all(dispose);
}
};
}
function instance$2($$self, $$props, $$invalidate) {
let $status;
let { sequence } = $$props;
let { index } = $$props;
const status = sequence.status;
component_subscribe($$self, status, (value) => $$invalidate(2, $status = value));
const click_handler = () => {
SequenceManager.RunningSequences.delete(sequence.id);
};
const click_handler_1 = () => {
sequence._abort();
};
$$self.$$set = ($$props2) => {
if ("sequence" in $$props2)
$$invalidate(0, sequence = $$props2.sequence);
if ("index" in $$props2)
$$invalidate(1, index = $$props2.index);
};
return [sequence, index, $status, status, click_handler, click_handler_1];
}
let Sequence$2 = class Sequence2 extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance$2, create_fragment$2, safe_not_equal, { sequence: 0, index: 1 });
}
};
const Sequences_svelte_svelte_type_style_lang = "";
function get_each_context(ctx, list, i) {
const child_ctx = ctx.slice();
child_ctx[7] = list[i][0];
child_ctx[8] = list[i][1];
child_ctx[10] = i;
return child_ctx;
}
function create_else_block(ctx) {
let div0;
let button0;
let t1;
let button1;
let t3;
let div1;
let each_blocks = [];
let each_1_lookup = /* @__PURE__ */ new Map();
let current;
let mounted;
let dispose;
let each_value = (
/*runningSequences*/
ctx[0]
);
const get_key = (ctx2) => (
/*id*/
ctx2[7]
);
for (let i = 0; i < each_value.length; i += 1) {
let child_ctx = get_each_context(ctx, each_value, i);
let key = get_key(child_ctx);
each_1_lookup.set(key, each_blocks[i] = create_each_block(key, child_ctx));
}
return {
c() {
div0 = element("div");
button0 = element("button");
button0.textContent = `${localize("SEQUENCER.Sequences.ClearFinished")}`;
t1 = space();
button1 = element("button");
button1.textContent = `${localize("SEQUENCER.Sequences.StopAll")}`;
t3 = space();
div1 = element("div");
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].c();
}
attr(button0, "type", "button");
attr(button0, "class", "svelte-ese-1ismzan");
attr(button1, "type", "button");
attr(button1, "class", "svelte-ese-1ismzan");
attr(div0, "class", "sequence-button-header svelte-ese-1ismzan");
attr(div1, "class", "running-sequences svelte-ese-1ismzan");
},
m(target, anchor) {
insert(target, div0, anchor);
append(div0, button0);
append(div0, t1);
append(div0, button1);
insert(target, t3, anchor);
insert(target, div1, anchor);
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].m(div1, null);
}
current = true;
if (!mounted) {
dispose = [
listen(
button0,
"click",
/*click_handler*/
ctx[3]
),
listen(
button1,
"click",
/*click_handler_1*/
ctx[4]
)
];
mounted = true;
}
},
p(ctx2, dirty) {
if (dirty & /*runningSequences*/
1) {
each_value = /*runningSequences*/
ctx2[0];
group_outros();
each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx2, each_value, each_1_lookup, div1, outro_and_destroy_block, create_each_block, null, get_each_context);
check_outros();
}
},
i(local) {
if (current)
return;
for (let i = 0; i < each_value.length; i += 1) {
transition_in(each_blocks[i]);
}
current = true;
},
o(local) {
for (let i = 0; i < each_blocks.length; i += 1) {
transition_out(each_blocks[i]);
}
current = false;
},
d(detaching) {
if (detaching)
detach(div0);
if (detaching)
detach(t3);
if (detaching)
detach(div1);
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].d();
}
mounted = false;
run_all(dispose);
}
};
}
function create_if_block(ctx) {
let div;
let h2;
return {
c() {
div = element("div");
h2 = element("h2");
h2.textContent = `${localize("SEQUENCER.Sequences.NoSequences")}`;
attr(div, "class", "no-sequences");
},
m(target, anchor) {
insert(target, div, anchor);
append(div, h2);
},
p: noop,
i: noop,
o: noop,
d(detaching) {
if (detaching)
detach(div);
}
};
}
function create_each_block(key_1, ctx) {
let first;
let sequence;
let current;
sequence = new Sequence$2({
props: {
sequence: (
/*sequence*/
ctx[8]
),
index: (
/*index*/
ctx[10] + 1
)
}
});
return {
key: key_1,
first: null,
c() {
first = empty();
create_component(sequence.$$.fragment);
this.first = first;
},
m(target, anchor) {
insert(target, first, anchor);
mount_component(sequence, target, anchor);
current = true;
},
p(new_ctx, dirty) {
ctx = new_ctx;
const sequence_changes = {};
if (dirty & /*runningSequences*/
1)
sequence_changes.sequence = /*sequence*/
ctx[8];
if (dirty & /*runningSequences*/
1)
sequence_changes.index = /*index*/
ctx[10] + 1;
sequence.$set(sequence_changes);
},
i(local) {
if (current)
return;
transition_in(sequence.$$.fragment, local);
current = true;
},
o(local) {
transition_out(sequence.$$.fragment, local);
current = false;
},
d(detaching) {
if (detaching)
detach(first);
destroy_component(sequence, detaching);
}
};
}
function create_fragment$1(ctx) {
let div;
let current_block_type_index;
let if_block;
let current;
const if_block_creators = [create_if_block, create_else_block];
const if_blocks = [];
function select_block_type(ctx2, dirty) {
if (!/*runningSequences*/
ctx2[0].length)
return 0;
return 1;
}
current_block_type_index = select_block_type(ctx);
if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
return {
c() {
div = element("div");
if_block.c();
attr(div, "class", "sequence-container svelte-ese-1ismzan");
},
m(target, anchor) {
insert(target, div, anchor);
if_blocks[current_block_type_index].m(div, null);
current = true;
},
p(ctx2, [dirty]) {
let previous_block_index = current_block_type_index;
current_block_type_index = select_block_type(ctx2);
if (current_block_type_index === previous_block_index) {
if_blocks[current_block_type_index].p(ctx2, dirty);
} else {
group_outros();
transition_out(if_blocks[previous_block_index], 1, 1, () => {
if_blocks[previous_block_index] = null;
});
check_outros();
if_block = if_blocks[current_block_type_index];
if (!if_block) {
if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx2);
if_block.c();
} else {
if_block.p(ctx2, dirty);
}
transition_in(if_block, 1);
if_block.m(div, null);
}
},
i(local) {
if (current)
return;
transition_in(if_block);
current = true;
},
o(local) {
transition_out(if_block);
current = false;
},
d(detaching) {
if (detaching)
detach(div);
if_blocks[current_block_type_index].d();
}
};
}
function instance$1($$self, $$props, $$invalidate) {
let runningSequences;
let $RunningSequences;
const RunningSequences = SequenceManager.RunningSequences;
component_subscribe($$self, RunningSequences, (value) => $$invalidate(2, $RunningSequences = value));
onDestroy(() => {
SequenceManager.RunningSequences.clearFinishedSequences();
});
const click_handler = () => {
SequenceManager.RunningSequences.clearFinishedSequences();
};
const click_handler_1 = () => {
SequenceManager.RunningSequences.stopAll();
};
$$self.$$.update = () => {
if ($$self.$$.dirty & /*$RunningSequences*/
4) {
Object.values($RunningSequences);
}
if ($$self.$$.dirty & /*$RunningSequences*/
4) {
$$invalidate(0, runningSequences = Object.entries($RunningSequences));
}
};
return [
runningSequences,
RunningSequences,
$RunningSequences,
click_handler,
click_handler_1
];
}
class Sequences extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance$1, create_fragment$1, safe_not_equal, {});
}
}
function create_default_slot(ctx) {
let tabs_1;
let updating_activeTab;
let t;
let switch_instance;
let switch_instance_anchor;
let current;
function tabs_1_activeTab_binding(value) {
ctx[4](value);
}
let tabs_1_props = { tabs: (
/*tabs*/
ctx[3]
) };
if (
/*activeTab*/
ctx[1] !== void 0
) {
tabs_1_props.activeTab = /*activeTab*/
ctx[1];
}
tabs_1 = new Tabs({ props: tabs_1_props });
binding_callbacks.push(() => bind(tabs_1, "activeTab", tabs_1_activeTab_binding));
var switch_value = (
/*component*/
ctx[2]
);
function switch_props(ctx2) {
return {};
}
if (switch_value) {
switch_instance = construct_svelte_component(switch_value, switch_props());
}
return {
c() {
create_component(tabs_1.$$.fragment);
t = space();
if (switch_instance)
create_component(switch_instance.$$.fragment);
switch_instance_anchor = empty();
},
m(target, anchor) {
mount_component(tabs_1, target, anchor);
insert(target, t, anchor);
if (switch_instance)
mount_component(switch_instance, target, anchor);
insert(target, switch_instance_anchor, anchor);
current = true;
},
p(ctx2, dirty) {
const tabs_1_changes = {};
if (!updating_activeTab && dirty & /*activeTab*/
2) {
updating_activeTab = true;
tabs_1_changes.activeTab = /*activeTab*/
ctx2[1];
add_flush_callback(() => updating_activeTab = false);
}
tabs_1.$set(tabs_1_changes);
if (switch_value !== (switch_value = /*component*/
ctx2[2])) {
if (switch_instance) {
group_outros();
const old_component = switch_instance;
transition_out(old_component.$$.fragment, 1, 0, () => {
destroy_component(old_component, 1);
});
check_outros();
}
if (switch_value) {
switch_instance = construct_svelte_component(switch_value, switch_props());
create_component(switch_instance.$$.fragment);
transition_in(switch_instance.$$.fragment, 1);
mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
} else {
switch_instance = null;
}
}
},
i(local) {
if (current)
return;
transition_in(tabs_1.$$.fragment, local);
if (switch_instance)
transition_in(switch_instance.$$.fragment, local);
current = true;
},
o(local) {
transition_out(tabs_1.$$.fragment, local);
if (switch_instance)
transition_out(switch_instance.$$.fragment, local);
current = false;
},
d(detaching) {
destroy_component(tabs_1, detaching);
if (detaching)
detach(t);
if (detaching)
detach(switch_instance_anchor);
if (switch_instance)
destroy_component(switch_instance, detaching);
}
};
}
function create_fragment(ctx) {
let applicationshell;
let updating_elementRoot;
let current;
function applicationshell_elementRoot_binding(value) {
ctx[5](value);
}
let applicationshell_props = {
$$slots: { default: [create_default_slot] },
$$scope: { ctx }
};
if (
/*elementRoot*/
ctx[0] !== void 0
) {
applicationshell_props.elementRoot = /*elementRoot*/
ctx[0];
}
applicationshell = new ApplicationShell({ props: applicationshell_props });
binding_callbacks.push(() => bind(applicationshell, "elementRoot", applicationshell_elementRoot_binding));
return {
c() {
create_component(applicationshell.$$.fragment);
},
m(target, anchor) {
mount_component(applicationshell, target, anchor);
current = true;
},
p(ctx2, [dirty]) {
const applicationshell_changes = {};
if (dirty & /*$$scope, component, activeTab*/
134) {
applicationshell_changes.$$scope = { dirty, ctx: ctx2 };
}
if (!updating_elementRoot && dirty & /*elementRoot*/
1) {
updating_elementRoot = true;
applicationshell_changes.elementRoot = /*elementRoot*/
ctx2[0];
add_flush_callback(() => updating_elementRoot = false);
}
applicationshell.$set(applicationshell_changes);
},
i(local) {
if (current)
return;
transition_in(applicationshell.$$.fragment, local);
current = true;
},
o(local) {
transition_out(applicationshell.$$.fragment, local);
current = false;
},
d(detaching) {
destroy_component(applicationshell, detaching);
}
};
}
function instance($$self, $$props, $$invalidate) {
let component;
const { application } = getContext("#external");
let { elementRoot } = $$props;
let tabs = [
{
value: "player",
label: localize("SEQUENCER.Player.Title"),
icon: "fas fa-play-circle",
component: Player
},
{
value: "manager",
label: localize("SEQUENCER.Manager.Title"),
icon: "fas fa-film",
component: Manager
},
{
value: "sequences",
label: localize("SEQUENCER.Sequences.Title"),
icon: "fas fa-play",
component: Sequences
},
{
value: "howto",
label: localize("SEQUENCER.HowTo.Title"),
icon: "fas fa-chalkboard-teacher",
component: HowTo
}
];
let activeTab = application.options.tab ?? "manager";
function tabs_1_activeTab_binding(value) {
activeTab = value;
$$invalidate(1, activeTab);
}
function applicationshell_elementRoot_binding(value) {
elementRoot = value;
$$invalidate(0, elementRoot);
}
$$self.$$set = ($$props2) => {
if ("elementRoot" in $$props2)
$$invalidate(0, elementRoot = $$props2.elementRoot);
};
$$self.$$.update = () => {
if ($$self.$$.dirty & /*activeTab*/
2) {
$$invalidate(2, component = tabs.find((tab) => tab.value === activeTab).component);
}
};
return [
elementRoot,
activeTab,
component,
tabs,
tabs_1_activeTab_binding,
applicationshell_elementRoot_binding
];
}
class Effects_ui_shell extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, { elementRoot: 0 });
}
get elementRoot() {
return this.$$.ctx[0];
}
set elementRoot(elementRoot) {
this.$$set({ elementRoot });
flush();
}
}
class EffectsUIApp extends SvelteApplication {
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
title: game.i18n.localize("SEQUENCER.ManagerUI"),
classes: ["dialog"],
width: "auto",
height: "auto",
top: 65,
left: 120,
resizable: false,
svelte: {
class: Effects_ui_shell,
target: document.body
}
});
}
static getActiveApp() {
return Object.values(ui.windows).find((app) => {
return app instanceof this && app._state > Application.RENDER_STATES.CLOSED;
});
}
static async show(options = {}) {
const existingApp = this.getActiveApp();
if (existingApp)
return existingApp.render(false, { focus: true });
return new Promise((resolve) => {
options.resolve = resolve;
new this(options).render(true, { focus: true });
});
}
}
function registerSettings() {
game.settings.register(CONSTANTS.MODULE_NAME, "enable-fix-pixi", {
name: "SEQUENCER.Setting.EnablePixiFix.Title",
hint: "SEQUENCER.Setting.EnablePixiFix.Label",
scope: "client",
config: true,
default: false,
requiresReload: true,
type: Boolean
});
game.settings.register(CONSTANTS.MODULE_NAME, "enable-global-fix-pixi", {
name: "SEQUENCER.Setting.EnableGlobalPixiFix.Title",
hint: "SEQUENCER.Setting.EnableGlobalPixiFix.Label",
scope: "client",
config: true,
default: false,
requiresReload: true,
type: Boolean
});
game.settings.register(CONSTANTS.MODULE_NAME, "enable-above-ui-screenspace", {
name: "SEQUENCER.Setting.EnableAboveUIScreenspace.Title",
hint: "SEQUENCER.Setting.EnableAboveUIScreenspace.Label",
scope: "client",
config: true,
default: true,
requiresReload: true,
type: Boolean
});
game.settings.register(CONSTANTS.MODULE_NAME, "debug", {
name: "SEQUENCER.Setting.Debug.Title",
hint: "SEQUENCER.Setting.Debug.Label",
scope: "client",
config: true,
default: false,
type: Boolean
});
game.settings.register(CONSTANTS.MODULE_NAME, "showSidebarTools", {
name: "SEQUENCER.Setting.ShowTools.Title",
hint: "SEQUENCER.Setting.ShowTools.Label",
scope: "client",
config: true,
default: true,
requiresReload: true,
type: Boolean
});
game.settings.register(CONSTANTS.MODULE_NAME, "showTokenSidebarTools", {
name: "SEQUENCER.Setting.ShowTokenTools.Title",
hint: "SEQUENCER.Setting.ShowTokenTools.Label",
scope: "client",
config: true,
default: true,
requiresReload: true,
type: Boolean
});
game.settings.register(CONSTANTS.MODULE_NAME, "effectsEnabled", {
name: "SEQUENCER.Setting.EnableEffects.Title",
hint: "SEQUENCER.Setting.EnableEffects.Label",
scope: "client",
config: true,
default: true,
requiresReload: true,
type: Boolean
});
game.settings.register(CONSTANTS.MODULE_NAME, "soundsEnabled", {
name: "SEQUENCER.Setting.EnableSounds.Title",
hint: "SEQUENCER.Setting.EnableSounds.Label",
scope: "client",
config: true,
default: true,
requiresReload: true,
type: Boolean
});
game.settings.register(CONSTANTS.MODULE_NAME, "user-effect-opacity", {
name: "SEQUENCER.Setting.ExternalEffectOpacity.Title",
hint: "SEQUENCER.Setting.ExternalEffectOpacity.Label",
scope: "client",
config: true,
default: 50,
type: Number,
range: {
min: 0,
max: 100,
step: 1
}
});
game.settings.register(CONSTANTS.MODULE_NAME, "db-list-view", {
scope: "client",
config: false,
default: false,
type: Boolean
});
const permissionLevels = [
game.i18n.localize("SEQUENCER.Permission.Player"),
game.i18n.localize("SEQUENCER.Permission.Trusted"),
game.i18n.localize("SEQUENCER.Permission.Assistant"),
game.i18n.localize("SEQUENCER.Permission.GM")
];
game.settings.register(CONSTANTS.MODULE_NAME, "permissions-effect-create", {
name: "SEQUENCER.Setting.Permission.EffectCreate.Title",
hint: "SEQUENCER.Setting.Permission.EffectCreate.Label",
scope: "world",
config: true,
default: 0,
type: Number,
choices: permissionLevels,
requiresReload: true
});
game.settings.register(CONSTANTS.MODULE_NAME, "permissions-effect-delete", {
name: "SEQUENCER.Setting.Permission.EffectDelete.Title",
hint: "SEQUENCER.Setting.Permission.EffectDelete.Label",
scope: "world",
config: true,
default: 2,
type: Number,
choices: permissionLevels,
requiresReload: true
});
game.settings.register(CONSTANTS.MODULE_NAME, "permissions-sound-create", {
name: "SEQUENCER.Setting.Permission.SoundCreate.Title",
hint: "SEQUENCER.Setting.Permission.SoundCreate.Label",
scope: "world",
config: true,
default: 0,
type: Number,
choices: permissionLevels,
requiresReload: true
});
game.settings.register(CONSTANTS.MODULE_NAME, "permissions-preload", {
name: "SEQUENCER.Setting.Permission.PreloadClients.Title",
hint: "SEQUENCER.Setting.Permission.PreloadClients.Label",
scope: "world",
config: true,
default: 1,
type: Number,
choices: permissionLevels,
requiresReload: true
});
game.settings.register(CONSTANTS.MODULE_NAME, "permissions-sidebar-tools", {
name: "SEQUENCER.Setting.Permission.UseSidebarTools.Title",
hint: "SEQUENCER.Setting.Permission.UseSidebarTools.Label",
scope: "world",
config: true,
default: 0,
type: Number,
choices: permissionLevels,
requiresReload: true
});
game.settings.register(CONSTANTS.MODULE_NAME, "effectPresets", {
scope: "client",
default: {},
type: Object
});
Hooks.on("getSceneControlButtons", (controls) => {
if (!game.settings.get(CONSTANTS.MODULE_NAME, "showSidebarTools"))
return;
const selectTool = {
icon: "fas fa-expand",
name: "select-effect",
title: "SEQUENCER.SidebarButtons.Select",
visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools")
};
const playTool = {
icon: "fas fa-play",
name: "play-effect",
title: "SEQUENCER.SidebarButtons.Play",
visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools"),
onClick: () => {
EffectsUIApp.show({ inFocus: true, tab: "player" });
}
};
const viewer = {
icon: "fas fa-film",
name: "effectviewer",
title: "SEQUENCER.SidebarButtons.Manager",
button: true,
visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools"),
onClick: () => {
EffectsUIApp.show({ inFocus: true, tab: "manager" });
}
};
const database = {
icon: "fas fa-database",
name: "effectdatabase",
title: "SEQUENCER.SidebarButtons.Database",
button: true,
visible: user_can_do("permissions-sidebar-tools"),
onClick: () => {
DatabaseViewerApp.show();
}
};
controls.push({
name: CONSTANTS.MODULE_NAME,
title: "Sequencer Layer",
icon: "fas fa-list-ol",
layer: "sequencerInterfaceLayer",
visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools"),
activeTool: "select-effect",
tools: [selectTool, playTool, database, viewer]
});
if (!game.settings.get(CONSTANTS.MODULE_NAME, "showTokenSidebarTools"))
return;
const bar = controls.find((c) => c.name === "token");
bar.tools.push(database);
bar.tools.push(viewer);
});
debug("Sequencer | Registered settings");
}
async function migrateSettings() {
const oldScreenspaceSetting = game.settings.storage.get("client").getItem("sequencer.disable-above-ui-screenspace");
if (oldScreenspaceSetting) {
const value = oldScreenspaceSetting === "true";
game.settings.storage.get("client").removeItem("sequencer.disable-above-ui-screenspace");
await game.settings.set(
CONSTANTS.MODULE_NAME,
"enable-above-ui-screenspace",
!value
);
}
}
function registerLayers() {
CONFIG.Canvas.layers = foundry.utils.mergeObject(Canvas.layers, {
sequencerEffects: {
layerClass: BaseEffectsLayer,
group: "primary"
},
sequencerInterfaceLayer: {
layerClass: SequencerInterfaceLayer,
group: "interface"
},
sequencerEffectsUILayer: {
layerClass: UIEffectsLayer,
group: "interface"
}
});
if (!Object.is(Canvas.layers, CONFIG.Canvas.layers)) {
const layers = Canvas.layers;
Object.defineProperty(Canvas, "layers", {
get: function() {
return foundry.utils.mergeObject(layers, CONFIG.Canvas.layers);
}
});
}
debug("Registered Layers");
}
const hotkeys = {
get _ready() {
return canvas.ready && canvas.sequencerInterfaceLayer.active;
},
playTool: {
playManySequencedDown: () => {
if (!hotkeys._ready)
return;
EffectPlayer.playManySequenced = true;
},
playManySequencedUp: () => {
if (!hotkeys._ready)
return;
EffectPlayer.playManySequenced = false;
if (!EffectPlayer.isActive)
return;
EffectPlayer.playManyUp();
},
playManyDown: () => {
if (!hotkeys._ready)
return;
EffectPlayer.playMany = true;
},
playManyUp: () => {
if (!hotkeys._ready)
return;
EffectPlayer.playMany = false;
if (!EffectPlayer.isActive)
return;
EffectPlayer.playManyUp();
},
attachToDown: () => {
if (!hotkeys._ready)
return;
PlayerSettings.attachTo.store.set(true);
PlayerSettings.stretchToAttach.store.set(true);
},
attachToUp: () => {
if (!hotkeys._ready)
return;
PlayerSettings.attachTo.store.set(false);
PlayerSettings.stretchToAttach.store.set(false);
}
},
selectTool: {
snapToGridDown: () => {
if (!hotkeys._ready)
return;
SelectionManager.snapToGrid = true;
},
snapToGridUp: () => {
if (!hotkeys._ready)
return;
SelectionManager.snapToGrid = false;
},
attachToTargetDown: () => {
if (!hotkeys._ready)
return;
if (!SelectionManager.isActive)
return;
PlayerSettings.attachTo.store.set(true);
PlayerSettings.stretchToAttach.store.set(true);
},
attachToTargetUp: () => {
if (!hotkeys._ready)
return;
PlayerSettings.attachTo.store.set(false);
PlayerSettings.stretchToAttach.store.set(false);
},
deleteDown: () => {
if (!hotkeys._ready)
return;
SelectionManager.delete();
}
}
};
function registerHotkeys() {
game.keybindings.register(CONSTANTS.MODULE_NAME, "play-tool-hotkey-control", {
name: "SEQUENCER.Hotkeys.PlayTool.Control",
editable: [{ key: "ControlLeft" }],
onDown: hotkeys.playTool.playManySequencedDown,
onUp: hotkeys.playTool.playManySequencedUp
});
game.keybindings.register(CONSTANTS.MODULE_NAME, "play-tool-hotkey-shift", {
name: "SEQUENCER.Hotkeys.PlayTool.Shift",
editable: [{ key: "ShiftLeft" }],
onDown: hotkeys.playTool.playManyDown,
onUp: hotkeys.playTool.playManyUp
});
game.keybindings.register(CONSTANTS.MODULE_NAME, "play-tool-hotkey-alt", {
name: "SEQUENCER.Hotkeys.PlayTool.Alt",
editable: [{ key: "AltLeft" }],
onDown: hotkeys.playTool.attachToDown,
onUp: hotkeys.playTool.attachToDown
});
game.keybindings.register(
CONSTANTS.MODULE_NAME,
"select-tool-hotkey-control",
{
name: "SEQUENCER.Hotkeys.SelectTool.Control",
editable: [{ key: "ControlLeft" }],
onDown: hotkeys.selectTool.snapToGridDown,
onUp: hotkeys.selectTool.snapToGridUp
}
);
game.keybindings.register(CONSTANTS.MODULE_NAME, "select-tool-hotkey-alt", {
name: "SEQUENCER.Hotkeys.SelectTool.Alt",
editable: [{ key: "AltLeft" }],
onDown: hotkeys.selectTool.attachToTargetDown,
onUp: hotkeys.selectTool.attachToTargetUp
});
game.keybindings.register(
CONSTANTS.MODULE_NAME,
"select-tool-hotkey-delete",
{
name: "SEQUENCER.Hotkeys.SelectTool.Delete",
editable: [{ key: "Delete" }],
onDown: hotkeys.selectTool.deleteDown
}
);
}
async function registerTypes(register) {
fetch("modules/sequencer/typings/types.d.ts").then((response) => response.text()).then((content) => register("sequencer/types.d.ts", content));
}
const presetMap = /* @__PURE__ */ new Map();
class SequencerPresets {
/**
* Adds a preset that can then be used in sequences
*
* @param {string} inName
* @param {Function} inFunction
* @param {boolean} [overwrite=false] overwrite
* @returns {Map}
*/
static add(inName, inFunction, overwrite = false) {
if (typeof inName !== "string") {
throw custom_error(
"Sequencer",
`SequencerPresets | inName must be of type string`
);
}
if (!is_function$1(inFunction)) {
throw custom_error(
"Sequencer",
`SequencerPresets | inFunction must be of type function`
);
}
if (presetMap.get(inName) && !overwrite) {
throw custom_error(
"Sequencer",
`SequencerPresets | Preset "${inName}" already exists`
);
}
presetMap.set(inName, inFunction);
debug(`Sequencer | Presets | Added "${inName}" preset`);
return presetMap;
}
/**
* Retrieves all presets
*
* @returns {Map}
*/
static getAll() {
return presetMap;
}
/**
* Retrieves preset based on its name
*
* @param {string} name
* @returns {Function}
*/
static get(name) {
return presetMap.get(name);
}
}
class Section {
constructor(inSequence) {
this.sequence = inSequence;
this._applyTraits();
this._sectionStatus = writable$1(CONSTANTS.STATUS.READY);
this._playIf = true;
this._waitUntilFinished = false;
this._async = false;
this._waitUntilFinishedDelay = [0, 0];
this._repetitions = 1;
this._currentRepetition = 0;
this._repeatDelayMin = 0;
this._repeatDelayMax = 0;
this._repeatDelay = 0;
this._delayMin = 0;
this._delayMax = 0;
this._basicDelay = 0;
this._duration = false;
}
static niceName = "Section";
/**
* @protected
*/
get _shouldAsync() {
return this._async || this._waitAnyway;
}
/**
* @protected
*/
get shouldWaitUntilFinished() {
return this._waitUntilFinished || this._waitAnyway;
}
/**
* @protected
*/
get _waitAnyway() {
return (this._async || this._waitUntilFinished) && this._isLastRepetition || this._isLastRepetition && this._isLastSection;
}
/**
* @protected
*/
get _isLastSection() {
return this.sequence.sections.length - 1 === this.sequence.sections.indexOf(this);
}
/** ------------------------------------------------------------------------------------------------------------------------------ *
* Methods below this point should NOT be overridden by child instances of the class, they are integral to the sequence functioning
* ------------------------------------------------------------------------------------------------------------------------------- */
/**
* @protected
*/
get _isLastRepetition() {
return this._repetitions === 1 || this._repetitions === this._currentRepetition + 1;
}
/**
* @protected
*/
get _currentWaitTime() {
let waitUntilFinishedDelay = this._waitAnyway ? random_int_between(...this._waitUntilFinishedDelay) : 0;
return waitUntilFinishedDelay + this._repeatDelay;
}
/**
* Method overwritten by inheriting classes, which is called just before the "run" method is called (see below)
*
* @returns {Promise}
* @protected
*/
async preRun() {
}
/**
* Method overwritten by inheriting classes, which is called when this section is executed by the Sequence
*
* @returns {Promise}
* @protected
*/
async run() {
}
/**
* Method overwritten by inheriting classes, which is called when this section is serialized by the Sequence
*
* @returns {object}
* @private
*/
async _serialize() {
return {
async: this._async,
delay: [this._delayMin, this._delayMax],
waitUntilFinished: this._waitUntilFinished,
waitUntilFinishedDelay: this._waitUntilFinishedDelay,
repetitions: this._repetitions,
repetitionsDelay: [this._repeatDelayMin, this._repeatDelayMax]
};
}
_deserialize(data) {
this._async = data.async;
this._waitUntilFinished = data.waitUntilFinished;
this._waitUntilFinishedDelay = data.waitUntilFinishedDelay;
this._repetitions = data.repetitions;
this._repeatDelayMin = data.repetitionsDelay[0];
this._repeatDelayMax = data.repetitionsDelay[1];
return this;
}
/**
* Method overwritten by inheriting classes, which stores data or prepares data before the Sequence executes it (see EffectsSection)
*
* @protected
*/
async _initialize() {
}
/**
* Method overwritten by inheriting classes. Inheriting classes uses the following to apply traits to themselves:
* - Object.assign(this.constructor.prototype, trait)
*
* @protected
*/
_applyTraits() {
}
/**
* Causes the section to be repeated n amount of times, with an optional delay. If given inRepeatDelayMin
* and inRepeatDelayMax, a random repetition delay will be picked for every repetition
*
* @param {number} inRepetitions
* @param {number} inRepeatDelayMin
* @param {number} inRepeatDelayMax
* @returns {Section} this
*/
repeats(inRepetitions, inRepeatDelayMin = 0, inRepeatDelayMax) {
if (!is_real_number(inRepetitions))
throw this.sequence._customError(
this,
"repeats",
"inRepetitions must be of type number"
);
if (!is_real_number(inRepeatDelayMin))
throw this.sequence._customError(
this,
"repeats",
"repeatDelayMin must be of type number"
);
if (inRepeatDelayMax && !is_real_number(inRepeatDelayMax)) {
throw this.sequence._customError(
this,
"repeats",
"repeatDelayMax must be of type number"
);
}
this._repetitions = inRepetitions;
this._repeatDelayMin = Math.min(
inRepeatDelayMin,
inRepeatDelayMax ?? inRepeatDelayMin
);
this._repeatDelayMax = Math.max(
inRepeatDelayMin,
inRepeatDelayMax ?? inRepeatDelayMin
);
return this;
}
/**
* Causes the effect or sound to not play, and skip all delays, repetitions, waits, etc. If you pass a function,
* the function should return something false-y if you do not want the effect or sound to play.
*
* @param {boolean|function} inCondition
* @returns {Section} this
*/
playIf(inCondition) {
this._playIf = inCondition;
return this;
}
/**
* Causes the section to finish running before starting the next section.
*
* @param {number|boolean} [minDelay=0] minDelay
* @param {number/null} [maxDelay=null] maxDelay
* @returns {Section} this
*/
waitUntilFinished(minDelay = 0, maxDelay = null) {
if (minDelay === false)
return this;
if (!is_real_number(minDelay))
throw this.sequence._customError(
this,
"waitUntilFinished",
"minDelay must be of type number"
);
if (maxDelay !== null && !is_real_number(maxDelay))
throw this.sequence._customError(
this,
"waitUntilFinished",
"maxDelay must be of type number"
);
this._waitUntilFinished = true;
this._waitUntilFinishedDelay = [
Math.min(minDelay, maxDelay ?? minDelay),
Math.max(minDelay, maxDelay ?? minDelay)
];
return this;
}
/**
* Causes each effect or sound to finish playing before the next one starts playing. This differs from
* .waitUntilFinished() in the sense that this is for each repetition, whilst .waitUntilFinished() is
* for the entire section.
*
* @returns {Section} this
*/
async() {
this._async = true;
return this;
}
/**
* Delays the effect or sound from being played for a set amount of milliseconds. If given a second number, a
* random delay between the two numbers will be generated.
*
* @param {number} [msMin=1] minMs
* @param {number} [msMax=1] maxMs
* @returns {Section} this
*/
delay(msMin, msMax) {
if (!is_real_number(msMin))
throw this.sequence._customError(
this,
"delay",
"msMin must be of type number"
);
if (msMax && !is_real_number(msMax))
throw this.sequence._customError(
this,
"delay",
"msMax must be of type number"
);
this._delayMin = Math.min(msMin, msMax ?? msMin);
this._delayMax = Math.max(msMin, msMax ?? msMin);
return this;
}
/**
* Overrides the duration of this section
*
* @param {number} inDuration
* @returns {Section} this
*/
duration(inDuration) {
if (!is_real_number(inDuration))
throw this.sequence._customError(
this,
"duration",
"inDuration must be of type number"
);
this._duration = inDuration;
return this;
}
/**
* Applies a preset to the current section
*
* @param {string} presetName
* @param {*} args
* @returns {Section|FunctionSection|EffectSection|AnimationSection|SoundSection}
*/
preset(presetName, ...args) {
if (typeof presetName !== "string") {
throw this.sequence._customError(
this,
"name",
`inName must be of type string`
);
}
const preset = SequencerPresets.get(presetName);
if (!preset) {
custom_warning(
"Sequencer",
`preset | Could not find preset with name "${presetName}"`
);
return this;
}
return preset(this, ...args);
}
/**
* @protected
*/
async _shouldPlay() {
return is_function$1(this._playIf) ? await this._playIf() : this._playIf;
}
/**
* @protected
*/
_validateLocation(inLocation) {
inLocation = validate_document(inLocation);
if (typeof inLocation === "string") {
inLocation = get_object_from_scene(inLocation) ?? inLocation;
}
if (typeof inLocation === "string") {
inLocation = safe_str(inLocation);
}
return inLocation;
}
/**
* @protected
*/
async _execute() {
if (!await this._shouldPlay()) {
this.sectionStatus = CONSTANTS.STATUS.SKIPPED;
return;
}
this._basicDelay = random_float_between(this._delayMin, this._delayMax);
return new Promise(async (resolve) => {
setTimeout(async () => {
this.sectionStatus = CONSTANTS.STATUS.RUNNING;
for (let i = 0; i < this._repetitions; i++) {
if (get_store_value(this.sectionStatus) === CONSTANTS.STATUS.ABORTED) {
resolve();
return;
}
this._currentRepetition = i;
this._repeatDelay = i !== this._repetitions - 1 ? random_float_between(
this._repeatDelayMin,
this._repeatDelayMax
) : 0;
await this.preRun();
if (this._shouldAsync) {
await this.run();
} else {
this.run();
}
if (this._repetitions > 1 && i !== this._repetitions - 1) {
await this._delayBetweenRepetitions();
}
}
resolve();
}, this._basicDelay);
}).then(() => {
this.sectionStatus = CONSTANTS.STATUS.COMPLETE;
});
}
set sectionStatus(inStatus) {
this._sectionStatus.update((currentStatus) => {
if (currentStatus === CONSTANTS.STATUS.READY || currentStatus === CONSTANTS.STATUS.RUNNING && inStatus !== CONSTANTS.STATUS.ABORTED) {
return inStatus;
}
return currentStatus;
});
}
get sectionStatus() {
return this._sectionStatus;
}
_abortSection() {
this.sectionStatus = CONSTANTS.STATUS.ABORTED;
}
/**
* @protected
*/
async _delayBetweenRepetitions() {
let self = this;
return new Promise((resolve) => {
setTimeout(resolve, self._repeatDelay);
});
}
}
class FunctionSection extends Section {
constructor(inSequence, inFunc) {
super(inSequence);
if (!is_function$1(inFunc))
this._customError(
"create",
"The given function needs to be an actual function"
);
this._func = inFunc;
this._waitUntilFinished = inFunc.constructor.name === "AsyncFunction";
}
static niceName = "Function";
/**
* @returns {Promise}
*/
async run() {
debug("Running function");
await this._func();
}
/**
* @returns {Promise}
* @private
*/
async _execute() {
await this.run();
}
}
const animation = {
/**
* Base properties
*/
_animations: null,
/**
* Animates a property on the target of the animation.
*
* @param {string} inTarget
* @param {string} inPropertyName
* @param {object} inOptions
* @param {Number} inOptions.from - a single number from which to animate
* @param {Number} inOptions.to - a single number to which to animate
* @param {Number} inOptions.duration - how long in ms the animation should take
* @param {Number} inOptions.delay - inserts a delay in ms before the animation starts
* @param {String} inOptions.ease - what type of easing the animation should use
* @param {Boolean} inOptions.gridUnits - if animating width or height, this will set it to work in the scene's grid units
* @param {Boolean} inOptions.fromEnd - makes this animation play from the end, like fadeOut, scaleOut, etc
*
* @returns this
*/
animateProperty(inTarget, inPropertyName, inOptions = {}) {
if (!this._animations)
this._animations = [];
const result = validateAnimation(
inTarget,
inPropertyName,
inOptions
);
if (typeof result === "string") {
throw this.sequence._customError(this, "animateProperty", result);
}
this._animations.push(result);
return this;
},
/**
* Loops a property between a set of values on the target
*
* @param {string} inTarget
* @param {string} inPropertyName
* @param {object} inOptions
* @param {Number} inOptions.from - a single number from which to loop
* @param {Number} inOptions.to - a single number to which to loop
* @param {Number} inOptions.values - an array of values to loop between
* @param {Number} inOptions.duration - how long in ms the loop should take
* @param {Number} inOptions.loops - how many loops in total this animation should go through - if none are specified, the loop is indefinite
* @param {Number} inOptions.delay - inserts a delay in ms before the animation starts
* @param {String} inOptions.ease - what type of easing the animation should use
* @param {Boolean} inOptions.pingPong - sets whether loop should interpolate to the first value after it reaches the first value, or simply set it to the first value
* @param {Boolean} inOptions.gridUnits - if animating width or height, this will set it to work in the scene's grid units
*
* @returns this
*/
loopProperty(inTarget, inPropertyName, inOptions = {}) {
if (!this._animations)
this._animations = [];
const result = validateLoopingAnimation(
inTarget,
inPropertyName,
inOptions
);
if (typeof result === "string") {
throw this.sequence._customError(this, "loopProperty", result);
}
this._animations.push(result);
return this;
}
};
const audio = {
/**
* Base properties
*/
_volume: null,
_fadeInAudio: null,
_fadeOutAudio: null,
/**
* Sets the volume of the sound.
*
* @param {number} inVolume
* @returns this
*/
volume(inVolume) {
if (!is_real_number(inVolume))
throw this.sequence._customError(
this,
"volume",
"inVolume must be of type number"
);
this._volume = Math.max(0, Math.min(1, inVolume));
return this;
},
/**
* Causes the animated section to fade in its audio (if any) when played
*
* @param {number} duration How long the fade should be
* @param {object} [options] Additional options, such as easing and delay
* @returns this
*/
fadeInAudio(duration, options = {}) {
if (typeof options !== "object")
throw this.sequence._customError(
this,
"fadeInAudio",
"options must be of type object"
);
options = foundry.utils.mergeObject(
{
ease: "linear",
delay: 0
},
options
);
if (!is_real_number(duration))
throw this.sequence._customError(
this,
"fadeInAudio",
"duration must be of type number"
);
if (typeof options.ease !== "string")
throw this.sequence._customError(
this,
"fadeInAudio",
"options.ease must be of type string"
);
if (!is_real_number(options.delay))
throw this.sequence._customError(
this,
"fadeInAudio",
"options.delay must be of type number"
);
this._fadeInAudio = {
duration,
ease: options.ease,
delay: options.delay
};
return this;
},
/**
* Causes the audio to fade out at the end of the animated section's duration
*
* @param {number} duration How long the fade should be
* @param {object} [options] Additional options, such as easing and delay
* @returns this
*/
fadeOutAudio(duration, options = {}) {
if (typeof options !== "object")
throw this.sequence._customError(
this,
"fadeOutAudio",
"options must be of type object"
);
options = foundry.utils.mergeObject(
{
ease: "linear",
delay: 0
},
options
);
if (!is_real_number(duration))
throw this.sequence._customError(
this,
"fadeOutAudio",
"duration must be of type number"
);
if (typeof options.ease !== "string")
throw this.sequence._customError(
this,
"fadeOutAudio",
"ease must be of type string"
);
if (!is_real_number(options.delay))
throw this.sequence._customError(
this,
"fadeOutAudio",
"delay must be of type number"
);
this._fadeOutAudio = {
duration,
ease: options.ease,
delay: options.delay
};
return this;
}
};
const files = {
/**
* Base properties
*/
_file: "",
_fileOptions: false,
_baseFolder: "",
_mustache: null,
/**
* Declares which file to be played. This may also be an array of paths, which will be randomly picked from each
* time the section is played.
*
* @param {string|array} inFile
* @returns this
*/
file(inFile) {
this._file = inFile;
return this;
},
/**
* Defines the base folder that will prepend to the file path. This is mainly just useful to make the file
* path easier to manage.
*
* @param {string} inBaseFolder
* @returns this
*/
baseFolder(inBaseFolder) {
if (typeof inBaseFolder !== "string")
throw this.sequence._customError(
this,
"baseFolder",
"inBaseFolder must be of type string"
);
this._baseFolder = inBaseFolder + (inBaseFolder.endsWith("/") ? "" : "/");
return this;
},
/**
* Sets the Mustache of the filepath. This is applied after the randomization of the filepath, if available.
*
* @param {object} inMustache
* @returns this
*/
setMustache(inMustache) {
if (typeof inMustache !== "object")
throw this.sequence._customError(
this,
"setMustache",
"inMustache must be of type object"
);
this._mustache = inMustache;
return this;
},
async _determineFile(inFile) {
if (!Array.isArray(inFile) && typeof inFile === "object") {
return this._validateCustomRange(inFile);
}
if (Array.isArray(inFile))
inFile = random_array_element(inFile, { recurse: true });
inFile = this._applyMustache(inFile);
if (Sequencer.Database.entryExists(inFile)) {
return this._determineDatabaseFile(inFile);
}
const determinedFile = await this._processFile(inFile);
return { file: determinedFile, forcedIndex: false, customRange: false };
},
async _processFile(inFile) {
inFile = this._applyMustache(inFile);
inFile = this._applyBaseFolder(inFile);
inFile = await this._applyWildcard(inFile);
if (Array.isArray(inFile))
inFile = random_array_element(inFile, { recurse: true });
return inFile;
},
async _validateCustomRange(inFile) {
const finalFiles = {};
const validRanges = Object.keys(SequencerFileRangeFind.ftToDistanceMap);
for (const [range, rangeFile] of Object.entries(inFile)) {
if (!validRanges.includes(range)) {
throw this.sequence._customError(
this,
"file",
`a file-distance key map must only contain the following keys: ${validRanges.join(
", "
)}`
);
}
finalFiles[range] = await this._processFile(rangeFile);
}
return { file: finalFiles, forcedIndex: false, customRange: true };
},
_determineDatabaseFile(inFile) {
const entries = Sequencer.Database.getEntry(inFile);
const entry = Array.isArray(entries) ? random_array_element(entries) : entries;
const match = inFile.match(/(\d)+$/);
return {
file: entry,
forcedIndex: match ? Number(match[1]) : false,
customRange: false
};
},
_applyBaseFolder(inFile) {
if (Array.isArray(inFile))
return inFile.map((file) => this._applyBaseFolder(file));
return inFile.startsWith(this._baseFolder) ? inFile : this._baseFolder + inFile;
},
_applyMustache(inFile) {
if (!this._mustache)
return inFile;
let template = Handlebars.compile(inFile);
return template(this._mustache);
},
async _applyWildcard(inFile) {
if (!inFile.includes("*"))
return inFile;
if (Array.isArray(inFile))
return inFile.map(async (file) => await this._applyWildcard(file));
inFile = this._applyBaseFolder(inFile);
return getFiles(inFile, {
applyWildCard: true,
softFail: this.sequence.softFail
});
}
};
const moves = {
/**
* Base properties
*/
_moveTowards: null,
_moveSpeed: null,
/**
* Sets the location to move the target object to
*
* @param {object|string} inTarget
* @param {object} options
* @returns this
*/
moveTowards(inTarget, options = {}) {
options = foundry.utils.mergeObject(
{
ease: "linear",
delay: 0,
rotate: true,
cacheLocation: false
},
options
);
if (typeof options.ease !== "string")
throw this.sequence._customError(
this,
"moveTowards",
"options.ease must be of type string"
);
if (!is_real_number(options.delay))
throw this.sequence._customError(
this,
"moveTowards",
"options.delay must be of type number"
);
if (typeof options.rotate !== "boolean")
throw this.sequence._customError(
this,
"moveTowards",
"options.rotate must be of type boolean"
);
if (typeof options.cacheLocation !== "boolean")
throw this.sequence._customError(
this,
"moveTowards",
"options.cacheLocation must be of type boolean"
);
options.target = this._validateLocation(inTarget);
if (!options.target)
throw this.sequence._customError(
this,
"moveTowards",
"could not find position of given object"
);
options.target = options.cacheLocation ? get_object_position(options.cacheLocation, { measure: true }) : options.target;
this._moveTowards = options;
return this;
},
/**
* Sets the speed (pixels per frame) to move the target object
*
* @param {number} inSpeed
* @returns this
*/
moveSpeed(inSpeed) {
if (!is_real_number(inSpeed))
throw this.sequence._customError(
this,
"moveSpeed",
"inSpeed must be of type number"
);
this._moveSpeed = inSpeed;
return this;
}
};
const opacity = {
/**
* Base properties
*/
_opacity: null,
_fadeIn: null,
_fadeOut: null,
/**
* Sets the opacity of the effect. If used with ._fadeIn() and/or ._fadeOut(), this defines what the effect will fade to/from
*
* @param {number} inOpacity
* @returns this
*/
opacity(inOpacity) {
if (!is_real_number(inOpacity))
throw this.sequence._customError(
this,
"opacity",
"inOpacity must be of type number"
);
this._opacity = inOpacity;
return this;
},
/**
* Causes the effect to fade in when played
*
* @param {number} duration How long the fade should be
* @param {object} [options] Additional options, such as easing and delay
* @returns this
*/
fadeIn(duration, options = {}) {
if (typeof options !== "object")
throw this.sequence._customError(
this,
"fadeIn",
"options must be of type object"
);
options = foundry.utils.mergeObject(
{
ease: "linear",
delay: 0
},
options
);
if (!is_real_number(duration))
throw this.sequence._customError(
this,
"fadeIn",
"duration must be of type number"
);
if (typeof options.ease !== "string")
throw this.sequence._customError(
this,
"fadeIn",
"options.ease must be of type string"
);
if (!is_real_number(options.delay))
throw this.sequence._customError(
this,
"fadeIn",
"options.delay must be of type number"
);
this._fadeIn = {
duration,
ease: options.ease,
delay: options.delay
};
return this;
},
/**
* Causes the effect to fade out at the end of the effect's duration
*
* @param {number} duration How long the fade should be
* @param {object} [options] Additional options, such as easing and delay
* @returns this
*/
fadeOut(duration, options = {}) {
if (typeof options !== "object")
throw this.sequence._customError(
this,
"fadeOut",
"options must be of type object"
);
options = foundry.utils.mergeObject(
{
ease: "linear",
delay: 0
},
options
);
if (!is_real_number(duration))
throw this.sequence._customError(
this,
"fadeOut",
"duration must be of type number"
);
if (typeof options.ease !== "string")
throw this.sequence._customError(
this,
"fadeOut",
"ease must be of type string"
);
if (!is_real_number(options.delay))
throw this.sequence._customError(
this,
"fadeOut",
"delay must be of type number"
);
this._fadeOut = {
duration,
ease: options.ease,
delay: options.delay
};
return this;
}
};
const rotation = {
/**
* Base properties
*/
_angle: null,
_rotateIn: null,
_rotateOut: null,
_randomRotation: null,
_rotateTowards: null,
/**
* The object gets a random rotation, which means it should not be used with .stretchTo()
*
* @param {boolean} [inBool=true] inBool
* @returns this
*/
randomRotation(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"randomRotation",
"inBool must be of type boolean"
);
this._randomRotation = inBool;
return this;
},
/**
* Sets the rotation of the object, which is added on top of the calculated rotation after .rotateTowards() or .randomRotation()
*
* @param {number} inRotation
* @returns this
*/
rotate(inRotation) {
if (!is_real_number(inRotation))
throw this.sequence._customError(
this,
"opacity",
"inRotation must be of type number"
);
this._angle = inRotation;
return this;
},
/**
* Causes the object to rotate when it starts playing
*
* @param {number} degrees
* @param {number} duration
* @param {object} [options] options
* @returns this
*/
rotateIn(degrees, duration, options = {}) {
if (typeof options !== "object")
throw this.sequence._customError(
this,
"rotateIn",
"options must be of type object"
);
options = foundry.utils.mergeObject(
{
ease: "linear",
delay: 0
},
options
);
if (!is_real_number(degrees))
throw this.sequence._customError(
this,
"rotateOut",
"degrees must be of type number"
);
if (!is_real_number(duration))
throw this.sequence._customError(
this,
"rotateOut",
"duration must be of type number"
);
if (typeof options.ease !== "string")
throw this.sequence._customError(
this,
"rotateIn",
"options.ease must be of type string"
);
if (!is_real_number(options.delay))
throw this.sequence._customError(
this,
"rotateIn",
"options.delay must be of type number"
);
this._rotateIn = {
value: degrees,
duration,
ease: options.ease,
delay: options.delay
};
return this;
},
/**
* Causes the object to rotate at the end of the effect's duration
*
* @param {number} degrees
* @param {number} duration
* @param {object} [options] options
* @returns this
*/
rotateOut(degrees, duration, options = {}) {
if (typeof options !== "object")
throw this.sequence._customError(
this,
"rotateOut",
"options must be of type object"
);
options = foundry.utils.mergeObject(
{
ease: "linear",
delay: 0
},
options
);
if (!is_real_number(degrees))
throw this.sequence._customError(
this,
"rotateOut",
"degrees must be of type number"
);
if (!is_real_number(duration))
throw this.sequence._customError(
this,
"rotateOut",
"duration must be of type number"
);
if (typeof options.ease !== "string")
throw this.sequence._customError(
this,
"rotateOut",
"options.ease must be of type string"
);
if (!is_real_number(options.delay))
throw this.sequence._customError(
this,
"rotateOut",
"options.delay must be of type number"
);
this._rotateOut = {
value: degrees,
duration,
ease: options.ease,
delay: options.delay
};
return this;
}
};
const scale = {
_scaleMin: null,
_scaleMax: null,
_scaleIn: null,
_scaleOut: null,
/**
* A method that can take the following:
* - A number to set the scale uniformly
* - An object with x and y for non-uniform scaling
* - Two numbers which the Sequencer will randomly pick a uniform scale between
*
* @param {number|object} inScaleMin
* @param {number} [inScaleMax] inScaleMax
* @returns this
*/
scale(inScaleMin, inScaleMax) {
if (!is_real_number(inScaleMin) && typeof inScaleMin !== "object")
throw this.sequence._customError(
this,
"scale",
"inScale must be of type number or object"
);
if (is_real_number(inScaleMin)) {
if (inScaleMax && !is_real_number(inScaleMax)) {
throw this.sequence._customError(
this,
"scale",
"if inScaleMin is a number, inScaleMax must also be of type number"
);
}
}
this._scaleMin = inScaleMin;
this._scaleMax = inScaleMax ?? false;
return this;
},
/**
* Causes the effect to scale when it starts playing
*
* @param {number|object} scale
* @param {number} duration
* @param {object} [options] options
* @returns this
*/
scaleIn(scale2, duration, options = {}) {
if (typeof options !== "object")
throw this.sequence._customError(
this,
"scaleIn",
"options must be of type object"
);
options = foundry.utils.mergeObject(
{
ease: "linear",
delay: 0
},
options
);
if (!is_real_number(duration))
throw this.sequence._customError(
this,
"scaleIn",
"duration must be of type number"
);
if (!is_real_number(scale2) && typeof scale2 !== "object")
throw this.sequence._customError(
this,
"scaleIn",
"scale must be of type number or object"
);
if (typeof options.ease !== "string")
throw this.sequence._customError(
this,
"scaleIn",
"options.ease must be of type string"
);
if (!is_real_number(options.delay))
throw this.sequence._customError(
this,
"scaleIn",
"options.delay must be of type number"
);
this._scaleIn = {
value: scale2,
duration,
ease: options.ease,
delay: options.delay
};
return this;
},
/**
* Causes the effect to scale at the end of the effect's duration
*
* @param {number|object} scale
* @param {number} duration
* @param {object} [options] options
* @returns this
*/
scaleOut(scale2, duration, options = {}) {
if (typeof options !== "object")
throw this.sequence._customError(
this,
"scaleOut",
"options must be of type object"
);
options = foundry.utils.mergeObject(
{
ease: "linear",
delay: 0
},
options
);
if (!is_real_number(duration))
throw this.sequence._customError(
this,
"scaleOut",
"duration must be of type number"
);
if (!is_real_number(scale2) && typeof scale2 !== "object")
throw this.sequence._customError(
this,
"scaleOut",
"scale must be of type number or object"
);
if (typeof options.ease !== "string")
throw this.sequence._customError(
this,
"scaleOut",
"options.ease must be of type string"
);
if (!is_real_number(options.delay))
throw this.sequence._customError(
this,
"scaleOut",
"options.delay must be of type number"
);
this._scaleOut = {
value: scale2,
duration,
ease: options.ease,
delay: options.delay
};
return this;
}
};
const time = {
_hasTime: true,
_isRange: false,
_startTime: null,
_startPerc: null,
_endTime: null,
_endPerc: null,
/**
* Sets the start and end time of the section, playing only that range
*
* @param {number} inMsStart
* @param {number} inMsEnd
* @returns this
*/
timeRange(inMsStart, inMsEnd) {
if (!is_real_number(inMsStart))
throw this.sequence._customError(
this,
"timeRange",
"inMsStart must be of type number"
);
if (!is_real_number(inMsEnd))
throw this.sequence._customError(
this,
"timeRange",
"inMsEnd must be of type number"
);
this._startTime = inMsStart;
this._endTime = inMsEnd;
this._isRange = true;
return this;
},
/**
* Sets the start time of the section.
*
* @param {number} inMs
* @returns this
*/
startTime(inMs) {
if (!is_real_number(inMs))
throw this.sequence._customError(
this,
"startTime",
"inMs must be of type number"
);
this._startTime = inMs;
this._startPerc = false;
this._isRange = false;
return this;
},
/**
* Sets the start time of the section based on a percentage from its total duration.
*
* @param {number} inPercentage
* @returns this
*/
startTimePerc(inPercentage) {
if (!is_real_number(inPercentage))
throw this.sequence._customError(
this,
"startTimePerc",
"inPercentage must be of type number"
);
this._startTime = inPercentage;
this._startPerc = true;
this._isRange = false;
return this;
},
/**
* Sets the ending time of the section (from the end).
*
* @param {number} inMs
* @returns this
*/
endTime(inMs) {
if (!is_real_number(inMs))
throw this.sequence._customError(
this,
"endTime",
"inMs must be of type number"
);
this._endTime = inMs;
this._endPerc = false;
this._isRange = false;
return this;
},
/**
* Sets the ending time of the section based on a percentage from the total duration.
*
* @param {number} inPercentage
* @returns this
*/
endTimePerc(inPercentage) {
if (!is_real_number(inPercentage))
throw this.sequence._customError(
this,
"endTimePerc",
"inPercentage must be of type number"
);
this._endTime = inPercentage;
this._endPerc = true;
this._isRange = false;
return this;
}
};
const users = {
_users: null,
_addUser(inUser) {
if (!this._users)
this._users = [];
if (typeof inUser !== "string")
throw this.sequence._customError(
this,
"_addUser",
"inUser must be of type string"
);
if (!game.users.has(inUser)) {
if (game.users.getName(inUser)) {
inUser = game.users.getName(inUser).id;
} else {
throw this.sequence._customError(
this,
"_addUser",
`user with id or name "${inUser}" does not exist!`
);
}
}
if (!this._users.includes(inUser))
this._users.push(inUser);
},
_deleteUser(inUser) {
if (!this._users)
this._users = [];
if (this._users.includes(inUser)) {
let index = this._users.indexOf(inUser);
this._users.splice(index, 1);
}
},
/**
* Causes section to be executed only locally, and not push to other connected clients.
*
* @param {boolean} inLocally
* @returns this
*/
locally(inLocally = true) {
if (inLocally)
this._addUser(game.userId);
else
this._deleteUser(game.userId);
return this;
},
/**
* Causes the section to be executed for only a set of users.
*
* @param {string|User|array} inUsers
* @returns this
*/
forUsers(inUsers) {
if (!Array.isArray(inUsers)) {
if (typeof inUsers !== "string")
throw this.sequence._customError(
this,
"forUsers",
"inUser must be of type string"
);
inUsers = [inUsers];
}
inUsers.forEach((u) => this._addUser(u));
return this;
}
};
const filter = {
_filters: null,
_addFilter(inFilterName, inData, inName = false) {
if (!this._filters)
this._filters = [];
this._filters.push({
className: inFilterName,
name: inName,
data: inData
});
},
_testFilter(inFilterName, inData) {
let filter2 = new filters[inFilterName](inData);
if (!filter2.isValid)
throw this.sequence._customError(
this,
"filter",
`Could not create ${inFilterName} filter - data is malformed!`
);
},
filter(inFilterName, inData = {}, inName = "") {
if (typeof inFilterName !== "string")
throw this.sequence._customError(
this,
"filter",
`inFilterName must be of type string`
);
if (!Object.keys(filters).includes(inFilterName))
throw this.sequence._customError(
this,
"filter",
`"${inFilterName}" does not exist`
);
this._testFilter(inFilterName, inData);
this._addFilter(inFilterName, inData, inName);
return this;
}
};
const tint = {
_tint: null,
/**
* Tints the target of this section by the color given to the
*
* @param {number|string} inColor
* @returns this
*/
tint(inColor) {
if (!is_real_number(inColor) && typeof inColor !== "string")
throw this.sequence._customError(
this,
"tint",
`inColor must be of type string (hexadecimal) or number (decimal)!`
);
this._tint = parseColor(inColor);
return this;
}
};
const location = {
/**
* Base properties
*/
_source: null,
/**
* A smart method that can take a reference to an object, or a direct on the canvas to play the effect at,
* or a string reference (see .name())
*
* @param {Object|String} inLocation
* @param {Object} inOptions
* @returns {EffectSection}
*/
atLocation(inLocation, inOptions = {}) {
if (!(typeof inLocation === "object" || typeof inLocation === "string")) {
throw this.sequence._customError(
this,
"atLocation",
`inLocation is invalid, and must be of type of object, string, placeable object, or document`
);
}
if (typeof inOptions !== "object")
throw this.sequence._customError(
this,
"atLocation",
`inOptions must be of type object`
);
inOptions = foundry.utils.mergeObject(
{
cacheLocation: false,
offset: false,
randomOffset: false,
gridUnits: false,
local: false
},
inOptions
);
inLocation = this._validateLocation(inLocation);
if (inLocation === void 0)
throw this.sequence._customError(
this,
"atLocation",
"could not find position of given object"
);
if (typeof inOptions.cacheLocation !== "boolean")
throw this.sequence._customError(
this,
"atLocation",
"inOptions.cacheLocation must be of type boolean"
);
if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
throw this.sequence._customError(
this,
"atLocation",
"inOptions.randomOffset must be of type boolean or number"
);
this._temporaryEffect = this._temporaryEffect || (inLocation instanceof foundry.abstract.Document ? !is_UUID(inLocation?.uuid) : false);
if (inOptions.offset) {
const offsetData = this._validateOffset(
"atLocation",
inOptions.offset,
inOptions
);
this._offset = {
source: offsetData,
target: this._offset?.target ?? false
};
}
this._randomOffset = {
source: inOptions.randomOffset,
target: this._randomOffset?.target ?? false
};
this._source = inOptions.cacheLocation && typeof inLocation !== "string" ? get_object_canvas_data(inLocation) : inLocation;
return this;
}
};
const offset = {
_offset: null,
_randomOffset: null,
_validateOffset(functionName, inOffset, inOptions = {}) {
inOffset = foundry.utils.mergeObject(
{
x: 0,
y: 0
},
inOffset
);
inOptions = foundry.utils.mergeObject(
{
gridUnits: false,
local: false
},
inOptions
);
if (typeof inOptions.gridUnits !== "boolean")
throw this.sequence._customError(
this,
functionName,
"inOptions.gridUnits must be of type boolean"
);
if (typeof inOptions.local !== "boolean")
throw this.sequence._customError(
this,
functionName,
"inOptions.local must be of type boolean"
);
if (!is_real_number(inOffset.x))
throw this.sequence._customError(
this,
functionName,
`inOffset.x must be of type number!`
);
if (!is_real_number(inOffset.y))
throw this.sequence._customError(
this,
functionName,
`inOffset.y must be of type number!`
);
return {
...inOffset,
...inOptions
};
}
};
const text = {
_text: null,
/**
* Creates a text element, attached to the sprite. The options for the text are available here:
* https://pixijs.io/pixi-text-style/
*
* @param {String} inText
* @param {Object} inOptions
* @returns {EffectSection}
*/
text(inText, inOptions = {}) {
if (typeof inText !== "string")
throw this.sequence._customError(
this,
"text",
"inText must be of type string"
);
this._text = foundry.utils.mergeObject(
{
text: inText
},
inOptions
);
return this;
}
};
const traits = {
animation,
audio,
files,
moves,
opacity,
rotation,
scale,
time,
users,
filter,
tint,
location,
offset,
text
};
class EffectSection extends Section {
constructor(inSequence, inFile = "") {
super(inSequence);
this._deserializedData = null;
this._file = inFile;
this._text = null;
this._source = null;
this._stretchTo = null;
this._attachTo = null;
this._from = null;
this._origin = null;
this._anchor = null;
this._spriteAnchor = null;
this._randomOffset = null;
this._missed = null;
this._private = null;
this._randomMirrorX = null;
this._randomMirrorY = null;
this._mirrorX = null;
this._mirrorY = null;
this._playbackRate = null;
this._template = null;
this._overrides = [];
this._name = null;
this._zIndex = null;
this._offset = null;
this._spriteOffset = null;
this._size = null;
this._persist = null;
this._persistOptions = null;
this._zeroSpriteRotation = null;
this._extraEndDuration = null;
this._noLoop = null;
this._tilingTexture = null;
this._snapToGrid = null;
this._scaleToObject = null;
this._screenSpace = null;
this._screenSpaceAboveUI = null;
this._screenSpaceAnchor = null;
this._screenSpacePosition = null;
this._screenSpaceScale = null;
this._elevation = null;
this._masks = [];
this._tiedDocuments = [];
this._selfMask = false;
this._temporaryEffect = false;
this._spriteRotation = 0;
this._randomSpriteRotation = false;
this._isRangedEffect = null;
this._offsetLegacy = null;
this._randomOffsetLegacy = null;
this._aboveLighting = null;
this._aboveInterface = null;
this._spriteScaleMin = 1;
this._spriteScaleMax = null;
this._isometric = null;
this._shapes = [];
this._xray = null;
this._playEffect = true;
}
static niceName = "Effect";
/**
* @private
*/
get _target() {
return this._stretchTo || this._rotateTowards || this._moveTowards || false;
}
static debounceWarning() {
custom_warning(
"Sequencer",
"Effect | This user does not have permissions to play effects. This can be configured in Sequencer's module settings."
);
}
/**
* Causes the effect's position to be stored and can then be used with .atLocation(), .stretchTowards(),
* and .rotateTowards() to refer to previous effects' locations
*
* @param {String} inName
* @returns {EffectSection}
*/
name(inName) {
if (typeof inName !== "string")
throw this.sequence._customError(
this,
"name",
"inName must be of type string"
);
this._name = safe_str(inName);
return this;
}
/**
* Causes the effect to persist indefinitely on the canvas until _ended via SequencerEffectManager.endAllEffects() or
* name the effect with .name() and then end it through SequencerEffectManager.endEffect()
*
* @param {Boolean} [inBool=true] inBool
* @param {Object} [inOptions={}] inOptions
* @returns {EffectSection}
*/
persist(inBool = true, inOptions = {}) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"persist",
"inBool must be of type boolean"
);
if (typeof inOptions !== "object")
throw this.sequence._customError(
this,
"persist",
`inOptions must be of type object`
);
inOptions = foundry.utils.mergeObject(
{
id: randomID(),
persistTokenPrototype: false
},
inOptions
);
if (typeof inOptions.persistTokenPrototype !== "boolean")
throw this.sequence._customError(
this,
"persist",
"inOptions.persistTokenPrototype must be of type boolean"
);
this._persist = inBool;
this._persistOptions = inOptions;
return this;
}
/**
* Causes the effect to become temporary, which means it will not be stored in the flags of any object,
* even if it .persist() is called
*
* @param {Boolean} inBool
* @returns {EffectSection}
*/
temporary(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"temporary",
"inBool must be of type boolean"
);
this._temporaryEffect = inBool || this._temporaryEffect;
return this;
}
/**
* Sets the effect's playback rate. A playback rate of 2.0 would make it play 2x as fast, 0.5 would make
* it play half as fast.
*
* @param {Number} inNumber
* @returns {EffectSection}
*/
playbackRate(inNumber = 1) {
if (!is_real_number(inNumber))
throw this.sequence._customError(
this,
"playbackRate",
"inNumber must be of type number"
);
this._playbackRate = inNumber;
return this;
}
/**
* Causes the effect to target a location close to the .stretchTowards() location, but not on it.
*
* @param {Boolean} [inBool=true] inBool
* @returns {EffectSection}
*/
missed(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"missed",
"inBool must be of type boolean"
);
this._missed = inBool;
return this;
}
/**
* Adds a function that will run at the end of the effect serialization step, but before it is played. Allows direct
* modifications of effect's data. For example, it could be manipulated to change which file will be used based
* on the distance to the target.
*
* @param {Function} inFunc
* @returns {EffectSection}
*/
addOverride(inFunc) {
if (!is_function$1(inFunc))
throw this.sequence._customError(
this,
"addOverride",
"The given function needs to be an actual function."
);
this._overrides.push(inFunc);
return this;
}
/**
* A smart method that can take a reference to an object, or a direct on the canvas to attach an effect to,
* or a string reference (see .name())
*
* @param {Object|String} inObject
* @param {Object} inOptions
* @returns {EffectSection}
*/
attachTo(inObject, inOptions = {}) {
if (!(typeof inObject === "object" || typeof inObject === "string")) {
throw this.sequence._customError(
this,
"attachTo",
`inObject is invalid, and must be of type of object, string, placeable object, or document`
);
}
if (typeof inOptions !== "object")
throw this.sequence._customError(
this,
"attachTo",
`inOptions must be of type object`
);
inOptions = foundry.utils.mergeObject(
{
align: "center",
edge: "on",
bindVisibility: true,
bindAlpha: true,
bindElevation: true,
followRotation: true,
offset: false,
randomOffset: false,
gridUnits: false,
local: false
},
inOptions
);
const validatedObject = this._validateLocation(inObject);
if (validatedObject === void 0)
throw this.sequence._customError(
this,
"attachTo",
"could not find given object"
);
let isValidObject = true;
if (typeof inObject === "string") {
isValidObject = validatedObject instanceof Token || validatedObject instanceof TokenDocument || validatedObject instanceof Tile || validatedObject instanceof TileDocument || validatedObject instanceof Drawing || validatedObject instanceof DrawingDocument || validatedObject instanceof MeasuredTemplate || validatedObject instanceof MeasuredTemplateDocument || validatedObject instanceof CanvasEffect;
if (!isValidObject) {
this.sequence._showWarning(
this,
"attachTo",
"Only Tokens, Tiles, Drawings, and MeasuredTemplates may have attached effects - will play effect on target's location"
);
}
}
const aligns = Object.keys(alignments);
if (typeof inOptions.align !== "string" || !aligns.includes(inOptions.align)) {
throw this.sequence._customError(
this,
"attachTo",
`inOptions.align must be of type string, one of: ${aligns.join(", ")}`
);
}
if (typeof inOptions.edge !== "string" || !(inOptions.edge === "on" || inOptions.edge === "inner" || inOptions.edge === "outer")) {
throw this.sequence._customError(
this,
"attachTo",
`inOptions.edge must of type string with the value of either "on", "inner", or "outer"`
);
}
if (typeof inOptions.bindVisibility !== "boolean")
throw this.sequence._customError(
this,
"attachTo",
`inOptions.bindVisibility must be of type boolean`
);
if (typeof inOptions.followRotation !== "boolean")
throw this.sequence._customError(
this,
"attachTo",
`inOptions.followRotation must be of type boolean`
);
if (typeof inOptions.bindAlpha !== "boolean")
throw this.sequence._customError(
this,
"attachTo",
"inOptions.bindAlpha must be of type boolean"
);
if (typeof inOptions.bindElevation !== "boolean")
throw this.sequence._customError(
this,
"attachTo",
"inOptions.bindElevation must be of type boolean"
);
if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
throw this.sequence._customError(
this,
"attachTo",
"inOptions.randomOffset must be of type boolean or number"
);
this._source = validatedObject;
this._temporaryEffect = this._temporaryEffect || (validatedObject instanceof foundry.abstract.Document || validatedObject instanceof MeasuredTemplate ? !is_UUID(validatedObject?.uuid) : this._temporaryEffect || false);
if (inOptions.offset) {
const offsetData = this._validateOffset(
"attachTo",
inOptions.offset,
inOptions
);
this._offset = {
source: offsetData,
target: this._offset?.target ?? false
};
}
this._randomOffset = {
source: inOptions.randomOffset,
target: this._randomOffset?.target ?? false
};
this._attachTo = {
active: isValidObject,
align: inOptions.align,
edge: inOptions.edge,
bindVisibility: inOptions.bindVisibility,
bindAlpha: inOptions.bindAlpha,
bindElevation: inOptions.bindElevation,
followRotation: inOptions.followRotation
};
return this;
}
/**
* Causes the effect to be rotated and stretched towards an object, or a direct on the canvas to play the effect at, or a string reference (see .name())
* This effectively calculates the proper X scale for the effect to reach the target
*
* @param {Object|String} inLocation
* @param {Object} inOptions
* @returns {EffectSection}
*/
stretchTo(inLocation, inOptions = {}) {
if (!(typeof inLocation === "object" || typeof inLocation === "string")) {
throw this.sequence._customError(
this,
"stretchTo",
`inLocation is invalid, and must be of type of object, string, placeable object, or document`
);
}
if (typeof inOptions !== "object")
throw this.sequence._customError(
this,
"stretchTo",
`inOptions must be of type object`
);
inOptions = foundry.utils.mergeObject(
{
cacheLocation: false,
attachTo: false,
onlyX: false,
tiling: false,
offset: false,
randomOffset: false,
gridUnits: false,
local: false,
requiresLineOfSight: false,
hideLineOfSight: false
},
inOptions
);
const validatedObject = this._validateLocation(inLocation);
if (validatedObject === void 0)
throw this.sequence._customError(
this,
"stretchTo",
"could not find position of given object"
);
if (typeof inOptions.cacheLocation !== "boolean")
throw this.sequence._customError(
this,
"stretchTo",
"inOptions.cacheLocation must be of type boolean"
);
if (typeof inOptions.attachTo !== "boolean")
throw this.sequence._customError(
this,
"stretchTo",
"inOptions.attachTo must be of type boolean"
);
if (typeof inOptions.onlyX !== "boolean")
throw this.sequence._customError(
this,
"stretchTo",
"inOptions.onlyX must be of type boolean"
);
if (typeof inOptions.tiling !== "boolean")
throw this.sequence._customError(
this,
"stretchTo",
"inOptions.tiling must be of type boolean"
);
if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
throw this.sequence._customError(
this,
"stretchTo",
"inOptions.randomOffset must be of type boolean or number"
);
if (inOptions.cacheLocation && inOptions.attachTo) {
throw this.sequence._customError(
this,
"stretchTo",
"cacheLocation and attachTo cannot both be true - pick one or the other"
);
}
if (typeof inOptions.requiresLineOfSight !== "boolean") {
throw this.sequence._customError(
this,
"stretchTo",
"requiresLineOfSight must be of type boolean"
);
}
if (!inOptions.attachTo && inOptions.requiresLineOfSight) {
throw this.sequence._customError(
this,
"stretchTo",
"requiresLineOfSight requires that attachTo is true"
);
}
if (typeof inOptions.hideLineOfSight !== "boolean") {
throw this.sequence._customError(
this,
"stretchTo",
"hideLineOfSight must be of type boolean"
);
}
if (!inOptions.requiresLineOfSight && inOptions.hideLineOfSight) {
throw this.sequence._customError(
this,
"stretchTo",
"hideLineOfSight requires that requiresLineOfSight is true"
);
}
if (inOptions.tiling)
this.tilingTexture();
this._temporaryEffect = this._temporaryEffect || (validatedObject instanceof foundry.abstract.Document ? !is_UUID(validatedObject?.uuid) : this._temporaryEffect || false);
if (inOptions.offset) {
const offsetData = this._validateOffset(
"stretchTo",
inOptions.offset,
inOptions
);
this._offset = {
source: this._offset?.source ?? false,
target: offsetData
};
}
this._randomOffset = {
source: this._randomOffset?.source ?? false,
target: inOptions.randomOffset
};
this._stretchTo = {
target: inOptions.cacheLocation ? get_object_canvas_data(validatedObject, { measure: true }) : validatedObject,
attachTo: inOptions.attachTo,
onlyX: inOptions.onlyX,
requiresLineOfSight: inOptions.requiresLineOfSight,
hideLineOfSight: inOptions.hideLineOfSight
};
return this;
}
/**
* Sets the location to rotate the object to
*
* @param {object|string} inLocation
* @param {object} inOptions
* @returns this
*/
rotateTowards(inLocation, inOptions = {}) {
if (!(typeof inLocation === "object" || typeof inLocation === "string")) {
throw this.sequence._customError(
this,
"inLocation",
`inLocation is invalid, and must be of type of object, string, placeable object, or document`
);
}
inOptions = foundry.utils.mergeObject(
{
rotationOffset: 0,
cacheLocation: false,
attachTo: false,
offset: false,
randomOffset: false,
local: false,
gridUnits: false
},
inOptions
);
if (!is_real_number(inOptions.rotationOffset))
throw this.sequence._customError(
this,
"rotateTowards",
"inOptions.rotationOffset must be of type number"
);
if (typeof inOptions.attachTo !== "boolean")
throw this.sequence._customError(
this,
"rotateTowards",
"inOptions.attachTo must be of type boolean"
);
if (typeof inOptions.cacheLocation !== "boolean")
throw this.sequence._customError(
this,
"rotateTowards",
"inOptions.cacheLocation must be of type boolean"
);
const validatedObject = this._validateLocation(inLocation);
if (!validatedObject)
throw this.sequence._customError(
this,
"rotateTowards",
"could not find position of given object"
);
this._temporaryEffect = this._temporaryEffect || (validatedObject instanceof foundry.abstract.Document ? !is_UUID(validatedObject?.uuid) : this._temporaryEffect || false);
if (inOptions.offset) {
const offsetData = this._validateOffset(
"attachTo",
inOptions.offset,
inOptions
);
this._offset = {
source: offsetData,
target: this._offset?.target ?? false
};
}
this._randomOffset = {
source: inOptions.randomOffset,
target: this._randomOffset?.target ?? false
};
this._rotateTowards = {
target: inOptions.cacheLocation ? get_object_canvas_data(validatedObject, { measure: true }) : validatedObject,
rotationOffset: inOptions.rotationOffset,
cacheLocation: inOptions.cacheLocation,
attachTo: inOptions.attachTo
};
return this;
}
/**
* Create an effect based on the given object, effectively copying the object as an effect. Useful when you want to do some effect magic on tokens or tiles.
*
* @param {Object} inObject
* @param {Object} inOptions
* @returns {EffectSection}
*/
from(inObject, inOptions = {}) {
if (!(inObject instanceof Token || inObject instanceof Tile || inObject instanceof TokenDocument || inObject instanceof TileDocument)) {
throw this.sequence._customError(
this,
"from",
"inObject must be of type Token, Tile, TokenDocument, or TileDocument"
);
}
if (typeof inOptions !== "object")
throw this.sequence._customError(
this,
"from",
`inOptions must be of type object`
);
inObject = inObject.document ?? inObject;
if (!inObject?.texture?.src)
throw this.sequence._customError(
this,
"from",
"could not find the image for the given object"
);
inOptions = foundry.utils.mergeObject(
{
cacheLocation: false,
offset: false,
randomOffset: false,
local: false,
gridUnits: false
},
inOptions
);
if (typeof inOptions.cacheLocation !== "boolean")
throw this.sequence._customError(
this,
"from",
"inOptions.cacheLocation must be of type boolean"
);
if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
throw this.sequence._customError(
this,
"from",
"inOptions.randomOffset must be of type boolean or number"
);
this._temporaryEffect = this._temporaryEffect || (inObject instanceof foundry.abstract.Document ? !is_UUID(inObject?.uuid) : this._temporaryEffect || false);
if (inOptions.offset) {
const offsetData = this._validateOffset(
"attachTo",
inOptions.offset,
inOptions
);
this._offset = {
source: offsetData,
target: this._offset?.target ?? false
};
}
this._randomOffset = {
source: inOptions.randomOffset,
target: this._randomOffset?.target ?? false
};
this._from = {
object: inObject,
options: inOptions
};
return this;
}
shape(inType, inOptions = {}) {
if (typeof inType !== "string")
throw this.sequence._customError(
this,
"shape",
"type must be of type string"
);
if (!Object.values(CONSTANTS.SHAPES).includes(inType)) {
throw this.sequence._customError(
this,
"shape",
"type must be one of: " + Object.values(CONSTANTS.SHAPES).join(", ")
);
}
if (inType === CONSTANTS.SHAPES.POLY) {
if (!Array.isArray(inOptions.points)) {
throw this.sequence._customError(
this,
"shape",
"if creating polygon, inOptions.points must be of type array"
);
}
inOptions.points = inOptions.points.map((point) => {
if (Array.isArray(point)) {
if (!is_real_number(point[0]) || !is_real_number(point[1])) {
throw this.sequence._customError(
this,
"shape",
"inOptions.points must be an array, containing an array of two numbers or objects with x and y number properties"
);
}
return point;
}
if (typeof point === "object") {
if (!is_real_number(point?.x) || !is_real_number(point?.y)) {
throw this.sequence._customError(
this,
"shape",
"inOptions.points must be an array, containing an array of two numbers or objects with x and y number properties"
);
}
return [point.x, point.y];
}
});
} else if (inType === CONSTANTS.SHAPES.CIRC) {
if (typeof inOptions.radius !== "number") {
throw this.sequence._customError(
this,
"shape",
"if creating circle, inOptions.radius must be of type number"
);
}
} else if (inType === CONSTANTS.SHAPES.RECT || inType === CONSTANTS.SHAPES.RREC || inType === CONSTANTS.SHAPES.ELIP) {
if (inOptions.width ^ inOptions.height) {
inOptions.width = inOptions.width ?? inOptions.height;
inOptions.height = inOptions.height ?? inOptions.width;
}
if (typeof inOptions.width !== "number") {
throw this.sequence._customError(
this,
"shape",
`if creating rectangle, rounded rectangle, or an ellipse, inOptions.width must be of type number`
);
}
if (typeof inOptions.height !== "number") {
throw this.sequence._customError(
this,
"shape",
"if creating rectangle, rounded rectangle, or an ellipse, inOptions.height must be of type number"
);
}
if (inType === CONSTANTS.SHAPES.RREC && typeof inOptions.radius !== "number") {
throw this.sequence._customError(
this,
"shape",
"if creating rounded border rectangle, inOptions.radius must be of type number"
);
}
}
if (inOptions.gridUnits !== void 0 && typeof inOptions.gridUnits !== "boolean") {
throw this.sequence._customError(
this,
"shape",
"inOptions.gridUnits must be of type boolean"
);
}
if (inOptions.name && typeof inOptions.name !== "string") {
throw this.sequence._customError(
this,
"shape",
"inOptions.name must be of type string"
);
}
if (inOptions.fillColor && !is_real_number(inOptions.fillColor) && typeof inOptions.fillColor !== "string") {
throw this.sequence._customError(
this,
"shape",
"inOptions.fillColor must be of type string (hexadecimal) or number (decimal)"
);
} else {
inOptions.fillColor = parseColor(inOptions.fillColor).decimal;
}
if (inOptions.fillAlpha && !is_real_number(inOptions.fillAlpha)) {
throw this.sequence._customError(
this,
"shape",
"inOptions.fillAlpha must be of type number"
);
}
if (inOptions.alpha && !is_real_number(inOptions.alpha)) {
throw this.sequence._customError(
this,
"shape",
"inOptions.alpha must be of type number"
);
}
if (inOptions.lineSize && !is_real_number(inOptions.lineSize)) {
throw this.sequence._customError(
this,
"shape",
"inOptions.lineSize must be of type number"
);
}
if (inOptions.lineColor && !is_real_number(inOptions.lineColor) && typeof inOptions.lineColor !== "string") {
throw this.sequence._customError(
this,
"shape",
"inOptions.lineColor must be of type string (hexadecimal) or number (decimal)"
);
} else {
inOptions.lineColor = parseColor(inOptions.lineColor).decimal;
}
if (inOptions.offset) {
inOptions.offset = this._validateOffset(
"shape",
inOptions.offset,
inOptions.offset
);
}
if (inOptions.texture !== void 0 && typeof inOptions.texture !== "string") {
throw this.sequence._customError(
this,
"shape",
"inOptions.texture must be of type string"
);
}
if (inOptions.isMask !== void 0 && typeof inOptions.isMask !== "boolean") {
throw this.sequence._customError(
this,
"shape",
"inOptions.isMask must be of type boolean"
);
}
this._shapes.push({
...inOptions,
type: inType
});
return this;
}
/**
* Causes the effect to be offset relative to its location based on a given vector
*
* @param {Object} inOffset
* @param {Object} inOptions
* @returns {EffectSection}
*/
offset(inOffset, inOptions = {}) {
this.sequence._showWarning(
this,
"offset",
"This method is becoming deprecated, please use the secondary offset option in atLocation, attachTo, stretchTo instead.",
true
);
if (inOffset === void 0)
throw this.sequence._customError(
this,
"offset",
"inOffset must not be undefined"
);
if (typeof inOptions !== "object")
throw this.sequence._customError(
this,
"offset",
"options must be of type object"
);
this._offsetLegacy = this._validateOffset("offset", inOffset, inOptions);
return this;
}
/**
* Causes the effect's sprite to be offset relative to its location based on a given vector
*
* @param {Object} inOffset
* @param {Object} inOptions
* @returns {EffectSection}
*/
spriteOffset(inOffset, inOptions = {}) {
if (inOffset === void 0)
throw this.sequence._customError(
this,
"spriteOffset",
"inOffset must not be undefined"
);
if (typeof inOptions !== "object")
throw this.sequence._customError(
this,
"spriteOffset",
"options must be of type object"
);
this._spriteOffset = this._validateOffset(
"spriteOffset",
inOffset,
inOptions
);
return this;
}
/**
* Causes the final effect location to be snapped to the grid
*
* @param {Boolean} inBool
* @returns {EffectSection}
*/
snapToGrid(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"snapToGrid",
"inBool must be of type boolean"
);
this._snapToGrid = inBool;
return this;
}
/**
* Causes the effect to be scaled to the target object's width
*
* @param {Number} inScale
* @param {Object} inOptions
* @returns {EffectSection}
*/
scaleToObject(inScale = 1, inOptions = {}) {
if (!is_real_number(inScale))
throw this.sequence._customError(
this,
"scaleToObject",
`inScale must be of type number!`
);
if (typeof inOptions !== "object")
throw this.sequence._customError(
this,
"scaleToObject",
"inOptions must be of type object"
);
inOptions = foundry.utils.mergeObject(
{
scale: inScale,
considerTokenScale: false,
uniform: false
},
inOptions
);
if (typeof inOptions.uniform !== "boolean")
throw this.sequence._customError(
this,
"scaleToObject",
"inBool must be of type boolean"
);
this._scaleToObject = inOptions;
return this;
}
/**
* Sets the width and the height of the effect in pixels, this size is set before any scaling
*
* @param {Number|Object<{width: {Number}, height: {Number}}>} inSize
* @param {Object} inOptions
* @returns {EffectSection}
*/
size(inSize, inOptions = {}) {
if (!is_real_number(inSize) && typeof inSize !== "object")
throw this.sequence._customError(
this,
"size",
"inSize must be of type number or object"
);
if (typeof inOptions !== "object")
throw this.sequence._customError(
this,
"size",
"inOptions must be of type object"
);
if (is_real_number(inSize)) {
inSize = {
width: inSize,
height: inSize
};
}
if (inSize.width === void 0 ^ inSize.height === void 0) {
if (inSize.width) {
if (!is_real_number(inSize.width))
throw this.sequence._customError(
this,
"size",
"inSize.width must be of type number or string 'auto'"
);
inSize["height"] = "auto";
} else {
if (!is_real_number(inSize.height))
throw this.sequence._customError(
this,
"size",
"inSize.height must be of type number or string 'auto'"
);
inSize["width"] = "auto";
}
}
inOptions = foundry.utils.mergeObject(
{
gridUnits: false
},
inOptions
);
if (!is_real_number(inSize.width) && inSize.width !== "auto")
throw this.sequence._customError(
this,
"size",
"inSize.width must be of type number or string 'auto'"
);
if (!is_real_number(inSize.height) && inSize.height !== "auto")
throw this.sequence._customError(
this,
"size",
"inSize.height must be of type number or string 'auto'"
);
if (typeof inOptions.gridUnits !== "boolean")
throw this.sequence._customError(
this,
"size",
"inOptions.gridUnits must be of type boolean"
);
this._size = {
width: inSize.width ?? canvas.grid.size,
height: inSize.height ?? canvas.grid.size,
...inOptions
};
return this;
}
/**
* This scales the sprite of the effect, and this method can take the following:
* - A number to set the scale uniformly
* - An object with x and y for non-uniform scaling
* - Two numbers which the Sequencer will randomly pick a uniform scale between
*
* @param {number|object} inScaleMin
* @param {number} [inScaleMax] inScaleMax
* @returns this
*/
spriteScale(inScaleMin, inScaleMax) {
if (!is_real_number(inScaleMin) && typeof inScaleMin !== "object")
throw this.sequence._customError(
this,
"spriteScale",
"inScale must be of type number or object"
);
if (is_real_number(inScaleMin)) {
if (inScaleMax && !is_real_number(inScaleMax)) {
throw this.sequence._customError(
this,
"spriteScale",
"if inScaleMin is a number, inScaleMax must also be of type number"
);
}
}
this._spriteScaleMin = inScaleMin;
this._spriteScaleMax = inScaleMax ?? false;
return this;
}
/**
* This defines the internal padding of this effect. Gridsize determines the internal grid size of this effect which will determine how big it is on the canvas
* relative to the canvas's grid size. Start and end point defines padding at the left and right of the effect
*
* @param {Number} gridSize
* @param {Number} startPoint
* @param {Number} endPoint
* @returns {EffectSection}
*/
template({ gridSize, startPoint, endPoint } = {}) {
if (gridSize && !is_real_number(gridSize))
throw this.sequence._customError(
this,
"template",
"gridSize must be of type number"
);
if (startPoint && !is_real_number(startPoint))
throw this.sequence._customError(
this,
"template",
"startPoint must be of type number"
);
if (endPoint && !is_real_number(endPoint))
throw this.sequence._customError(
this,
"template",
"endPoint must be of type number"
);
if (!gridSize && !startPoint && !endPoint)
throw this.sequence._customError(
this,
"template",
"You need to define at least one parameter!"
);
if (!this._template)
this._template = {};
if (gridSize)
this._template["gridSize"] = gridSize;
if (startPoint)
this._template["startPoint"] = startPoint;
if (endPoint)
this._template["endPoint"] = endPoint;
return this;
}
/**
* This makes the texture of the effect tile, effectively repeat itself within the sprite's dimensions
*
* @param {Object|Number} scale
* @param {Object} position
* @returns {EffectSection}
*/
tilingTexture(scale2 = { x: 1, y: 1 }, position = { x: 0, y: 0 }) {
if (is_real_number(scale2)) {
scale2 = { x: scale2, y: scale2 };
}
scale2 = { x: scale2?.x ?? 1, y: scale2?.y ?? 1 };
if (!is_real_number(scale2.x))
throw this.sequence._customError(
this,
"tilingTexture",
`scale.x must be of type number!`
);
if (!is_real_number(scale2.y))
throw this.sequence._customError(
this,
"tilingTexture",
`scale.y must be of type number!`
);
position = { x: position?.x ?? 0, y: position?.y ?? 0 };
if (!is_real_number(position.x))
throw this.sequence._customError(
this,
"tilingTexture",
`position.x must be of type number!`
);
if (!is_real_number(position.y))
throw this.sequence._customError(
this,
"tilingTexture",
`position.y must be of type number!`
);
this._tilingTexture = {
scale: scale2,
position
};
return this;
}
/**
* Anchors the sprite's container according to the given x and y coordinates, or uniformly based on a single number
*
* @param {Number|Object} inAnchor
* @returns {EffectSection}
*/
anchor(inAnchor) {
if (is_real_number(inAnchor)) {
inAnchor = {
x: inAnchor,
y: inAnchor
};
}
inAnchor = {
x: inAnchor?.x ?? 0.5,
y: inAnchor?.y ?? 0.5
};
if (!is_real_number(inAnchor.x))
throw this.sequence._customError(
this,
"anchor",
`inAnchor.x must be of type number!`
);
if (!is_real_number(inAnchor.y))
throw this.sequence._customError(
this,
"anchor",
`inAnchor.y must be of type number!`
);
this._anchor = inAnchor;
return this;
}
/**
* Anchors the sprite according to the given x and y coordinates, or uniformly based on a single number
*
* @param {Number|Object} inAnchor
* @returns {EffectSection}
*/
spriteAnchor(inAnchor) {
if (is_real_number(inAnchor)) {
inAnchor = {
x: inAnchor,
y: inAnchor
};
}
inAnchor = {
x: inAnchor?.x ?? 0.5,
y: inAnchor?.y ?? 0.5
};
if (!is_real_number(inAnchor.x))
throw this.sequence._customError(
this,
"anchor",
`inAnchor.x must be of type number!`
);
if (!is_real_number(inAnchor.y))
throw this.sequence._customError(
this,
"anchor",
`inAnchor.y must be of type number!`
);
this._spriteAnchor = inAnchor;
return this;
}
/**
* Centers the sprite, effectively giving it an anchor of {x: 0.5, y: 0.5}
*
* Note: If this is used, it will override the anchor set by Aim Towards, which sets the sprite's anchor to the
* outermost edge of the location the sprite is played at
*
* @returns {EffectSection}
*/
center() {
this.anchor(0.5);
return this;
}
/**
* The sprite gets a random offset on its target location, usually within the object's bounds. The optional parameter
* scales how much offset should be added. Defaults to 1.0, which covers the entire target position, 0.5 would cover half.
*
* @param {Number} inOffsetScale
* @returns {EffectSection}
*/
randomOffset(inOffsetScale = 1) {
this.sequence._showWarning(
this,
"randomOffset",
"This method has been deprecated, please use randomOffset as a second parameter on atLocation, stretchTo, etc.",
true
);
if (!is_real_number(inOffsetScale))
throw this.sequence._customError(
this,
"randomOffset",
"inBool must be of type number"
);
this._randomOffsetLegacy = inOffsetScale;
return this;
}
/**
* The sprite gets a randomized flipped X scale. If the scale on that axis was 1, it can
* become 1 or -1, effectively mirroring the sprite on its horizontal axis
*
* @param {Boolean} inBool
* @returns {EffectSection}
*/
randomizeMirrorX(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"randomizeMirrorX",
"inBool must be of type boolean"
);
this._randomMirrorX = inBool;
return this;
}
/**
* The sprite gets a randomized flipped Y scale. If the scale on that axis was 1, it can
* become 1 or -1, effectively mirroring the sprite on its vertical axis
*
* @param {Boolean} inBool
* @returns {EffectSection}
*/
randomizeMirrorY(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"randomizeMirrorY",
"inBool must be of type boolean"
);
this._randomMirrorY = inBool;
return this;
}
/**
* The sprite gets a flipped X scale. If the scale on that axis was 1, it will become 1 or -1, effectively
* mirroring the sprite on its horizontal axis
*
* @param {Boolean} inBool
* @returns {EffectSection}
*/
mirrorX(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"mirrorX",
"inBool must be of type boolean"
);
this._mirrorX = inBool;
return this;
}
/**
* The sprite gets a flipped Y scale. If the scale on that axis was 1, it will become 1 or -1, effectively
* mirroring the sprite on its vertical axis
*
* @param {Boolean} inBool
* @returns {EffectSection}
*/
mirrorY(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"mirrorY",
"inBool must be of type boolean"
);
this._mirrorY = inBool;
return this;
}
/**
* Causes the effect to play beneath most tokens
*
* @param {Boolean} inBool
* @returns {EffectSection}
*/
belowTokens(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"belowTokens",
"inBool must be of type boolean"
);
if (!inBool)
return this;
return this.elevation(0, { absolute: true });
}
/**
* Causes the effect to play beneath most tiles
*
* @param {Boolean} inBool
* @returns {EffectSection}
*/
belowTiles(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"belowTokens",
"inBool must be of type boolean"
);
if (!inBool)
return this;
return this.elevation(-1, { absolute: true });
}
/**
* Causes the effect to be played on top of the vision mask
*
* @param {Boolean} inBool
* @returns {EffectSection}
*/
aboveLighting(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"aboveLighting",
"inBool must be of type boolean"
);
this._aboveLighting = inBool;
return this;
}
/**
* Causes the effect to be played on top of interface
*
* @param {Boolean} inBool
* @returns {EffectSection}
*/
aboveInterface(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"aboveInterface",
"inBool must be of type boolean"
);
this._aboveInterface = inBool;
return this;
}
/**
* Changes the effect's elevation
*
* @param {Number} inElevation
* @param {Object} inOptions
* @returns {EffectSection}
*/
elevation(inElevation, inOptions = {}) {
if (typeof inElevation !== "number")
throw this.sequence._customError(
this,
"elevation",
"inElevation must be of type number"
);
if (typeof inOptions !== "object")
throw this.sequence._customError(
this,
"elevation",
`inOptions must be of type object`
);
inOptions = foundry.utils.mergeObject(
{
elevation: 1,
absolute: false
},
inOptions
);
if (typeof inOptions.absolute !== "boolean")
throw this.sequence._customError(
this,
"elevation",
"inOptions.absolute must be of type boolean"
);
this._elevation = {
elevation: inElevation,
absolute: inOptions.absolute
};
return this;
}
/**
* Sets the zIndex of the effect, potentially displaying it on top of other effects the same elevation
*
* @param {Number} inZIndex
* @returns {EffectSection}
*/
zIndex(inZIndex) {
if (!is_real_number(inZIndex))
throw this.sequence._customError(
this,
"zIndex",
"inZIndex must be of type number"
);
this._zIndex = inZIndex;
return this;
}
/**
* This method only modifies .persist()ed effects and causes them to not immediately end, but stick around for the given duration passed to this method.
*
* @param {Number} inExtraDuration
* @returns {EffectSection}
*/
extraEndDuration(inExtraDuration) {
if (!is_real_number(inExtraDuration))
throw this.sequence._customError(
this,
"extraEndDuration",
"inExtraDuration must be of type number"
);
this._extraEndDuration = inExtraDuration;
return this;
}
/**
* Rotates the sprite
*
* @param {Number} inAngle
* @returns {EffectSection}
*/
spriteRotation(inAngle) {
if (!is_real_number(inAngle))
throw this.sequence._customError(
this,
"spriteRotation",
"inAngle must be of type number"
);
this._spriteRotation = inAngle;
return this;
}
/**
* Rotates the sprite
*
* @param {Boolean} inBool
* @returns {EffectSection}
*/
randomSpriteRotation(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"randomSpriteRotation",
"inBool must be of type boolean"
);
this._randomSpriteRotation = inBool;
return this;
}
/**
* Causes the effect to not rotate should its container rotate
*
* @param {Boolean} [inBool=true] inBool
* @returns {EffectSection}
*/
zeroSpriteRotation(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"zeroSpriteRotation",
"inBool must be of type boolean"
);
this._zeroSpriteRotation = inBool;
return this;
}
/**
* If the effect would loop due to its duration or persistence, this causes it not to
*
* @param {Boolean} [inBool=true] inBool
* @returns {EffectSection}
*/
noLoop(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"noLoop",
"inBool must be of type boolean"
);
this._noLoop = inBool;
return this;
}
/**
* Causes the effect to not show up in the Effect Manager UI - DO NOT USE UNLESS YOU KNOW WHAT YOU ARE DOING
*
* @param inBool
* @returns {EffectSection}
*/
private(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"private",
"inBool must be of type boolean"
);
this._private = inBool;
return this;
}
/**
* Causes the effect to be played in screen space instead of world space (where tokens are)
*
* @param {Boolean} [inBool=true] inBool
* @returns {EffectSection}
*/
screenSpace(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"screenSpace",
"inBool must be of type boolean"
);
this._screenSpace = inBool;
this._screenSpaceAnchor = this._screenSpaceAnchor ?? { x: 0.5, y: 0.5 };
return this;
}
/**
* Causes the effect to be played above all of the UI elements
*
* @param {Boolean} [inBool=true] inBool
* @returns {EffectSection}
*/
screenSpaceAboveUI(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"screenSpaceAboveUI",
"inBool must be of type boolean"
);
this._screenSpaceAboveUI = inBool;
return this;
}
/**
* Positions the effect in a screen space position, offset from its .screenSpaceAnchor()
*
* @param {Object} inPosition
* @returns {EffectSection}
*/
screenSpacePosition(inPosition) {
inPosition = {
x: inPosition?.x ?? 0,
y: inPosition?.y ?? 0
};
if (!is_real_number(inPosition.x))
throw this.sequence._customError(
this,
"screenSpacePosition",
`inPosition.x must be of type number!`
);
if (!is_real_number(inPosition.y))
throw this.sequence._customError(
this,
"screenSpacePosition",
`inPosition.y must be of type number!`
);
this._screenSpacePosition = inPosition;
return this;
}
/**
* Anchors the sprite according to the given x and y coordinates, or uniformly based on a single number in screen space
*
* @param {Number|Object} inAnchor
* @returns {EffectSection}
*/
screenSpaceAnchor(inAnchor) {
if (is_real_number(inAnchor)) {
inAnchor = {
x: inAnchor,
y: inAnchor
};
}
inAnchor = {
x: inAnchor?.x ?? 0.5,
y: inAnchor?.y ?? 0.5
};
if (!is_real_number(inAnchor.x))
throw this.sequence._customError(
this,
"screenSpaceAnchor",
`inAnchor.x must be of type number!`
);
if (!is_real_number(inAnchor.y))
throw this.sequence._customError(
this,
"screenSpaceAnchor",
`inAnchor.y must be of type number!`
);
this._screenSpaceAnchor = inAnchor;
return this;
}
/**
* Sets up various properties relating to scale of the effect on the screen
*
* @param {Object} inOptions
* @returns {EffectSection}
*/
screenSpaceScale(inOptions) {
if (typeof inOptions !== "object")
throw this.sequence._customError(
this,
"screenSpaceScale",
`inOptions must be of type object`
);
inOptions = foundry.utils.mergeObject(
{
x: 1,
y: 1,
fitX: false,
fitY: false,
ratioX: false,
ratioY: false
},
inOptions
);
if (!is_real_number(inOptions.x))
throw this.sequence._customError(
this,
"screenSpaceScale",
`inOptions.x must be of type number!`
);
if (!is_real_number(inOptions.y))
throw this.sequence._customError(
this,
"screenSpaceScale",
`inOptions.y must be of type number!`
);
if (typeof inOptions.fitX !== "boolean")
throw this.sequence._customError(
this,
"screenSpaceScale",
"inOptions.fitX must be of type boolean"
);
if (typeof inOptions.fitY !== "boolean")
throw this.sequence._customError(
this,
"screenSpaceScale",
"inOptions.fitY must be of type boolean"
);
if (typeof inOptions.ratioX !== "boolean")
throw this.sequence._customError(
this,
"screenSpaceScale",
"inOptions.ratioX must be of type boolean"
);
if (typeof inOptions.ratioY !== "boolean")
throw this.sequence._customError(
this,
"screenSpaceScale",
"inOptions.ratioY must be of type boolean"
);
if (inOptions.ratioX && inOptions.ratioY)
throw this.sequence._customError(
this,
"screenSpaceScale",
"both ratioX and ratioY cannot be true, one axis must fit or be set directly"
);
this._screenSpaceScale = inOptions;
return this;
}
/**
* This is for adding extra information to an effect, like the origin of the effect in the form of the item's uuid.
* The method accepts a string or a Document that has an UUID.
*
* @param {string|document} inOrigin
* @returns {Section}
*/
origin(inOrigin) {
inOrigin = validate_document(inOrigin);
if (inOrigin instanceof foundry.abstract.Document) {
inOrigin = inOrigin?.uuid;
if (!inOrigin)
throw this.sequence._customError(
this,
"origin",
"could not find the UUID for the given Document"
);
}
if (typeof inOrigin !== "string")
throw this.sequence._customError(
this,
"origin",
"inOrigin must be of type string"
);
this._origin = inOrigin;
return this;
}
/**
* Ties the effect to any number of documents in Foundry - if those get deleted, the effect is ended.
*
* @param {String|PlaceableObject|foundry.abstract.Document|Array} inDocuments
* @returns {EffectSection}
*/
tieToDocuments(inDocuments) {
if (!Array.isArray(inDocuments)) {
inDocuments = [inDocuments];
}
for (let doc of inDocuments) {
if (typeof doc !== "string" && !(doc instanceof PlaceableObject) && !(doc instanceof foundry.abstract.Document)) {
throw this.sequence._customError(
this,
"tieToDocument",
"inOrigin must be of type string, PlaceableObject, or Document, or an array thereof"
);
}
if (typeof doc === "string") {
const obj = fromUuidSync(doc);
if (!obj)
throw this.sequence._customError(
this,
"tieToDocument",
`could not find document with UUID "${doc}"`
);
} else {
doc = validate_document(doc);
if (doc instanceof foundry.abstract.Document) {
doc = doc?.uuid;
if (!doc)
throw this.sequence._customError(
this,
"tieToDocument",
"could not find the UUID for the given object"
);
}
}
this._tiedDocuments.push(doc);
}
return this;
}
/**
* Masks the effect to the given object or objects. If no object is given, the effect will be masked to the source
* of the effect.
*
* @param {Token/TokenDocument/Tile/TileDocument/Drawing/DrawingDocument/MeasuredTemplate/MeasuredTemplateDocument/Array} inObject
* @returns {Section}
*/
mask(inObject) {
if (!inObject) {
this._selfMask = true;
return this;
}
if (Array.isArray(inObject)) {
for (let obj of inObject) {
this.mask(obj);
}
return this;
}
const validatedObject = this._validateLocation(inObject);
const isValidObject = validatedObject instanceof TokenDocument || validatedObject instanceof TileDocument || validatedObject instanceof DrawingDocument || validatedObject instanceof MeasuredTemplateDocument;
if (!isValidObject) {
throw this.sequence._customError(
this,
"mask",
"A foundry object was provided, but only Tokens, Tiles, Drawings, and MeasuredTemplates may be used to create effect masks"
);
}
this._masks.push(get_object_identifier(validatedObject));
return this;
}
/**
* Causes the effect to be visible through walls
*
* @param inBool
* @returns {EffectSection}
*/
xray(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"xray",
"inBool must be of type boolean"
);
this._xray = inBool;
return this;
}
/**
* Configures the isometric configuration of this effect
*
* @param inOptions
* @returns {EffectSection}
*/
isometric(inOptions = {}) {
if (typeof inOptions !== "object")
throw this.sequence._customError(
this,
"isometric",
`inOptions must be of type object`
);
inOptions = foundry.utils.mergeObject(
{
overlay: false
},
inOptions
);
if (typeof inOptions?.overlay !== "boolean")
throw this.sequence._customError(
this,
"isometric",
"inOptions.overlay must be of type boolean"
);
this._isometric = inOptions;
return this;
}
/**
* @private
*/
_expressWarnings() {
if (this._stretchTo && this._anchor?.x) {
this.sequence._showWarning(
this,
"stretchTo",
"you have called .stretchTo() and .anchor() - stretchTo will manually set the X axis of the anchor and may not behave like you expect.",
true
);
}
if (this._stretchTo && this._scaleToObject) {
throw this.sequence._customError(
this,
"stretchTo",
"You're trying to stretch towards an object, while scaling to fit another??? Make up your mind!"
);
}
if (this._stretchTo && this._randomRotation) {
throw this.sequence._customError(
this,
"stretchTo",
"You're trying to stretch towards an object, while trying to randomly rotate the effect? What?"
);
}
if (this._stretchTo && this._moveTowards) {
throw this.sequence._customError(
this,
"stretchTo",
"You're trying to stretch towards an object, while moving towards it? You're insane."
);
}
if (this._attachTo && this._stretchTo?.attachTo && (this._startTime || this._endTime) && this._isRangedEffect) {
throw this.sequence._customError(
this,
"stretchTo",
"Dual-attached range-finding effects combined while using any of the time methods is stable - modern web browsers cannot handle it and it may crash them, so this feature has been disabled."
);
}
const source = this._getSourceObject();
const target = this._getTargetObject();
if (!this._screenSpace && this._persistOptions?.persistTokenPrototype && this._masks.filter((uuid) => uuid !== source).length > 0) {
this.sequence._showWarning(
this,
"persist",
"You have applied persistTokenPrototype with multiple masks from objects in the scene - these will not be persisted across scenes!",
true
);
}
if (!source && !target && !this._screenSpace) {
throw this.sequence._customError(
this,
"play",
"Could not determine where to play the effect!"
);
}
}
/**
* @OVERRIDE
*/
async preRun() {
if (this._from) {
this._file = this._file || this._from.object?.texture?.src;
if (this._source === null) {
this._source = this._validateLocation(this._from.object);
}
if (this._size === null) {
const size = get_object_dimensions(this._from.object);
this._size = {
width: size?.width ?? canvas.grid.size,
height: size?.height ?? canvas.grid.size,
gridUnits: false
};
}
if (this._mirrorX === null && (this._from.object.mirrorX || this._from.object?.tile && this._from.object?.tile.scale.x < 0)) {
this._mirrorX = true;
}
if (this._mirrorY === null && (this._from.object.mirrorY || this._from.object?.tile && this._from.object?.tile.scale.y < 0)) {
this._mirrorY = true;
}
if (this._angle === null && this._from.object?.rotation) {
this._angle = -this._from.object.rotation;
}
this._randomOffset = {
source: this._randomOffset?.source ?? this._from.options.randomOffset,
target: this._randomOffset?.target ?? false
};
}
}
/**
* @OVERRIDE
* @returns {Promise}
*/
async run() {
if (!user_can_do("permissions-effect-create") || !this._playEffect) {
if (!user_can_do("permissions-effect-create")) {
debounce(EffectSection.debounceWarning, 1e3);
}
return new Promise((resolve) => {
resolve();
});
}
if (!this._deserializedData)
this._expressWarnings();
const data = await this._sanitizeEffectData();
if (Hooks.call("preCreateSequencerEffect", data) === false)
return;
let push = !(data?.users?.length === 1 && data?.users?.includes(game.userId)) && !this.sequence.localOnly;
let canvasEffectData = await Sequencer.EffectManager.play(data, push);
let totalDuration = this._currentWaitTime;
if (this._persist) {
totalDuration += await canvasEffectData.promise;
} else {
totalDuration += await canvasEffectData.duration;
}
await new Promise((resolve) => setTimeout(resolve, totalDuration));
}
/**
* @private
*/
_applyTraits() {
Object.assign(this.constructor.prototype, traits.files);
Object.assign(this.constructor.prototype, traits.audio);
Object.assign(this.constructor.prototype, traits.moves);
Object.assign(this.constructor.prototype, traits.opacity);
Object.assign(this.constructor.prototype, traits.rotation);
Object.assign(this.constructor.prototype, traits.scale);
Object.assign(this.constructor.prototype, traits.time);
Object.assign(this.constructor.prototype, traits.users);
Object.assign(this.constructor.prototype, traits.animation);
Object.assign(this.constructor.prototype, traits.filter);
Object.assign(this.constructor.prototype, traits.tint);
Object.assign(this.constructor.prototype, traits.location);
Object.assign(this.constructor.prototype, traits.offset);
Object.assign(this.constructor.prototype, traits.text);
}
/**
* @private
*/
async _initialize() {
if (this._name) {
if (!this.sequence.nameOffsetMap) {
this.sequence.nameOffsetMap = {};
}
if (!this.sequence.nameOffsetMap[this._name]) {
const source = this._getSourceObject();
const target = this._getTargetObject();
if (this._offsetLegacy && !this._offset) {
this._offset = {
source: !target ? this._offsetLegacy : false,
target: !!target ? this._offsetLegacy : false
};
}
if (this._randomOffsetLegacy && !this._randomOffset) {
this._randomOffset = {
source: !target ? this._randomOffsetLegacy : false,
target: !!target ? this._randomOffsetLegacy : false
};
}
this.sequence.nameOffsetMap[this._name] = {
seed: `${this._name}-${randomID()}`,
source,
target,
randomOffset: this._randomOffset,
missed: this._missed,
offset: this._offset,
repetitions: this._repetitions,
twister: {}
};
}
}
if (!this._file && !this._from && !this._text && !this._shapes.length && this.sequence.softFail) {
this._playEffect = false;
return;
}
let fileData = this._file ? await this._determineFile(this._file) : {
file: this._file,
forcedIndex: false,
customRange: false
};
this._isRangedEffect = fileData?.file?.rangeFind;
if (fileData.customRange || fileData.file?.dbPath)
return;
let exists = false;
try {
exists = await SequencerFileCache.srcExists(fileData.file);
} catch (err) {
}
if (!exists) {
if (this.sequence.softFail) {
this._playEffect = false;
return;
}
throw this.sequence._customError(
this,
"Play",
`Could not find file:
${fileData.file}`
);
}
}
/**
* @private
*/
_getSourceObject() {
if (!this._source || typeof this._source !== "object")
return this._source;
if (this._source?.cachedLocation || !this._attachTo) {
return get_object_canvas_data(this._source);
}
return get_object_identifier(this._source) ?? get_object_canvas_data(this._source);
}
/**
* @private
*/
_getTargetObject() {
if (!this._target?.target)
return this._target;
if (typeof this._target.target !== "object")
return this._target.target;
if (this._target?.target?.cachedLocation || !(this._stretchTo?.attachTo || this._rotateTowards?.attachTo)) {
return get_object_canvas_data(this._target.target, true);
}
return get_object_identifier(this._target.target) ?? get_object_canvas_data(this._target.target, true);
}
/**
* @private
*/
async _sanitizeEffectData() {
if (this._deserializedData) {
this._deserializedData.creationTimestamp = +new Date();
this._deserializedData.remote = true;
return this._deserializedData;
}
const { file, forcedIndex, customRange } = this._file && this._playEffect ? await this._determineFile(this._file) : {
file: this._file,
forcedIndex: false,
customRange: false
};
const source = this._getSourceObject();
const target = this._getTargetObject();
if (this._offsetLegacy) {
this._offset = {
source: !target && this._offset?.source ? this._offsetLegacy : this._offset?.source,
target: !!target && this._offset?.target ? this._offsetLegacy : this._offset?.target
};
}
if (this._randomOffsetLegacy) {
this._randomOffset = {
source: !target && this._randomOffset?.source ? this._randomOffsetLegacy : this._randomOffset?.source,
target: !!target && this._randomOffset?.target ? this._randomOffsetLegacy : this._randomOffset?.target
};
}
if (this._selfMask) {
this._masks.push(
get_object_identifier(this._source) ?? get_object_canvas_data(this._source)
);
}
let sceneId = game.user.viewedScene;
if (is_UUID(source)) {
const potentialSceneId = source.split(".")[1];
if (game.scenes.get(potentialSceneId)) {
sceneId = potentialSceneId;
}
} else if (is_UUID(target)) {
const potentialSceneId = target.split(".")[1];
if (game.scenes.get(potentialSceneId)) {
sceneId = potentialSceneId;
}
}
let data = foundry.utils.duplicate({
/**
* Core properties
*/
_id: randomID(),
flagVersion: flagManager.latestFlagVersion,
sequenceId: this.sequence.id,
creationTimestamp: +new Date(),
sceneId,
creatorUserId: game.userId,
moduleName: this.sequence.moduleName,
users: this._users ? Array.from(this._users) : false,
name: this._name,
origin: this._origin,
index: this.sequence.effectIndex,
repetition: this._currentRepetition,
private: this._private,
temporary: this._temporaryEffect,
tiedDocuments: Array.from(new Set(this._tiedDocuments)),
/**
* Source/target properties
*/
source,
target,
rotateTowards: this._rotateTowards,
stretchTo: this._stretchTo ? {
attachTo: this._stretchTo.attachTo,
onlyX: this._stretchTo.onlyX,
requiresLineOfSight: this._stretchTo.requiresLineOfSight,
hideLineOfSight: this._stretchTo.hideLineOfSight
} : false,
moveTowards: this._moveTowards ? {
ease: this._moveTowards.ease,
rotate: this._moveTowards.rotate
} : false,
attachTo: this._attachTo,
missed: this._missed,
/**
* Sprite properties
*/
file: file?.dbPath ?? file,
customRange,
forcedIndex,
text: this._text,
tilingTexture: this._tilingTexture,
masks: Array.from(new Set(this._masks)),
shapes: this._shapes,
volume: this._volume,
isometric: this._isometric,
// Transforms
scale: this._getCalculatedScale("scale"),
spriteScale: this._getCalculatedScale("spriteScale"),
angle: this._angle,
size: this._size,
offset: this._offset,
anchor: this._anchor,
spriteOffset: this._spriteOffset,
spriteAnchor: this._spriteAnchor,
template: this._template,
zeroSpriteRotation: this._zeroSpriteRotation,
randomOffset: this._randomOffset,
randomRotation: this._randomRotation,
scaleToObject: this._scaleToObject,
elevation: this._elevation,
aboveLighting: this._aboveLighting,
aboveInterface: this._aboveInterface,
xray: this._xray,
// Appearance
zIndex: this._zIndex,
opacity: is_real_number(this._opacity) ? this._opacity : 1,
filters: this._filters,
noLoop: this._noLoop,
spriteRotation: this._spriteRotation,
randomSpriteRotation: this._randomSpriteRotation,
tint: this._tint?.decimal,
flipX: this._mirrorX || this._randomMirrorX && Math.random() < 0.5,
flipY: this._mirrorY || this._randomMirrorY && Math.random() < 0.5,
/**
* Time properties
*/
duration: this._duration,
persist: this._persist,
persistOptions: this._persistOptions,
playbackRate: this._playbackRate,
extraEndDuration: this._extraEndDuration,
time: this._startTime || this._endTime ? {
start: is_real_number(this._startTime) ? {
value: this._startTime,
isPerc: this._startPerc
} : false,
end: is_real_number(this._endTime) ? {
value: this._endTime,
isPerc: this._endPerc
} : false,
isRange: this._isRange
} : false,
/**
* Animation properties
*/
moves: this._moveTowards,
moveSpeed: this._moveSpeed,
fadeIn: this._fadeIn,
fadeOut: this._fadeOut,
scaleIn: this._scaleIn,
scaleOut: this._scaleOut,
rotateIn: this._rotateIn,
rotateOut: this._rotateOut,
fadeInAudio: this._fadeInAudio,
fadeOutAudio: this._fadeOutAudio,
animations: this._animations,
/**
* Screenspace properties
*/
screenSpace: this._screenSpace,
screenSpaceAboveUI: this._screenSpaceAboveUI,
screenSpaceAnchor: this._screenSpaceAnchor,
screenSpacePosition: this._screenSpacePosition,
screenSpaceScale: this._screenSpaceScale,
nameOffsetMap: this.sequence.nameOffsetMap
});
for (let override of this._overrides) {
data = await override(this, data);
}
if ((typeof data.file !== "string" || data.file === "") && !data.text && !data.shapes && !data.customRange) {
throw this.sequence._customError(
this,
"file",
"an effect must have a file, text, or have a shape!"
);
}
return data;
}
async _serialize() {
const data = await super._serialize();
await this.preRun();
const sectionData = await this._sanitizeEffectData();
return {
...data,
type: "effect",
sectionData
};
}
async _deserialize(data) {
this._deserializedData = data.sectionData;
return super._deserialize(data);
}
/**
* @private
*/
_getCalculatedScale(type) {
const min = this["_" + type + "Min"];
const max = this["_" + type + "Max"];
let scale2 = min;
if (is_real_number(min)) {
if (max && is_real_number(max)) {
scale2 = random_float_between(min, max);
}
scale2 = {
x: scale2,
y: scale2
};
}
return {
x: scale2?.x ?? 1,
y: scale2?.y ?? 1
};
}
}
class SoundSection extends Section {
constructor(inSequence, inFile = "") {
super(inSequence);
this._file = inFile;
this._volume = 0.8;
this._overrides = [];
}
static niceName = "Sound";
/**
* Adds a function that will run at the end of the sound serialization step, but before it is played. Allows direct
* modifications of sound's data.
*
* @param {function} inFunc
* @returns {SoundSection}
*/
addOverride(inFunc) {
if (!is_function$1(inFunc))
throw this.sequence._customError(
this,
"addOverride",
"The given function needs to be an actual function."
);
this._overrides.push(inFunc);
return this;
}
/**
* @private
*/
_applyTraits() {
Object.assign(this.constructor.prototype, traits.files);
Object.assign(this.constructor.prototype, traits.audio);
Object.assign(this.constructor.prototype, traits.time);
Object.assign(this.constructor.prototype, traits.users);
}
/**
* @OVERRIDE
* @returns {Promise}
*/
async run() {
const playData = await this._sanitizeSoundData();
if (!playData.play && this.sequence.softFail) {
return new Promise((reject2) => {
reject2();
});
}
if (!playData?.play) {
this.sequence._customError(
this,
"Play",
`File not found: ${playData.src}`
);
return new Promise((reject2) => reject2());
}
if (Hooks.call("preCreateSequencerSound", playData) === false)
return;
let push = !(playData?.users?.length === 1 && playData?.users?.includes(game.userId)) && !this.sequence.localOnly;
SequencerAudioHelper.play(playData, push);
await new Promise(
(resolve) => setTimeout(resolve, this._currentWaitTime + playData.duration)
);
}
/**
* @returns {Promise}
* @private
*/
async _sanitizeSoundData() {
if (this._deserializedData) {
return this._deserializedData;
}
if (!this._file) {
return {
play: false,
src: false
};
}
let { file, forcedIndex } = await this._determineFile(this._file);
if (!file) {
return {
play: false,
src: false
};
}
if (file instanceof SequencerFileBase) {
file.forcedIndex = forcedIndex;
if (file.timeRange) {
[this._startTime, this._endTime] = file.timeRange;
this._isRange = true;
}
file = file.getFile();
}
let soundFile = await AudioHelper.preloadSound(file);
if (!soundFile || soundFile.failed) {
return {
play: false,
src: this._file
};
}
let duration = soundFile.duration * 1e3;
let startTime = (this._startTime ? !this._startPerc ? this._startTime : this._startTime * duration : 0) / 1e3;
if (this._endTime) {
duration = !this._endPerc ? Number(
this._isRange ? this._endTime - this._startTime : duration - this._endTime
) : this._endTime * duration;
}
let data = {
id: randomID(),
play: true,
src: file,
loop: this._duration > duration,
volume: this._volume,
fadeIn: this._fadeInAudio,
fadeOut: this._fadeOutAudio,
startTime,
duration: this._duration || duration,
sceneId: game.user.viewedScene,
users: this._users ? Array.from(this._users) : null
};
for (let override of this._overrides) {
data = await override(this, data);
}
if (typeof data.src !== "string" || data.src === "") {
throw this.sequence._customError(
this,
"file",
"a sound must have a src of type string!"
);
}
return data;
}
async _serialize() {
const data = await super._serialize();
const sectionData = await this._sanitizeSoundData();
return {
...data,
type: "sound",
sectionData
};
}
async _deserialize(data) {
this._deserializedData = data.sectionData;
return super._deserialize(data);
}
}
class AnimationSection extends Section {
constructor(inSequence, inTarget) {
super(inSequence);
this._teleportTo = false;
this._originObject = false;
this._moveSpeed = 23;
this._offset = { x: 0, y: 0 };
this._closestSquare = false;
this._snapToGrid = false;
this._hide = void 0;
if (inTarget)
this.on(inTarget);
}
static niceName = "Animation";
/**
* Sets the target object to be animated
*
* @param {object|string} inTarget
* @returns {AnimationSection}
*/
on(inTarget) {
inTarget = this._validateLocation(inTarget);
if (!inTarget)
throw this.sequence._customError(
this,
"on",
"could not find position of given object"
);
this._originObject = this._validateLocation(inTarget);
return this;
}
/**
* Sets the location to teleport the target object to
*
* @param {object|string} inTarget
* @param {object} options
* @returns {AnimationSection}
*/
teleportTo(inTarget, options = {}) {
options = foundry.utils.mergeObject(
{
delay: 0,
relativeToCenter: false
},
options
);
if (!is_real_number(options.delay))
throw this.sequence._customError(
this,
"teleportTo",
"options.delay must be of type number"
);
inTarget = this._validateLocation(inTarget);
if (!inTarget)
throw this.sequence._customError(
this,
"teleportTo",
"could not find position of given object"
);
options.target = this._validateLocation(inTarget);
this._teleportTo = options;
return this;
}
/**
* Sets the location to rotate the object to
*
* @param {object|string} inLocation
* @param {object} options
* @returns this
*/
rotateTowards(inLocation, options = {}) {
options = foundry.utils.mergeObject(
{
duration: 0,
ease: "linear",
delay: 0,
rotationOffset: 0,
towardsCenter: true,
cacheLocation: false
},
options
);
if (!is_real_number(options.duration))
throw this.sequence._customError(
this,
"rotateTowards",
"options.duration must be of type number"
);
if (typeof options.ease !== "string")
throw this.sequence._customError(
this,
"rotateTowards",
"options.ease must be of type string"
);
if (!is_real_number(options.delay))
throw this.sequence._customError(
this,
"rotateTowards",
"options.delay must be of type number"
);
if (!is_real_number(options.rotationOffset))
throw this.sequence._customError(
this,
"rotateTowards",
"options.rotationOffset must be of type number"
);
if (typeof options.towardsCenter !== "boolean")
throw this.sequence._customError(
this,
"rotateTowards",
"options.towardsCenter must be of type boolean"
);
if (typeof options.cacheLocation !== "boolean")
throw this.sequence._customError(
this,
"rotateTowards",
"options.cacheLocation must be of type boolean"
);
options.target = this._validateLocation(inLocation);
if (!options.target)
throw this.sequence._customError(
this,
"rotateTowards",
"could not find position of given object"
);
options.target = options.cacheLocation ? get_object_position(options.target, { measure: true }) : options.target;
this._rotateTowards = options;
return this;
}
/**
* Causes the movement or teleportation to be offset in the X and/or Y axis
*
* @param {object} inOffset
* @returns {AnimationSection}
*/
offset(inOffset) {
inOffset = foundry.utils.mergeObject({ x: 0, y: 0 }, inOffset);
this._offset = this._validateLocation(inOffset);
return this;
}
/**
* Causes the movement or teleportation to pick the closest non-intersecting square, if the target is a token or tile
*
* @param {boolean} inBool
* @returns {AnimationSection}
*/
closestSquare(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"closestSquare",
"inBool must be of type boolean"
);
this._closestSquare = inBool;
return this;
}
/**
* Causes the final location to be snapped to the grid
*
* @param {boolean} inBool
* @returns {AnimationSection}
*/
snapToGrid(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"snapToGrid",
"inBool must be of type boolean"
);
this._snapToGrid = inBool;
return this;
}
/**
* Causes the object to become hidden
*
* @param {boolean} inBool
* @returns {AnimationSection}
*/
hide(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"hide",
"inBool must be of type boolean"
);
this._hide = inBool;
return this;
}
/**
* Causes the object to become visible
*
* @param {boolean} inBool
* @returns {AnimationSection}
*/
show(inBool = true) {
if (typeof inBool !== "boolean")
throw this.sequence._customError(
this,
"show",
"inBool must be of type boolean"
);
this._hide = !inBool;
return this;
}
/**
* @private
*/
async run() {
return this._runAnimate();
}
/**
* @private
*/
_applyTraits() {
Object.assign(this.constructor.prototype, traits.moves);
Object.assign(this.constructor.prototype, traits.opacity);
Object.assign(this.constructor.prototype, traits.rotation);
Object.assign(this.constructor.prototype, traits.audio);
Object.assign(this.constructor.prototype, traits.tint);
}
/**
* @private
*/
async _updateObject(obj, updates, animate = false, animation2 = {}) {
const uuid = obj?.uuid ?? obj?.document?.uuid;
await sequencerSocket.executeAsGM(
SOCKET_HANDLERS.UPDATE_DOCUMENT,
uuid,
updates,
{ animate, animation: animation2 }
);
}
/**
* @private
*/
async _waitForTokenRefresh(obj) {
let token;
if (obj instanceof Token) {
token = obj;
}
if (obj instanceof TokenDocument) {
token = obj.object;
}
if (token == null || token.mesh != null) {
return;
}
let refreshTokenID;
return await new Promise((resolve) => {
refreshTokenID = Hooks.on("refreshToken", async (tokenDoc) => {
if (tokenDoc.document.actorId !== token.document.actorId || !token.mesh)
return;
Hooks.off("refreshToken", refreshTokenID);
resolve();
});
});
}
/**
* @private
*/
async _execute() {
if (!await this._shouldPlay())
return;
let self = this;
this._basicDelay = random_float_between(this._delayMin, this._delayMax);
return new Promise(async (resolve) => {
setTimeout(async () => {
await this._waitForTokenRefresh(this._originObject);
if (this._shouldAsync) {
await self.run();
} else {
self.run();
}
resolve();
}, this._basicDelay);
});
}
/**
* @private
*/
_getClosestSquare(origin, target) {
let originLoc = get_object_position(origin, { exact: true });
let targetLoc = get_object_position(target, { exact: true });
let originDimensions = get_object_dimensions(origin);
let targetDimensions = get_object_dimensions(target);
let originBottom = Math.max(
originDimensions.width - canvas.grid.size,
canvas.grid.size
);
let originRight = Math.max(
originDimensions.height - canvas.grid.size,
canvas.grid.size
);
let ray = new Ray(originLoc, targetLoc);
let dx = ray.dx;
let dy = ray.dy;
if (dx > 0 && Math.abs(dx) > originRight) {
dx -= originDimensions.width;
} else if (dx < 0 && Math.abs(dx) > targetDimensions.width) {
dx += targetDimensions.height;
} else {
dx = 0;
}
if (dy > 0 && Math.abs(dy) > originBottom) {
dy -= originDimensions.height;
} else if (dy < 0 && Math.abs(dy) > targetDimensions.height) {
dy += targetDimensions.height;
} else {
dy = 0;
}
const pos = {
x: originLoc.x + dx,
y: originLoc.y + dy,
elevation: targetLoc?.elevation
};
if (!pos.elevation) {
delete pos["elevation"];
}
return pos;
}
/**
* @private
*/
_snapLocationToGrid(inLocation) {
return canvas.grid.getSnappedPosition(inLocation.x, inLocation.y);
}
/**
* This needs a rewrite, jeesus.
*/
async _runAnimate() {
let animData = {
attributes: [],
maxFPS: 1e3 / game.settings.get("core", "maxFPS"),
lastTimespan: performance.now(),
totalDt: 0
};
let overallDuration = this._duration ? this._duration : 0;
const originLocation = get_object_position(this._originObject, {
exact: true
});
if (this._rotateTowards) {
let offset2 = (this._angle ? this._angle : 0) + this._rotateTowards.rotationOffset;
let targetLoc = this._moveTowards?.target || this._teleportTo?.target || this._originObject;
targetLoc = this._closestSquare ? this._getClosestSquare(this._originObject, targetLoc) : get_object_position(targetLoc, { exact: true });
targetLoc.x += this._offset.x;
targetLoc.y += this._offset.y;
if (this._snapToGrid) {
targetLoc = this._snapLocationToGrid(targetLoc);
}
if (this._originObject instanceof TokenDocument) {
setTimeout(async () => {
let startLocation = this._originObject.object?.center ?? targetLoc;
let targetLocation = this._rotateTowards.target?.object ?? this._rotateTowards.target;
if (this._rotateTowards.towardsCenter) {
targetLocation = targetLocation?.center ?? targetLocation;
}
let ray = new Ray(startLocation, targetLocation);
let angle = Math.normalizeDegrees(ray.angle * 180 / Math.PI - 90);
angle += offset2;
await this._updateObject(
this._originObject,
{ rotation: angle },
this._rotateTowards.duration > 0,
{
duration: this._rotateTowards.duration,
easing: this._rotateTowards.ease
}
);
}, this._rotateTowards.delay ?? 0);
} else {
animData.attributes.push({
name: "rotationTowards",
offset: offset2,
origin: this._originObject,
originLocation: targetLoc,
target: this._rotateTowards.target?.object ?? this._rotateTowards.target,
towardsCenter: this._rotateTowards.towardsCenter,
from: false,
to: false,
progress: 0,
done: false,
duration: this._rotateTowards.duration,
durationDone: 0,
delay: this._rotateTowards.delay,
ease: this._rotateTowards.ease
});
}
let rotateDuration = this._rotateTowards.duration + this._rotateTowards.delay;
overallDuration = overallDuration > rotateDuration ? overallDuration : rotateDuration;
}
if (this._fadeIn) {
let to = is_real_number(this._opacity) ? this._opacity : 1;
if (this._originObject instanceof TokenDocument) {
setTimeout(async () => {
await this._updateObject(this._originObject, { alpha: to }, true, {
duration: this._fadeIn.duration,
easing: this._fadeIn.ease
});
}, this._fadeIn.delay ?? 0);
} else {
animData.attributes.push({
name: "alpha",
from: 0,
to,
progress: 0,
done: false,
duration: this._fadeIn.duration,
durationDone: 0,
delay: this._fadeIn.delay,
ease: this._fadeIn.ease
});
}
let fadeDuration = this._fadeIn.duration + this._fadeIn.delay;
overallDuration = overallDuration > fadeDuration ? overallDuration : fadeDuration;
}
if (this._fadeInAudio && this._originObject?.video?.volume !== void 0) {
let to = is_real_number(this._volume) ? this._volume : 1;
animData.attributes.push({
name: "video.volume",
from: 0,
to,
progress: 0,
done: false,
duration: this._fadeInAudio.duration,
durationDone: 0,
delay: this._fadeInAudio.delay,
ease: this._fadeInAudio.ease
});
let fadeDuration = this._fadeInAudio.duration + this._fadeInAudio.delay;
overallDuration = overallDuration > fadeDuration ? overallDuration : fadeDuration;
}
if (this._rotateIn) {
let from = this._angle ? this._angle : this._originObject.rotation;
let to = this._rotateIn.value;
if (Math.abs(from - to) > 180) {
if (to < 0) {
to += 360;
} else if (from > to) {
from -= 360;
}
}
if (this._originObject instanceof TokenDocument) {
setTimeout(async () => {
if (this._originObject.rotation !== from) {
await this._updateObject(
this._originObject,
{ rotation: from },
false
);
}
await this._updateObject(this._originObject, { rotation: to }, true, {
duration: this._rotateIn.duration,
easing: this._rotateIn.ease
});
}, this._rotateIn.delay ?? 0);
} else {
animData.attributes.push({
name: "rotation",
from,
to,
progress: 0,
done: false,
duration: this._rotateIn.duration,
durationDone: 0,
delay: this._rotateIn.delay,
ease: this._rotateIn.ease
});
}
let rotateDuration = this._rotateIn.duration + this._rotateIn.delay;
overallDuration = overallDuration > rotateDuration ? overallDuration : rotateDuration;
}
if (this._moveTowards) {
let originLocation2 = get_object_position(this._originObject, {
exact: true
});
let targetLocation = this._closestSquare ? this._getClosestSquare(this._originObject, this._moveTowards.target) : get_object_position(this._moveTowards.target, {
exact: true
});
targetLocation.x += this._offset.x;
targetLocation.y += this._offset.y;
targetLocation.elevation = targetLocation?.elevation ?? this._originObject?.elevation;
if (this._moveTowards.relativeToCenter) {
const dimensions = get_object_dimensions(this._originObject);
targetLocation.x -= dimensions.width / 2;
targetLocation.y -= dimensions.height / 2;
if (this._snapToGrid) {
targetLocation.x -= 0.01;
targetLocation.y -= 0.01;
}
}
if (this._snapToGrid) {
targetLocation = this._snapLocationToGrid(targetLocation);
}
let originalDx = targetLocation.x - originLocation2.x;
let originalDy = targetLocation.y - originLocation2.y;
let originalDistance = Math.sqrt(
originalDx * originalDx + originalDy * originalDy
);
let duration = this._duration ? this._duration : originalDistance / this._moveSpeed * animData.maxFPS;
let moveDuration = duration + this._moveTowards.delay;
overallDuration = overallDuration > moveDuration ? overallDuration : moveDuration;
if (this._originObject instanceof TokenDocument) {
setTimeout(async () => {
await this._updateObject(this._originObject, targetLocation, true, {
movementSpeed: this._moveSpeed,
duration,
easing: this._moveTowards.ease
});
}, this._moveTowards.delay ?? 0);
} else {
if (!(duration === 0 || originalDistance === 0)) {
animData.attributes.push({
name: "position",
origin: originLocation2,
target: targetLocation,
originalDistance,
currentDistance: 0,
progress: 0,
speed: 0,
duration,
done: false,
ease: this._moveTowards.ease,
delay: this._moveTowards.delay
});
}
}
}
if (this._fadeOut) {
let from = is_real_number(this._opacity) ? this._opacity : this._originObject.alpha ?? 1;
if (this._originObject instanceof TokenDocument) {
setTimeout(async () => {
await this._updateObject(this._originObject, { alpha: 0 }, true, {
duration: this._fadeOut.duration,
easing: this._fadeOut.ease
});
}, this._fadeOut.delay ?? 0);
} else {
animData.attributes.push({
name: "alpha",
from,
to: 0,
progress: 0,
done: false,
duration: this._fadeOut.duration,
durationDone: 0,
delay: overallDuration - this._fadeOut.duration,
ease: this._fadeOut.ease
});
}
let fadeOutDuration = this._fadeOut.duration + this._fadeOut.delay;
overallDuration = overallDuration > fadeOutDuration ? overallDuration : fadeOutDuration;
}
if (this._fadeOutAudio && this._originObject?.video?.volume !== void 0) {
let from = is_real_number(this._volume) ? this._volume : this._originObject.video.volume;
animData.attributes.push({
name: "video.volume",
from,
to: 0,
progress: 0,
done: false,
duration: this._fadeOutAudio.duration,
durationDone: 0,
delay: overallDuration - this._fadeOutAudio.duration,
ease: this._fadeOutAudio.ease
});
let fadeOutAudioDuration = this._fadeOutAudio.duration + this._fadeOutAudio.delay;
overallDuration = overallDuration > fadeOutAudioDuration ? overallDuration : fadeOutAudioDuration;
}
if (this._rotateOut) {
let from = this._rotateOut.value;
let to = this._angle ? this._angle : this._originObject.rotation;
if (this._rotateIn)
from += this._rotateIn.value;
if (Math.abs(from - to) > 180) {
if (to < 0) {
to += 360;
} else if (from > to) {
from -= 360;
}
}
if (this._originObject instanceof TokenDocument) {
setTimeout(async () => {
if (this._originObject.rotation !== from) {
await this._updateObject(
this._originObject,
{ rotation: from },
false
);
}
await this._updateObject(this._originObject, { rotation: to }, true, {
duration: this._rotateOut.duration,
easing: this._rotateOut.ease
});
}, this._rotateOut.delay ?? 0);
} else {
animData.attributes.push({
name: "rotation",
from,
to,
progress: 0,
done: false,
duration: this._rotateOut.duration,
durationDone: 0,
delay: overallDuration - this._rotateOut.duration,
ease: this._rotateOut.ease
});
}
let rotateOutDuration = this._rotateOut.duration + this._rotateOut.delay;
overallDuration = overallDuration > rotateOutDuration ? overallDuration : rotateOutDuration;
}
if (this._teleportTo) {
setTimeout(async () => {
let targetLocation = this._closestSquare ? this._getClosestSquare(this._originObject, this._teleportTo.target) : get_object_position(this._teleportTo.target, {
exact: true
});
if (targetLocation.x === void 0)
targetLocation.x = originLocation.x;
if (targetLocation.y === void 0)
targetLocation.y = originLocation.y;
if (targetLocation.elevation === void 0)
targetLocation.elevation = originLocation.elevation;
targetLocation.x += this._offset.x;
targetLocation.y += this._offset.y;
targetLocation.elevation = targetLocation?.elevation ?? this._originObject?.elevation;
const dimensions = get_object_dimensions(this._originObject);
if (this._teleportTo.relativeToCenter) {
targetLocation.x -= dimensions.width / 2;
targetLocation.y -= dimensions.height / 2;
if (this._snapToGrid) {
targetLocation.x -= 0.01;
targetLocation.y -= 0.01;
}
}
if (this._snapToGrid) {
targetLocation.x -= dimensions.width / 2;
targetLocation.y -= dimensions.height / 2;
targetLocation = this._snapLocationToGrid(targetLocation);
}
await this._updateObject(this._originObject, targetLocation, false);
}, this._teleportTo.delay);
if (overallDuration <= this._teleportTo.delay) {
this._waitUntilFinished = true;
}
overallDuration = overallDuration > this._teleportTo.delay ? overallDuration : this._teleportTo.delay;
}
let updateAttributes = {};
if (is_real_number(this._angle) && !this._rotateIn && !this._rotateOut) {
updateAttributes["rotation"] = this._angle;
}
if (is_real_number(this._opacity) && !this._fadeIn && !this._fadeOut) {
updateAttributes["alpha"] = this._opacity;
}
if (is_real_number(this._volume) && !this._fadeInAudio && !this._fadeOutAudio && this._originObject?.video?.volume !== void 0) {
updateAttributes["video.volume"] = this._volume;
}
if (this._tint) {
updateAttributes["tint"] = this._tint.hexadecimal;
}
if (this._hide !== void 0) {
updateAttributes["hidden"] = this._hide;
}
if (Object.keys(updateAttributes).length) {
await wait$1(1);
await this._updateObject(this._originObject, updateAttributes);
await wait$1(1);
}
return new Promise(async (resolve) => {
this._animate(animData, resolve);
setTimeout(
resolve,
Math.max(0, overallDuration + this._currentWaitTime + animData.maxFPS)
);
});
}
/**
* @private
*/
async _animate(animData, resolve, timespan) {
if (timespan) {
let animatedAttributes = {};
let dt = timespan - animData.lastTimespan;
if (dt >= animData.maxFPS) {
animData.totalDt += dt;
for (let attribute of animData.attributes) {
if (attribute.done)
continue;
if (animData.totalDt < attribute.delay)
continue;
if (attribute.name === "position") {
attribute.speed = attribute.originalDistance / (attribute.duration / dt);
attribute.currentDistance += attribute.speed;
attribute.progress = attribute.currentDistance / attribute.originalDistance;
let x = interpolate(
attribute.origin.x,
attribute.target.x,
attribute.progress,
attribute.ease
);
let y = interpolate(
attribute.origin.y,
attribute.target.y,
attribute.progress,
attribute.ease
);
if (attribute.currentDistance >= attribute.originalDistance) {
x = attribute.target.x;
y = attribute.target.y;
attribute.done = true;
}
animatedAttributes["x"] = x;
animatedAttributes["y"] = y;
} else {
if (attribute.name === "rotationTowards" && !attribute.from && !attribute.to) {
let target = attribute.target;
if (this._rotateTowards.towardsCenter)
target = target?.center ?? target;
let ray = new Ray(attribute.originLocation, target);
let angle = ray.angle * 180 / Math.PI - 90;
angle += attribute.offset;
attribute.from = attribute.origin.rotation;
attribute.to = angle;
if (Math.abs(attribute.from - attribute.to) > 180) {
if (attribute.to < 0) {
attribute.to += 360;
} else if (attribute.from > attribute.to) {
attribute.from -= 360;
}
}
attribute.name = "rotation";
}
attribute.durationDone += dt;
attribute.progress = attribute.durationDone / attribute.duration;
let val = interpolate(
attribute.from,
attribute.to,
attribute.progress,
attribute.ease
);
if (attribute.progress >= 1) {
val = attribute.to;
attribute.done = true;
}
animatedAttributes[attribute.name] = val;
}
}
if (Object.keys(animatedAttributes).length > 0) {
await this._updateObject(this._originObject, animatedAttributes);
}
animData.attributes = animData.attributes.filter((a) => !a.done);
if (animData.attributes.length === 0)
return;
animData.lastTimespan = timespan;
}
}
let self = this;
requestAnimationFrame(function(timespan2) {
self._animate(animData, resolve, timespan2);
});
}
}
class ScrollingTextSection extends Section {
constructor(inSequence, target, text2, textOptions) {
super(inSequence);
this._deserializedData = null;
this._source = null;
this._text = "";
this._duration = 2e3;
this._distance = null;
this._jitter = 0;
this._anchor = null;
this._direction = null;
this._seed = get_hash(randomID());
if (target) {
this.atLocation(target);
}
if (text2) {
this.text(text2, textOptions);
}
}
static niceName = "Scrolling Text";
/**
* @private
*/
_applyTraits() {
Object.assign(this.constructor.prototype, traits.users);
Object.assign(this.constructor.prototype, traits.location);
Object.assign(this.constructor.prototype, traits.offset);
Object.assign(this.constructor.prototype, traits.text);
}
direction(inDirection) {
if (!(typeof inDirection === "string" || is_real_number(inDirection))) {
throw this.sequence._customError(
this,
"direction",
"inDirection must be of type string (CONST.TEXT_ANCHOR_POINTS) or number"
);
}
if (typeof inDirection === "string" && !CONST.TEXT_ANCHOR_POINTS[inDirection]) {
throw this.sequence._customError(
this,
"direction",
`${inDirection} does not exist in CONST.TEXT_ANCHOR_POINTS!`
);
} else if (typeof inDirection === "string") {
this._direction = CONST.TEXT_ANCHOR_POINTS[inDirection];
} else {
this._direction = inDirection;
}
return this;
}
anchor(inAnchor) {
if (!(typeof inAnchor === "string" || is_real_number(inAnchor))) {
throw this.sequence._customError(
this,
"direction",
"inAnchor must be of type string (CONST.TEXT_ANCHOR_POINTS) or number"
);
}
if (typeof inAnchor === "string" && !CONST.TEXT_ANCHOR_POINTS[inAnchor] || is_real_number(inAnchor) && !Object.values(CONST.TEXT_ANCHOR_POINTS).includes(inAnchor)) {
throw this.sequence._customError(
this,
"direction",
`${inAnchor} does not exist in CONST.TEXT_ANCHOR_POINTS!`
);
} else if (typeof inAnchor === "string") {
this._anchor = CONST.TEXT_ANCHOR_POINTS[inAnchor];
} else {
this._anchor = inAnchor;
}
return this;
}
jitter(inJitter) {
if (!(is_real_number(inJitter) && inJitter >= 0 && inJitter <= 1)) {
throw this.sequence._customError(
this,
"jitter",
"inJitter must be of type number between 0 and 1"
);
}
this._jitter = inJitter;
return this;
}
async run() {
const data = await this._sanitizeTextData();
if (Hooks.call("preCreateScrollingText", data) === false)
return;
const push = !(data?.users?.length === 1 && data?.users?.includes(game.userId)) && !this.sequence.localOnly;
const duration = SequencerFoundryReplicator.playScrollingText(data, push);
await new Promise(
(resolve) => setTimeout(resolve, this._currentWaitTime + duration)
);
}
_getSourceObject() {
if (!this._source || typeof this._source !== "object")
return this._source;
return get_object_identifier(this._source) ?? get_object_canvas_data(this._source);
}
async _sanitizeTextData() {
if (this._deserializedData) {
return this._deserializedData;
}
return {
sceneId: game.user.viewedScene,
seed: this._seed,
sequenceId: this.sequence.id,
creatorUserId: game.userId,
users: this._users ? Array.from(this._users) : false,
moduleName: this.sequence.moduleName,
source: this._getSourceObject(),
offset: this._offset?.source ?? false,
randomOffset: this._randomOffset?.source ?? false,
content: this._text?.text ?? "",
options: {
anchor: this._anchor,
direction: this._direction,
duration: this._duration,
distance: this._distance,
jitter: this._jitter,
...this._text
}
};
}
async _serialize() {
const data = await super._serialize();
const sectionData = await this._sanitizeTextData();
return {
...data,
type: "scrollingText",
sectionData
};
}
async _deserialize(data) {
this._deserializedData = data.sectionData;
return super._deserialize(data);
}
}
class CanvasPanSection extends Section {
constructor(inSequence, target, duration, scale2) {
super(inSequence);
this._deserializedData = null;
this._source = null;
this._duration = duration ?? 250;
this._speed = null;
this._scale = scale2 ?? 1;
this._lockView = null;
this._shake = null;
this._seed = get_hash(randomID());
if (target) {
this.atLocation(target);
}
}
static niceName = "Canvas Pan";
/**
* @private
*/
_applyTraits() {
Object.assign(this.constructor.prototype, traits.users);
Object.assign(this.constructor.prototype, traits.location);
Object.assign(this.constructor.prototype, traits.offset);
}
/**
* Sets the speed (pixels per frame) of the canvas pan
*
* @param {number} inSpeed
* @returns this
*/
speed(inSpeed) {
if (!is_real_number(inSpeed))
throw this.sequence._customError(
this,
"speed",
"inSpeed must be of type number"
);
if (inSpeed < 0) {
throw this.sequence._customError(
this,
"speed",
"inSpeed must be greater or equal to 0"
);
}
this._speed = inSpeed;
return this;
}
/**
* Sets the zoom level of the canvas pan
*
* @param {number} inScale
* @returns this
*/
scale(inScale) {
if (!is_real_number(inScale))
throw this.sequence._customError(
this,
"scale",
"inScale must be of type number"
);
if (inScale <= 0) {
throw this.sequence._customError(
this,
"scale",
"inScale must be greater than 0"
);
}
this._scale = inScale;
return this;
}
/**
* Locks the canvas at the given location for the given duration
*
* @param {number} inDuration
* @returns this
*/
lockView(inDuration) {
if (!is_real_number(inDuration))
throw this.sequence._customError(
this,
"lockView",
"inDuration must be of type number"
);
if (inDuration < 0) {
throw this.sequence._customError(
this,
"lockView",
"inDuration must be greater or equal to 0"
);
}
this._lockView = inDuration;
return this;
}
/**
* Shakes the canvas
*
* @param {number} duration
* @param {number} strength
* @param {number} frequency
* @param {number} fadeInDuration
* @param {number} fadeOutDuration
* @param {boolean} rotation
* @returns this
*/
shake({
duration = 250,
strength = 20,
frequency = 10,
fadeInDuration = 0,
fadeOutDuration = 200,
rotation: rotation2 = true
} = {}) {
if (!is_real_number(duration)) {
throw this.sequence._customError(
this,
"shake",
"duration must be of type number"
);
}
if (!is_real_number(strength)) {
throw this.sequence._customError(
this,
"shake",
"strength must be of type number"
);
}
if (!is_real_number(strength)) {
throw this.sequence._customError(
this,
"shake",
"frequency must be of type number"
);
}
if (!is_real_number(fadeInDuration)) {
throw this.sequence._customError(
this,
"shake",
"fadeInDuration must be of type number"
);
}
if (!is_real_number(fadeOutDuration)) {
throw this.sequence._customError(
this,
"shake",
"fadeOutDuration must be of type number"
);
}
if (typeof rotation2 !== "boolean") {
throw this.sequence._customError(
this,
"shake",
"rotation must be of type boolean"
);
}
this._shake = {
duration,
strength,
frequency,
fadeInDuration,
fadeOutDuration,
rotation: rotation2
};
return this;
}
async run() {
const data = await this._sanitizeData();
if (Hooks.call("preCanvasPan", data) === false)
return;
const push = !(data?.users?.length === 1 && data?.users?.includes(game.userId)) && !this.sequence.localOnly;
const duration = SequencerFoundryReplicator.panCanvas(data, push);
await new Promise(
(resolve) => setTimeout(resolve, this._currentWaitTime + duration)
);
}
_getSourceObject() {
if (!this._source || typeof this._source !== "object")
return this._source;
return get_object_identifier(this._source) ?? get_object_canvas_data(this._source);
}
async _sanitizeData() {
if (this._deserializedData) {
return this._deserializedData;
}
return {
sceneId: game.user.viewedScene,
seed: this._seed,
sequenceId: this.sequence.id,
creatorUserId: game.userId,
users: this._users ? Array.from(this._users) : false,
moduleName: this.sequence.moduleName,
source: this._getSourceObject(),
offset: this._offset?.source ?? false,
randomOffset: this._randomOffset?.source ?? false,
duration: this._duration,
speed: this._speed,
scale: this._scale,
lockView: this._lockView,
shake: this._shake
};
}
async _serialize() {
const data = await super._serialize();
const sectionData = await this._sanitizeData();
return {
...data,
type: "canvasPan",
sectionData
};
}
async _deserialize(data) {
this._deserializedData = data.sectionData;
return super._deserialize(data);
}
}
class WaitSection extends Section {
constructor(inSequence, msMin, msMax) {
super(inSequence);
this._waitUntilFinished = true;
this._waitDuration = random_int_between(msMin, Math.max(msMin, msMax));
}
static niceName = "Wait";
/**
* @returns {Promise}
*/
async run() {
debug("Running wait");
await new Promise(async (resolve) => {
setTimeout(resolve, this._waitDuration);
});
}
/**
* @returns {Promise}
* @private
*/
async _execute() {
await this.run();
}
async _serialize() {
const data = await super._serialize();
return {
...data,
type: "wait",
sectionData: {
waitDuration: this._waitDuration
}
};
}
async _deserialize(data) {
this._waitDuration = data.sectionData.waitDuration;
return super._deserialize(data);
}
}
let Sequence$1 = class Sequence3 {
constructor(options = {
moduleName: "Sequencer",
softFail: false
}, softFail = false) {
this.id = randomID();
this.moduleName = typeof options === "string" ? options : options?.moduleName ?? "Sequencer";
this.softFail = options?.softFail ?? softFail;
this.sections = [];
this.nameOffsetMap = false;
this.effectIndex = 0;
this.sectionToCreate = void 0;
this.localOnly = false;
this._status = writable$1(CONSTANTS.STATUS.READY);
return sequence_proxy_wrap(this);
}
/**
* Plays all of this sequence's sections
*
* @returns {Promise}
*/
async play({ remote = false } = {}) {
if (remote) {
this.localOnly = true;
const data = await this.toJSON();
sequencerSocket.executeForOthers(
SOCKET_HANDLERS.RUN_SEQUENCE_LOCALLY,
data
);
return new Sequence3().fromJSON(data).play();
}
Hooks.callAll("createSequencerSequence", this);
debug("Initializing sections");
for (let section of this.sections) {
await section._initialize();
}
SequenceManager.RunningSequences.add(this.id, this);
this.effectIndex = 0;
debug("Playing sections");
this.status = CONSTANTS.STATUS.RUNNING;
const promises = [];
for (let section of this.sections) {
if (section instanceof EffectSection)
this.effectIndex++;
if (section.shouldWaitUntilFinished) {
promises.push(await section._execute());
} else {
promises.push(section._execute());
}
if (get_store_value(this.status) === CONSTANTS.STATUS.ABORTED) {
continue;
}
if (!section._isLastSection) {
await new Promise((resolve) => setTimeout(resolve, 1));
}
}
return Promise.allSettled(promises).then(() => {
Hooks.callAll("endedSequencerSequence");
debug("Finished playing sections");
this.status = CONSTANTS.STATUS.COMPLETE;
});
}
/**
* Creates a section that will run a function.
*
* @param {function} inFunc
* @returns {Sequence} this
*/
thenDo(inFunc) {
const func2 = section_proxy_wrap(new FunctionSection(this, inFunc));
this.sections.push(func2);
return func2;
}
/**
* Creates a section that will run a macro based on a name or a direct reference to a macro.
*
* @param {string|Macro} inMacro
* @param {object} args
* @returns {Sequence} this
*/
macro(inMacro, ...args) {
let macro;
let compendium = false;
if (typeof inMacro === "string") {
if (inMacro.startsWith("Compendium")) {
let packArray = inMacro.split(".");
let pack = game.packs.get(`${packArray[1]}.${packArray[2]}`);
if (!pack) {
if (this.softFail) {
return this;
}
throw custom_error(
this.moduleName,
`macro - Compendium '${packArray[1]}.${packArray[2]}' was not found`
);
}
macro = packArray;
compendium = pack;
} else {
macro = game.macros.getName(inMacro);
if (!macro) {
if (this.softFail) {
return this;
}
throw custom_error(
this.moduleName,
`macro - Macro '${inMacro}' was not found`
);
}
}
} else if (inMacro instanceof Macro) {
macro = inMacro;
} else {
throw custom_error(
this.moduleName,
`macro - inMacro must be of instance string or Macro`
);
}
if (isNewerVersion(game.version, "11")) {
args = args.length ? args?.[0] : {};
if (typeof args !== "object") {
throw custom_error(
this.moduleName,
`macro - Secondary argument must be an object`
);
}
} else if (args && args.length && !game.modules.get("advanced-macros")?.active) {
custom_warning(
this.moduleName,
`macro - Supplying macros with arguments require the advanced-macros module to be active`,
true
);
}
const func2 = section_proxy_wrap(
new FunctionSection(
this,
async () => {
if (compendium) {
const macroData = (await compendium.getDocuments()).find((i) => i.name === macro[3])?.toObject();
if (!macroData) {
if (this.softFail) {
return;
}
throw custom_error(
this.moduleName,
`macro - Macro '${macro[3]}' was not found in compendium '${macro[1]}.${macro[2]}'`
);
}
macro = new Macro(macroData);
macro.ownership.default = CONST.DOCUMENT_PERMISSION_LEVELS.OWNER;
}
if (isNewerVersion(game.version, "11")) {
await macro.execute(args);
} else {
const version = game.modules.get("advanced-macros")?.version;
const bugAdvancedMacros = game.modules.get("advanced-macros")?.active && isNewerVersion(
version.startsWith("v") ? version.slice(1) : version,
"1.18.2"
) && !isNewerVersion(
version.startsWith("v") ? version.slice(1) : version,
"1.19.1"
);
if (bugAdvancedMacros) {
await macro.execute([...args]);
} else {
await macro.execute(...args);
}
}
},
true
)
);
this.sections.push(func2);
return this;
}
/**
* Creates an effect section. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Effect section.
*
* @param {string} [inFile] inFile
* @returns {Section}
*/
effect(inFile = "") {
const effect = section_proxy_wrap(new EffectSection(this, inFile));
this.sections.push(effect);
return effect;
}
/**
* Creates a sound section. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Sound section.
*
* @param {string} [inFile] inFile
* @returns {Section}
*/
sound(inFile = "") {
const sound = section_proxy_wrap(new SoundSection(this, inFile));
this.sections.push(sound);
return sound;
}
/**
* Creates an animation. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Animation section.
*
* @param {Token|Tile} [inTarget=false] inTarget
* @returns {AnimationSection}
*/
animation(inTarget) {
const animation2 = section_proxy_wrap(
new AnimationSection(this, inTarget)
);
this.sections.push(animation2);
return animation2;
}
/**
* Creates a scrolling text. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Scrolling Text section.
*
* @param {Object|String|Boolean} [inTarget=false] inTarget
* @param {String|Boolean} [inText=false] inText
* @param {Object} [inTextOptions={}] inTextOptions
* @returns {AnimationSection}
*/
scrollingText(inTarget = false, inText = false, inTextOptions = {}) {
const scrolling = section_proxy_wrap(
new ScrollingTextSection(this, inTarget, inText, inTextOptions)
);
this.sections.push(scrolling);
return scrolling;
}
/**
* Pans the canvas text. Until you call any other sections you'll be working on the Canvas Pan section.
*
* @param {Object|String|Boolean} [inTarget=false] inTarget
* @param {Boolean|Number} inDuration
* @param {Boolean|Number} inSpeed
* @returns {AnimationSection}
*/
canvasPan(inTarget = false, inDuration = null, inSpeed = null) {
const panning = section_proxy_wrap(
new CanvasPanSection(this, inTarget)
);
this.sections.push(panning);
return panning;
}
/**
* Causes the sequence to wait after the last section for as many milliseconds as you pass to this method. If given
* a second number, a random wait time between the two given numbers will be generated.
*
* @param {number} [msMin=1] minMs
* @param {number} [msMax=1] maxMs
* @returns {Sequence} this
*/
wait(msMin = 1, msMax = 1) {
if (msMin < 1)
throw custom_error(
this.moduleName,
`wait - Wait ms cannot be less than 1`
);
if (msMax < 1)
throw custom_error(
this.moduleName,
`wait - Max wait ms cannot be less than 1`
);
const section = section_proxy_wrap(new WaitSection(this, msMin, msMax));
this.sections.push(section);
return this;
}
/**
* Applies a preset to the sequence
*
* @param {string} presetName
* @param {*} args
* @returns {Sequence|FunctionSection|EffectSection|AnimationSection|SoundSection}
*/
preset(presetName, ...args) {
if (typeof presetName !== "string") {
throw this._customError(this, "name", `inName must be of type string`);
}
const preset = SequencerPresets.get(presetName);
if (!preset) {
custom_warning(
"Sequencer",
`preset | Could not find preset with name "${presetName}"`
);
return this;
}
const lastSection = this.sections[this.sections.length - 1];
return preset(lastSection, ...args);
}
/**
* Adds the sections from a given Sequence to this Sequence
*
* @param {Sequence|FunctionSection|EffectSection|AnimationSection|SoundSection} inSequence
* @returns {Sequence} this
*/
addSequence(inSequence) {
if (inSequence instanceof Section)
inSequence = inSequence.sequence;
if (!(inSequence instanceof Sequence3)) {
throw custom_error(
this.moduleName,
`addSequence - could not find the sequence from the given parameter`
);
}
const newSections = inSequence.sections.map((section) => {
const newSection = Object.assign(
Object.create(Object.getPrototypeOf(section)),
section
);
newSection.sequence = this;
return newSection;
});
this.sections = this.sections.concat(newSections);
return this;
}
async toJSON() {
const data = {
options: { moduleName: this.moduleName, softFail: this.softFail },
sections: []
};
for (const section of this.sections) {
const sectionData = await section._serialize();
if (!sectionData.type) {
throw new Error(
`Sequencer | toJson | ${section.constructor.name} does not support serialization!`
);
}
data.sections.push(sectionData);
}
return data;
}
fromJSON(data) {
this.moduleName = data.options.moduleName;
this.softFail = data.options.softFail;
this.localOnly = true;
for (const section of data.sections) {
this[section.type]()._deserialize(section);
}
return this;
}
_createCustomSection(...args) {
const func2 = section_proxy_wrap(
new this.sectionToCreate(this, ...args)
);
this.sectionToCreate = void 0;
this.sections.push(func2);
return func2;
}
_showWarning(self, func2, warning, notify) {
custom_warning(
this.moduleName,
`${self.constructor.name.replace("Section", "")} | ${func2} - ${warning}`,
notify
);
}
_customError(self, func2, error) {
return custom_error(
this.moduleName,
`${self.constructor.name.replace("Section", "")} | ${func2} - ${error}`
);
}
set status(inStatus) {
this._status.update((currentStatus) => {
if (currentStatus === CONSTANTS.STATUS.READY || currentStatus === CONSTANTS.STATUS.RUNNING) {
return inStatus;
}
return currentStatus;
});
}
get status() {
return this._status;
}
_abort() {
this.status = CONSTANTS.STATUS.ABORTED;
for (const section of this.sections) {
section._abortSection();
}
}
};
const SequencerPreloader = {
_usersToRespond: /* @__PURE__ */ new Set(),
_clientsDone: [],
_resolve: () => {
},
/**
* Caches provided file(s) locally, vastly improving loading speed of those files.
*
* @param {Array|String} inSrcs
* @param {Boolean} showProgressBar
* @returns {Promise}
*/
preload(inSrcs, showProgressBar = false) {
if (Array.isArray(inSrcs)) {
inSrcs.forEach((str) => {
if (typeof str !== "string") {
throw custom_error(
"Sequencer",
"preload | each entry in inSrcs must be of type string"
);
}
});
} else if (typeof inSrcs !== "string") {
throw custom_error(
"Sequencer",
"preload | inSrcs must be of type string or array of strings"
);
}
if (typeof showProgressBar !== "boolean") {
throw custom_error(
"Sequencer",
"preload | showProgressBar must be of type of boolean"
);
}
const srcs = this._cleanSrcs(inSrcs);
if (srcs.length === 0)
return;
return this._preloadLocal(srcs, showProgressBar);
},
/**
* Causes each connected client (including the caller) to fetch and cache the provided file(s) locally, vastly
* improving loading speed of those files.
*
* @param {Array|String} inSrcs
* @param {Boolean} showProgressBar
* @returns {Promise}
*/
preloadForClients(inSrcs, showProgressBar = false) {
if (Array.isArray(inSrcs)) {
inSrcs.forEach((str) => {
if (typeof str !== "string") {
throw custom_error(
"Sequencer",
"preloadForClients | each entry in inSrcs must be of type string"
);
}
});
} else if (typeof inSrcs !== "string") {
throw custom_error(
"Sequencer",
"preloadForClients | inSrcs must be of type string or array of strings"
);
}
if (typeof showProgressBar !== "boolean") {
throw custom_error(
"Sequencer",
"preloadForClients | showProgressBar must be of type of boolean"
);
}
const srcs = this._cleanSrcs(inSrcs);
if (srcs.length === 0)
return;
if (!user_can_do("permissions-preload")) {
custom_warning(
"Sequencer",
"preloadForClients - You do not have permission to force other clients to preload. Preloading locally instead."
);
return this._preloadLocal(srcs, showProgressBar);
}
const promise2 = new Promise((resolve) => {
this._resolve = resolve;
});
this._usersToRespond = new Set(
game.users.filter((user) => user.active).map((user) => user.id)
);
sequencerSocket.executeForEveryone(
SOCKET_HANDLERS.PRELOAD,
game.user.id,
srcs,
showProgressBar
);
return promise2;
},
async respond(inSenderId, inSrcs, showProgressBar) {
const numFilesFailedToLoad = await this._preloadLocal(
inSrcs,
showProgressBar
);
return sequencerSocket.executeAsUser(
SOCKET_HANDLERS.PRELOAD_RESPONSE,
inSenderId,
game.user.id,
numFilesFailedToLoad
);
},
handleResponse(inUserId, numFilesFailedToLoad) {
this._usersToRespond.delete(inUserId);
this._clientsDone.push({
userId: inUserId,
numFilesFailedToLoad
});
if (this._usersToRespond.size > 0)
return;
this._clientsDone.forEach((user) => {
if (user.numFilesFailedToLoad > 0) {
debug(
`${game.users.get(user.userId).name} preloaded files, failed to preload ${user.numFilesFailedToLoad} files`
);
} else {
debug(
`${game.users.get(user.userId).name} preloaded files successfully`
);
}
});
debug(`All clients responded to file preloads`);
this._resolve();
this._usersToRespond = /* @__PURE__ */ new Set();
this._clientsDone = [];
this._resolve = () => {
};
},
/**
* Filters and cleans up file paths given to the preload methods
*
* @private
*/
_cleanSrcs(inSrcs) {
if (!Array.isArray(inSrcs)) {
inSrcs = [inSrcs];
}
if (inSrcs.length === 0) {
custom_warning("Sequencer", "You need to provide files to preload");
return [];
}
inSrcs = make_array_unique(
inSrcs.filter(Boolean).map((src) => {
if (Sequencer.Database.entryExists(src)) {
return Sequencer.Database.getAllFileEntries(src);
}
return src;
})
).deepFlatten();
if (inSrcs.length >= 750) {
custom_warning(
"Sequencer",
"You are preloading over 750 files, you are most likely preloading more files than the system can cache.",
true
);
}
return inSrcs;
},
/**
* The method that actually preloads files locally, with an optional progress bar
*
* @private
*/
_preloadLocal(inSrcs, showProgressBar) {
let startTime = performance.now();
let numFilesToLoad = inSrcs.length;
debug(`Preloading ${numFilesToLoad} files...`);
if (showProgressBar)
LoadingBar.init(
`Sequencer - Preloading ${numFilesToLoad} files`,
numFilesToLoad
);
return new Promise(async (resolve) => {
let numFilesFailedToLoad = 0;
for (let src of inSrcs) {
const blob = await SequencerFileCache.loadFile(src, true);
if (showProgressBar)
LoadingBar.incrementProgress();
if (!blob) {
numFilesFailedToLoad++;
}
}
const timeTaken = (performance.now() - startTime) / 1e3;
let failedToLoad = ` (${numFilesFailedToLoad} failed to load)`;
debug(
`Preloading ${numFilesToLoad} files took ${timeTaken}s` + failedToLoad
);
resolve(numFilesFailedToLoad);
});
}
};
class SequencerSectionManager {
constructor() {
this.externalSections = {};
}
/**
* Registers a class by a name that will then be available through the Sequencer
*
* @param {String} inModuleName
* @param {String} inMethodName
* @param {Class} inClass
* @param {Boolean} overwrite
* @returns {Boolean} Whether the registration succeeded
*/
registerSection(inModuleName, inMethodName, inClass, overwrite = false) {
if (!(inClass.prototype instanceof Section)) {
throw custom_error(
inModuleName,
`inClass must be instance of Sequencer.BaseSection`
);
}
let coreMethods = Object.getOwnPropertyNames(Sequence.prototype).filter(
(method) => {
return !method.startsWith("_") && method !== "constructor";
}
);
if (coreMethods.includes(inMethodName)) {
throw custom_error(
inModuleName,
`${inMethodName} is an existing protected method of the Sequence class - please register with another method name!`
);
}
if (this.externalSections[inMethodName] && !overwrite) {
throw custom_error(
inModuleName,
`${inMethodName} is already a registered Section with the class ${this.externalSections[inMethodName].constructor.name}`
);
}
coreMethods = coreMethods.concat(Object.keys(this.externalSections));
const clashingMethods = Object.getOwnPropertyNames(
inClass.prototype
).filter((method) => coreMethods.includes(method));
if (clashingMethods.length) {
let errors = clashingMethods.join(", ");
throw custom_error(
inModuleName,
`${inMethodName} cannot contain the following methods: ${errors}
These methods are existing methods on the Sequence or from already registered Sections. Please rename these methods to avoid conflicts.`
);
}
debug(
`SectionManager | Successfully registered ${inMethodName} with Sequencer!`
);
this.externalSections[inMethodName] = inClass;
return true;
}
}
let libWrapper = void 0;
const TGT_SPLIT_RE = new RegExp(
`([^.[]+|\\[('([^'\\\\]|\\\\.)+?'|"([^"\\\\]|\\\\.)+?")\\])`,
"g"
);
const TGT_CLEANUP_RE = new RegExp(`(^\\['|'\\]$|^\\["|"\\]$)`, "g");
Hooks.once("init", () => {
if (globalThis.libWrapper && !(globalThis.libWrapper.is_fallback ?? true)) {
libWrapper = globalThis.libWrapper;
return;
}
libWrapper = class {
static get is_fallback() {
return true;
}
static get WRAPPER() {
return "WRAPPER";
}
static get MIXED() {
return "MIXED";
}
static get OVERRIDE() {
return "OVERRIDE";
}
static register(package_id, target, fn, type = "MIXED", { chain = void 0, bind: bind2 = [] } = {}) {
const is_setter = target.endsWith("#set");
target = !is_setter ? target : target.slice(0, -4);
const split = target.match(TGT_SPLIT_RE).map((x) => x.replace(/\\(.)/g, "$1").replace(TGT_CLEANUP_RE, ""));
const root_nm = split.splice(0, 1)[0];
let obj, fn_name;
if (split.length == 0) {
obj = globalThis;
fn_name = root_nm;
} else {
const _eval = eval;
fn_name = split.pop();
obj = split.reduce(
(x, y) => x[y],
globalThis[root_nm] ?? _eval(root_nm)
);
}
let iObj = obj;
let descriptor = null;
while (iObj) {
descriptor = Object.getOwnPropertyDescriptor(iObj, fn_name);
if (descriptor)
break;
iObj = Object.getPrototypeOf(iObj);
}
if (!descriptor || descriptor?.configurable === false)
throw new Error(
`libWrapper Shim: '${target}' does not exist, could not be found, or has a non-configurable descriptor.`
);
let original = null;
const wrapper = chain ?? (type.toUpperCase?.() != "OVERRIDE" && type != 3) ? function(...args) {
return fn.call(this, original.bind(this), ...bind2, ...args);
} : function(...args) {
return fn.call(this, ...bind2, ...args);
};
if (!is_setter) {
if (descriptor.value) {
original = descriptor.value;
descriptor.value = wrapper;
} else {
original = descriptor.get;
descriptor.get = wrapper;
}
} else {
if (!descriptor.set)
throw new Error(
`libWrapper Shim: '${target}' does not have a setter`
);
original = descriptor.set;
descriptor.set = wrapper;
}
descriptor.configurable = true;
Object.defineProperty(obj, fn_name, descriptor);
}
};
});
function registerLibwrappers() {
const override = isNewerVersion(game.version, "11") ? "PIXI.BaseImageResource.prototype.upload" : "PIXI.resources.BaseImageResource.prototype.upload";
libWrapper.register(CONSTANTS.MODULE_NAME, override, PIXIUPLOAD);
}
function PIXIUPLOAD(wrapped, ...args) {
let baseTexture = args[1];
if (baseTexture.sequencer_patched || !game.settings.get(CONSTANTS.MODULE_NAME, "enable-global-fix-pixi")) {
return wrapped(...args);
}
let source = args[3];
source = source || this.source;
const isVideo = !!source.videoWidth;
if (isVideo) {
baseTexture.alphaMode = PIXI.ALPHA_MODES.PREMULTIPLIED_ALPHA;
baseTexture.sequencer_patched = true;
}
return wrapped(...args);
}
async function runMigrations() {
const sortedMigrations = Object.entries(migrations).sort((a, b) => {
return isNewerVersion(b[0], a[0]) ? -1 : 1;
});
for (const [version, migration] of sortedMigrations) {
try {
await migration(version);
} catch (err) {
custom_warning(
"Sequencer",
`Something went wrong when migrating to version ${version}. Please check the console for the error!`,
true
);
console.error(err);
}
}
}
function getSequencerEffectTokens(version, tokenFilter = false) {
return Array.from(game.scenes).map((scene) => [
scene,
Array.from(scene.tokens).filter((token) => token.isOwner).filter((token, index) => {
if (tokenFilter) {
return tokenFilter(token, index);
}
const effects = getProperty(token, CONSTANTS.EFFECTS_FLAG) ?? [];
const effectsOutOfDate = effects.filter(
(e) => isNewerVersion(version, e[1].flagVersion)
);
return effectsOutOfDate.length;
})
]).filter(([_, tokens]) => tokens.length);
}
function getSequencerEffectActors(version, actorFilter = false) {
return Array.from(game.actors).filter((actor) => actor.isOwner).filter((actor, index) => {
if (actorFilter) {
return actorFilter(actor, index);
}
const effects = getProperty(actor, CONSTANTS.EFFECTS_FLAG) ?? [];
const effectsOutOfDate = effects.filter(
(e) => isNewerVersion(version, e[1].flagVersion)
);
return effectsOutOfDate.length;
});
}
const migrations = {
"3.0.0": async (version) => {
const actorsToUpdate = getSequencerEffectActors(version, (actor) => {
const effects = getProperty(actor, "prototypeToken." + CONSTANTS.EFFECTS_FLAG) ?? [];
const effectsOutOfDate = effects.filter(
(e) => isNewerVersion(version, e[1].flagVersion)
);
return effectsOutOfDate.length;
});
const actorUpdateArray = actorsToUpdate.map((actor) => {
const effectsToMoveFromTokenPrototype = getProperty(
actor.prototypeToken,
CONSTANTS.EFFECTS_FLAG
).filter(([_, effect]) => {
return effect.persistOptions?.persistTokenPrototype;
}).map(([id, effect]) => {
effect.flagVersion = version;
return [id, effect];
});
const effectsToKeepOnTokenPrototype = getProperty(
actor.prototypeToken,
CONSTANTS.EFFECTS_FLAG
).filter(([_, effect]) => {
return !effect.persistOptions?.persistTokenPrototype;
}).map(([id, effect]) => {
effect.flagVersion = version;
return [id, effect];
});
return {
_id: actor.id,
[CONSTANTS.EFFECTS_FLAG]: effectsToMoveFromTokenPrototype,
["prototypeToken." + CONSTANTS.EFFECTS_FLAG]: effectsToKeepOnTokenPrototype
};
});
const tokensOnScenes = getSequencerEffectTokens(version, (t) => {
let actor;
try {
actor = t.actor;
} catch (err) {
return false;
}
const effects = getProperty(t, CONSTANTS.EFFECTS_FLAG) ?? [];
const prototypeTokenEffects = getProperty(actor, "prototypeToken." + CONSTANTS.EFFECTS_FLAG) ?? [];
const effectsOutOfDate = effects.filter(
(e) => isNewerVersion(version, e[1].flagVersion)
);
const prototypeEffectsOutOfDate = prototypeTokenEffects.filter(
(e) => isNewerVersion(version, e[1].flagVersion)
);
return t.actorLink && (effectsOutOfDate.length || prototypeEffectsOutOfDate.length);
});
for (const [scene, tokens] of tokensOnScenes) {
const updates = [];
for (const token of tokens) {
const effectsToKeepOnToken = getProperty(token, CONSTANTS.EFFECTS_FLAG).filter(([_, effect]) => {
return !effect.persistOptions?.persistTokenPrototype;
}).map(([id, effect]) => {
effect.flagVersion = version;
return [id, effect];
});
updates.push({
_id: token.id,
[CONSTANTS.EFFECTS_FLAG]: effectsToKeepOnToken
});
}
if (updates.length) {
debug(
`Sequencer | Updated ${updates.length} tokens' effects on scene ${scene.id} to version ${version}`
);
await scene.updateEmbeddedDocuments("Token", updates);
}
}
if (actorUpdateArray.length) {
debug(
`Sequencer | Updated ${actorUpdateArray.length} actors' effects to version ${version}`
);
await Actor.updateDocuments(actorUpdateArray);
}
}
};
let moduleValid = false;
let moduleReady = false;
let canvasReady = false;
Hooks.once("init", async function() {
if (!game.modules.get("socketlib")?.active)
return;
moduleValid = true;
CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE = false;
initializeModule();
registerSocket();
});
Hooks.once("socketlib.ready", registerSocket);
Hooks.once("ready", async function() {
if (!game.modules.get("socketlib")?.active) {
ui.notifications.error(
"Sequencer requires the SocketLib module to be active and will not work without it!",
{ console: true }
);
return;
}
for (const [name, func2] of Object.entries(easeFunctions)) {
if (!CanvasAnimation[name]) {
CanvasAnimation[name] = func2;
}
}
if (game.user.isGM) {
await runMigrations();
await migrateSettings();
await PlayerSettings.migrateOldPresets();
}
SequencerFoundryReplicator.registerHooks();
InteractionManager.initialize();
});
Hooks.on("canvasTearDown", () => {
canvasReady = false;
SequencerEffectManager.tearDownPersists();
});
const setupModule = debounce(() => {
if (!moduleValid)
return;
if (!moduleReady) {
moduleReady = true;
debug("Ready to go!");
Hooks.callAll("sequencer.ready");
Hooks.callAll("sequencerReady");
}
if (!canvasReady) {
canvasReady = true;
SequencerEffectManager.initializePersistentEffects();
}
}, 25);
Hooks.on("canvasReady", () => {
setTimeout(() => {
setupModule();
}, 450);
});
Hooks.on("refreshToken", setupModule);
Hooks.on("refreshDrawing", setupModule);
Hooks.on("refreshTile", setupModule);
Hooks.on("refreshMeasuredTemplate", setupModule);
function initializeModule() {
window.Sequence = Sequence$1;
window.Sequencer = {
Player: EffectPlayer,
Presets: SequencerPresets,
Database: SequencerDatabase,
DatabaseViewer: DatabaseViewerApp,
Preloader: SequencerPreloader,
EffectManager: SequencerEffectManager,
SectionManager: new SequencerSectionManager(),
registerEase,
BaseSection: Section,
CONSTANTS: {
EASE
},
Helpers: {
wait: wait$1,
clamp,
interpolate,
random_float_between,
random_int_between,
shuffle_array,
random_array_element,
random_object_element,
make_array_unique
}
};
registerSettings();
registerLayers();
registerHotkeys();
registerLibwrappers();
SequencerAboveUILayer.setup();
SequencerEffectManager.setup();
}
Hooks.once("monaco-editor.ready", registerTypes);
//# sourceMappingURL=module.js.map