From ff1129ddae1cef85a7bd3dbfcf5db82c45937d42 Mon Sep 17 00:00:00 2001 From: xao321 <xao321@hotmail.com> Date: Sat, 11 May 2024 16:27:15 +0200 Subject: [PATCH] Added reflections for water, with distortion animation (currently for sea, docks, beach, lake) Added setting in options to disable reflections Changed where sun/moon enters/leaves canvas, slightly Reverted setting to disable location-image animations --- game/03-JavaScript/debug-menu.js | 18 +- .../weather/00-docs/weather.g.ts | 17 +- .../weather/01-setup/canvas-settings.js | 12 +- .../weather/01-setup/location-images.js | 178 ++++++----- .../weather/01-setup/weather-bindings.js | 4 - .../03-canvas/01-src/00-main/00-sky-canvas.js | 5 + .../03-canvas/01-src/01-effect-manager.js | 2 +- .../03-canvas/01-src/03-observables.js | 1 + .../02-lib/00-effects/effects-location.js | 284 ++++++++++++++++-- .../02-lib/01-layers/layer-location.js | 84 +++--- game/04-Variables/variables-start.twee | 1 + .../04-Variables/variables-versionUpdate.twee | 3 - game/base-system/overlays/options.twee | 28 +- img/misc/locations/docks/reflective.png | Bin 218 -> 195 bytes img/misc/locations/docks/water.png | Bin 389 -> 401 bytes img/misc/locations/lake/water.png | Bin 334 -> 309 bytes img/misc/locations/sea/base.png | Bin 526 -> 0 bytes img/misc/locations/sea/reflective.png | Bin 247 -> 120 bytes img/misc/locations/sea/water.png | Bin 0 -> 242 bytes 19 files changed, 451 insertions(+), 186 deletions(-) delete mode 100644 img/misc/locations/sea/base.png create mode 100644 img/misc/locations/sea/water.png diff --git a/game/03-JavaScript/debug-menu.js b/game/03-JavaScript/debug-menu.js index a0e246ddcd..48715a6fff 100644 --- a/game/03-JavaScript/debug-menu.js +++ b/game/03-JavaScript/debug-menu.js @@ -12,18 +12,8 @@ setup.debugMenu = { setup.debugMenu.eventList = { Main: [ { - link: [`Freeze stats`, stayOnPassageFn], - widgets: [`<<set $statFreeze to true>>`], - condition() { - return !V.statFreeze; - }, - }, - { - link: [`Unfreeze stats`, stayOnPassageFn], - widgets: [`<<set $statFreeze to false>>`], - condition() { - return V.statFreeze; - }, + link: [`Home`, `Bedroom`], + widgets: [`<<endcombat>>`], }, { link: [`Pass 1 minute`, stayOnPassageFn], @@ -65,10 +55,6 @@ setup.debugMenu.eventList = { link: [`Pass 24 hours`, stayOnPassageFn], widgets: [`<<pass 24 hours>>`], }, - { - link: [`Home`, `Bedroom`], - widgets: [`<<endcombat>>`], - }, { link: [`Wardrobe`, `Wardrobe`], widgets: [``], diff --git a/game/03-JavaScript/weather/00-docs/weather.g.ts b/game/03-JavaScript/weather/00-docs/weather.g.ts index fc19a7d519..7eafcf826e 100644 --- a/game/03-JavaScript/weather/00-docs/weather.g.ts +++ b/game/03-JavaScript/weather/00-docs/weather.g.ts @@ -1,8 +1,9 @@ interface ImageLocation { folder: string; - base: ImageSetting | { [key: string]: ImageSetting }; + base?: ImageSetting | { [key: string]: ImageSetting }; emissive?: EmissiveSetting | { [key: string]: EmissiveSetting }; - reflective?: ReflectiveSetting }; + reflective?: ReflectiveSetting; + layerTop?: ImageSetting | { [key: string]: ImageSetting }; } interface AnimationSetting { @@ -34,14 +35,20 @@ interface EmissiveSetting { } interface ReflectiveSetting { + /** + * The primary mask setting used to define the basic masking properties of a reflection. + * This should be the first property defined for clarity when setting up reflective properties. + */ mask: MaskSetting; - [key: string]: ImageSetting; + [key: string]: ImageSetting | any; } interface MaskSetting { image: string; - alpha?: number; - horizon?: number; + alpha?: number | (() => number); + horizon?: number | (() => number); + waveShiftFactor?: number | (() => number); + animationCondition?: boolean | (() => boolean); } declare global { diff --git a/game/03-JavaScript/weather/01-setup/canvas-settings.js b/game/03-JavaScript/weather/01-setup/canvas-settings.js index b9d9f89c0a..eff6abc00f 100644 --- a/game/03-JavaScript/weather/01-setup/canvas-settings.js +++ b/game/03-JavaScript/weather/01-setup/canvas-settings.js @@ -23,8 +23,8 @@ setup.SkySettings = { riseTime: 7, setTime: 19, path: { - startX: -14, // Start offscreen - endX: 64 + 14, // End offscreen + startX: -7, + endX: 64 + 7, peakY: 70, horizon: 162, }, @@ -35,8 +35,8 @@ setup.SkySettings = { riseTime: 19, setTime: 7, path: { - startX: -12, // Start offscreen - endX: 64 + 12, // End offscreen + startX: -6, + endX: 64 + 6, peakY: 50, // The y value of the top of the orbit arc horizon: 162, // The y value of the horizon }, @@ -45,8 +45,8 @@ setup.SkySettings = { riseTime: 19, setTime: 7, path: { - startX: -12, // Start offscreen - endX: 64 + 12, // End offscreen + startX: -6, + endX: 64 + 6, peakY: 90, horizon: 162, }, diff --git a/game/03-JavaScript/weather/01-setup/location-images.js b/game/03-JavaScript/weather/01-setup/location-images.js index e48ae870c6..38384868be 100644 --- a/game/03-JavaScript/weather/01-setup/location-images.js +++ b/game/03-JavaScript/weather/01-setup/location-images.js @@ -202,9 +202,17 @@ setup.LocationImages = { condition: () => Weather.isSnow, image: "snow.png", }, - water: { + }, + reflective: { + mask: { + image: "reflective.png", + alpha: 0.7, + }, + overlay: { image: "water.png", }, + }, + layerTop: { tree: { image: "tree.png", animation: { @@ -212,13 +220,7 @@ setup.LocationImages = { cycleDelay: () => 2500, }, }, - }, - reflective: { - image: "reflective.png", - backgroundOnly: true, - animation: "tree", - blur: 0, - }, + } }, bog: { folder: "bog", @@ -314,9 +316,9 @@ setup.LocationImages = { color: "#deae66", strength: 2, }, - reflective: { - image: "reflective.png", - }, + // reflective: { + // image: "reflective.png", + // }, }, cafe_construction: { folder: "cafe_construction", @@ -336,9 +338,9 @@ setup.LocationImages = { color: "#deae66", strength: 2, }, - reflective: { - image: "reflective.png", - }, + // reflective: { + // image: "reflective.png", + // }, }, cafe_renovated: { folder: "cafe_renovated", @@ -352,9 +354,9 @@ setup.LocationImages = { image: "snow.png", }, }, - reflective: { - image: "reflective.png", - }, + // reflective: { + // image: "reflective.png", + // }, }, canal: { folder: "canal", @@ -384,9 +386,9 @@ setup.LocationImages = { }, }, }, - reflective: { - image: "reflective.png", - }, + // reflective: { + // image: "reflective.png", + // }, }, churchyard: { folder: "churchyard", @@ -495,9 +497,6 @@ setup.LocationImages = { condition: () => Weather.isSnow, image: "snow.png", }, - water: { - image: "water.png", - }, boat: { // Not at same time as cruiser waitForAnimation: "cruiser", @@ -521,8 +520,14 @@ setup.LocationImages = { }, }, reflective: { - image: "reflective.png", - alpha: 0.6, + mask: { + image: "reflective.png", + alpha: 0.2, + waveShiftFactor: 0.004, // default is 0.006 + }, + overlay: { + image: "water.png", + }, }, }, drain: { @@ -547,10 +552,18 @@ setup.LocationImages = { }, }, }, - reflective: { - image: "reflective.png", - alpha: 0.6, - }, + // reflective: { + // mask: { + // image: "reflective.png", + // }, + // water: { + // image: "water.png", + // animation: { + // frameDelay: 1000, + // cycleDelay: () => 2000, + // }, + // }, + // }, }, estate: { folder: "estate", @@ -804,7 +817,7 @@ setup.LocationImages = { image: "snow.png", }, smoke: { - condition: () => !Time.bloodMoon, + condition: () => !Weather.bloodMoon, image: "smoke.png", animation: { frameDelay: 200, @@ -868,21 +881,19 @@ setup.LocationImages = { default: { image: "base.png", }, - water: { - image: "water.png", - animation: { - frameDelay: 500, - cycleDelay: () => 500, - }, - }, - }, - reflective: { - image: "reflective.png", - animation: "water", - horizon: 122, - alpha: 0.25, - blur: 0.8, }, + // reflective: { + // mask: { + // image: "reflective.png", + // }, + // overlay: { + // image: "water.png", + // animation: { + // frameDelay: 500, + // cycleDelay: () => 500, + // }, + // }, + // }, }, kylar_manor: { folder: "kylar_manor", @@ -915,18 +926,6 @@ setup.LocationImages = { condition: () => Weather.isSnow, image: "snow.png", }, - water: { - condition: () => !Weather.isFrozen("lake"), - image: "water.png", - animation: { - frameDelay: 1000, - cycleDelay: () => 0, - }, - }, - ice: { - condition: () => Weather.isFrozen("lake"), - image: "ice.png", - }, deer: { condition: () => !Weather.isSnow && Time.dayState === "dawn", image: "deer.png", @@ -976,8 +975,25 @@ setup.LocationImages = { }, }, reflective: { - image: "reflective.png", - horizon: 156, + mask: { + image: "reflective.png", + alpha: () => (!Weather.isFrozen("lake") ? 0.8 : 0.35), + horizon: 18, + waveShiftFactor: 0.009, + animationCondition: () => !Weather.isFrozen("lake"), + }, + water: { + condition: () => !Weather.isFrozen("lake"), + image: "water.png", + animation: { + frameDelay: 1050, + cycleDelay: () => 0, + }, + }, + ice: { + condition: () => Weather.isFrozen("lake"), + image: "ice.png", + }, }, }, lake_ruin: { @@ -1032,17 +1048,17 @@ setup.LocationImages = { color: "#e63e3e", }, }, - reflective: { - default: { - condition: () => !Weather.bloodMoon, - image: "reflective.png", - }, - bloodMoon: { - condition: () => Weather.bloodMoon, - image: "reflective_blood.png", - }, - horizon: 112, - }, + // reflective: { + // default: { + // condition: () => !Weather.bloodMoon, + // image: "reflective.png", + // }, + // bloodMoon: { + // condition: () => Weather.bloodMoon, + // image: "reflective_blood.png", + // }, + // horizon: 112, + // }, }, landfill: { folder: "landfill", @@ -1127,10 +1143,10 @@ setup.LocationImages = { }, }, }, - reflective: { - image: "reflective.png", - horizon: 112, - }, + // reflective: { + // image: "reflective.png", + // horizon: 112, + // }, }, museum: { folder: "museum", @@ -1511,16 +1527,14 @@ setup.LocationImages = { }, sea: { folder: "sea", - base: { - image: "base.png", - animation: { - frameDelay: 300, - cycleDelay: () => 0, - }, - }, reflective: { - image: "reflective.png", - alpha: 0.8, + mask: { + image: "reflective.png", + alpha: 0.7, + }, + overlay: { + image: "water.png", + }, }, }, pirate_ship: { diff --git a/game/03-JavaScript/weather/01-setup/weather-bindings.js b/game/03-JavaScript/weather/01-setup/weather-bindings.js index 5341ce0ff1..db775e8127 100644 --- a/game/03-JavaScript/weather/01-setup/weather-bindings.js +++ b/game/03-JavaScript/weather/01-setup/weather-bindings.js @@ -48,10 +48,6 @@ setup.WeatherBindings = { variable: () => Weather.lightsOn, layers: ["location"], }, - locationAnimations: { - variable: () => V.options.locationAnimations, - layers: ["location"], - }, fireOn: { variable: () => { if (V.bird.firepit === undefined) return null; diff --git a/game/03-JavaScript/weather/03-canvas/01-src/00-main/00-sky-canvas.js b/game/03-JavaScript/weather/03-canvas/01-src/00-main/00-sky-canvas.js index 723cf8708b..15798af1fa 100644 --- a/game/03-JavaScript/weather/03-canvas/01-src/00-main/00-sky-canvas.js +++ b/game/03-JavaScript/weather/03-canvas/01-src/00-main/00-sky-canvas.js @@ -19,6 +19,11 @@ Weather.Sky = (() => { this.element.height = height; } + reset() { + this.clear(); + this.ctx.reset(); + } + /* Aliases */ clear() { this.ctx.clearRect(0, 0, this.element.width, this.element.height); diff --git a/game/03-JavaScript/weather/03-canvas/01-src/01-effect-manager.js b/game/03-JavaScript/weather/03-canvas/01-src/01-effect-manager.js index f0e9a1278e..21fc0c047c 100644 --- a/game/03-JavaScript/weather/03-canvas/01-src/01-effect-manager.js +++ b/game/03-JavaScript/weather/03-canvas/01-src/01-effect-manager.js @@ -11,7 +11,7 @@ Weather.Sky.Effects = (() => { params = { ...optionalParams, ...params }; if (_effects.has(params.name)) { - Weather.Sky.errors.add("Effects", `Effect with name '${params.name}', already exists, and will be overwritten.`); + console.error("Effects", `Effect with name '${params.name}', already exists, and will be overwritten.`); } const { name, ...context } = params; diff --git a/game/03-JavaScript/weather/03-canvas/01-src/03-observables.js b/game/03-JavaScript/weather/03-canvas/01-src/03-observables.js index bb61afcc13..9ed92d7685 100644 --- a/game/03-JavaScript/weather/03-canvas/01-src/03-observables.js +++ b/game/03-JavaScript/weather/03-canvas/01-src/03-observables.js @@ -81,6 +81,7 @@ Weather.Observables = (() => { }); return { + objects: observables, checkForUpdate: setBindings, }; })(); diff --git a/game/03-JavaScript/weather/03-canvas/02-lib/00-effects/effects-location.js b/game/03-JavaScript/weather/03-canvas/02-lib/00-effects/effects-location.js index df5ad753f2..9a63941068 100644 --- a/game/03-JavaScript/weather/03-canvas/02-lib/00-effects/effects-location.js +++ b/game/03-JavaScript/weather/03-canvas/02-lib/00-effects/effects-location.js @@ -20,13 +20,106 @@ Weather.Sky.Effects.create({ }, }, }, + { + effect: "colorOverlay", + drawCondition: () => !Weather.Sky.skyDisabled, + params: { + color: { + nightDark: "#00001ceb", + nightBright: "#0d0d26bf", + day: "#00000000", + dawnDusk: "#4f3605a5", + bloodMoon: "#380101bf", + }, + }, + bindings: { + sunFactor() { + return Weather.Sky.orbitals.sun.factor; + }, + moonFactor() { + return Weather.Sky.moonBrightnessFactor; + }, + bloodMoon() { + return Weather.bloodMoon; + }, + }, + }, ], init() { this.animationFrame = 0; }, draw() { + // Add the overlay to the effect itself this.effects[0].draw(); + this.effects[1].draw(); this.canvas.drawImage(this.effects[0].canvas.element); + this.canvas.ctx.globalCompositeOperation = "source-atop"; + this.canvas.drawImage(this.effects[1].canvas.element); + + }, +}); + +// Fallback only if reflections are disabled +Weather.Sky.Effects.create({ + name: "locationWater", + effects: [ + { + effect: "locationImageAnimation", + bindings: { + location() { + return this.location; + }, + key() { + return this.key; + }, + parentLayer() { + return this.parentLayer; + }, + fullPath() { + return `${this.path}/` + (this.location.folder ? `${this.location.folder}/` : ""); + }, + }, + }, + { + effect: "colorOverlay", + drawCondition: () => !Weather.Sky.skyDisabled, + params: { + color: { + nightDark: "#00001ceb", + nightBright: "#0d0d26bf", + day: "#00000000", + dawnDusk: "#4f3605a5", + bloodMoon: "#380101bf", + }, + }, + bindings: { + sunFactor() { + return Weather.Sky.orbitals.sun.factor; + }, + moonFactor() { + return Weather.Sky.moonBrightnessFactor; + }, + bloodMoon() { + return Weather.bloodMoon; + }, + }, + }, + ], + init() { + this.animationFrame = 0; + }, + draw() { + // Ignore mask + this.effects[0].draw({ start: key => { + if (this.key === "reflective" && key === "mask") { + return false; + } + return true; + }}); + this.effects[1].draw(); + this.canvas.drawImage(this.effects[0].canvas.element); + this.canvas.ctx.globalCompositeOperation = "source-atop"; + this.canvas.drawImage(this.effects[1].canvas.element); }, }); @@ -58,7 +151,8 @@ Weather.Sky.Effects.create({ }, ], draw() { - this.effects[0].draw((drawCanvas, obj) => { + // Set the blur + this.effects[0].draw({ start: (key, obj, drawCanvas) => { const glowSize = obj.size ?? this.defaultSize; const glowColor = obj.color ?? this.defaultColor; const glowAlpha = obj.alpha ?? this.defaultAlpha; @@ -66,8 +160,8 @@ Weather.Sky.Effects.create({ drawCanvas.ctx.shadowBlur = glowSize; drawCanvas.ctx.filter = `blur(0.5px) drop-shadow(0px 0px ${glowSize}px ${glowColor})`; drawCanvas.ctx.globalAlpha = glowAlpha; - return drawCanvas; - }); + return true; + }}); this.canvas.drawImage(this.effects[0].canvas.element); }, }); @@ -75,10 +169,14 @@ Weather.Sky.Effects.create({ Weather.Sky.Effects.create({ name: "locationReflective", defaultParameters: { - horizon: 112, - blur: 0.6, - reflectionAlpha: 0.8, - contrast: 0.9, + defaultHorizon: 40, + defaultBlur: 0.7, + defaultAlpha: 0.7, + defaultContrast: 0.9, + minAmplitude: 1, + maxAmplitude: 6, + waveFrequency: 10, + defaultWaveShiftFactor: 0.006, }, effects: [ { @@ -92,27 +190,167 @@ Weather.Sky.Effects.create({ }, parentLayer() { return this.parentLayer; - } + }, + fullPath() { + return `${this.path}/` + (this.location.folder ? `${this.location.folder}/` : ""); + }, + }, + }, + { + effect: "colorOverlay", + params: { + color: { + nightDark: "#00001ceb", + nightBright: "#0d0d26bf", + day: "#00000000", + dawnDusk: "#4f3605a5", + bloodMoon: "#380101bf", + }, + }, + bindings: { + sunFactor() { + return Weather.Sky.orbitals.sun.factor; + }, + moonFactor() { + return Weather.Sky.moonBrightnessFactor; + }, + bloodMoon() { + return Weather.bloodMoon; + }, }, }, ], init() { + if (this.location?.[this.key] === undefined) return; + const obj = this.location[this.key].mask; + if (obj === undefined) throw new Error("Property 'mask' is not defined in reflective property.") + this.reflectionCanvas = new Weather.Sky.Canvas(); this.locationCanvas = new Weather.Sky.Canvas(); + this.distortionMask = new Weather.Sky.Canvas(); + this.distortionCanvas = new Weather.Sky.Canvas(this.canvas.element.width, this.canvas.element.height); + + this.horizon = resolveValue(obj.horizon, this.defaultHorizon) * setup.SkySettings.scale; + this.overlayAlpha = resolveValue(obj.alpha, this.defaultAlpha); + this.blur = resolveValue(obj.blur, this.defaultBlur); + this.contrast = resolveValue(obj.contrast, this.defaultContrast); + this.waveShiftFactor = resolveValue(obj.waveShiftFactor, this.defaultWaveShiftFactor); + this.animationCondition = resolveValue(obj.animationCondition, true); + + // Precalculate the sine curve for multiple frames + this.sineFrames = []; + const numFrames = 24; + // Perform a full cycle to look fluent + const phaseShiftPerFrame = (2 * Math.PI) / numFrames; + + this.startY = 2 + this.locationCanvas.element.height - ((this.locationCanvas.element.height - this.horizon) * setup.SkySettings.scale); + for (let frame = 0; frame < numFrames; frame++) { + const sines = []; + for (let y = 0; y < this.distortionCanvas.element.height; y++) { + // Linear interpolation for amplitude based on y position + const amplitude = this.minAmplitude + (this.maxAmplitude - this.minAmplitude) * (y / this.distortionCanvas.element.height); + const basePhase = (y + this.startY) * this.waveFrequency; + const animPhase = basePhase + frame * phaseShiftPerFrame; + const totalSineValue = Math.sin(basePhase) * amplitude + Math.sin(animPhase) * amplitude * 0.5; + sines.push(totalSineValue); + } + this.sineFrames.push(sines); + } + + // Set up animation properties + const animationOptions = { + image: this.distortionCanvas.element, + canvas: this.canvas, + numFrames, + frameDelay: 150, // milliseconds per frame + cycleDelay: 0, // No delay between cycles + startDelay: 0, + currentFrame: 0, + alwaysDisplay: true, + condition: () => typeof this.animationCondition === "function" ? this.animationCondition() : this.animationCondition, + }; + + // Only try to draw overlay if there is one + this.drawOverlay = this.effects[0].animations.size > 0 && [...this.effects[0].animations.keys()].some(key => key !== "mask"); + + this.animation = new Weather.Sky.Animation(animationOptions); + this.parentLayer.animationGroup.add("distortionAnimation", this.animation); + this.animation.enable(); }, - draw(canvas, layerCanvas) { - // Temporarily disable reflections until next update + draw(canvas, locationLayer) { + this.reflectionCanvas.reset(); + this.locationCanvas.reset(); + this.distortionMask.reset(); + this.distortionCanvas.reset(); + + // Draw the background canvas, then flip it upside down, and only draw it on top of the reflection map + this.locationCanvas.drawImage(canvas.element); + this.locationCanvas.drawImage(locationLayer.element); + this.reflectionCanvas.ctx.save(); + this.reflectionCanvas.ctx.filter = `blur(${this.blur}px) contrast(${this.contrast})`; + this.reflectionCanvas.ctx.globalCompositeOperation = 'source-over'; + this.reflectionCanvas.ctx.scale(1, -1); + this.reflectionCanvas.ctx.drawImage( + this.locationCanvas.element, + 0, + canvas.element.height - (this.horizon * 2), + this.reflectionCanvas.element.width, + this.horizon, + 0, + -canvas.element.height, + this.reflectionCanvas.element.width, + this.horizon + ); + this.reflectionCanvas.ctx.restore(); + + // Save the mask separately from the other water sprites + this.effects[0].draw({ end: (key, obj, drawCanvas) => { + if (key === "mask") { + this.distortionMask.drawImage(drawCanvas.element); + drawCanvas.clear(); + } + return true; + }}); + + // Overlay sky only on the water effects + if (this.drawOverlay) { + this.effects[1].draw(); + this.effects[0].canvas.ctx.globalCompositeOperation = "source-atop"; + this.effects[0].canvas.drawImage(this.effects[1].canvas.element); + + // Add the rest of the water effects here + this.reflectionCanvas.ctx.globalAlpha = 1 - this.overlayAlpha; + this.reflectionCanvas.drawImage(this.effects[0].canvas.element); + } + + // Draw the reflection below the distortion first, in case of transparent pixels + this.distortionCanvas.drawImage(this.reflectionCanvas.element); + + // Loop through the predetermined distortion frames for a simplified ripple effect + const currentFrameIndex = this.animation.currentFrame; + const sines = this.sineFrames[currentFrameIndex]; + for (let y = 0; y < this.distortionCanvas.element.height; y++) { + const shiftX = sines[y] * this.waveShiftFactor; + this.distortionCanvas.ctx.setTransform(1, 0, shiftX, 1, 0, 0); + this.distortionCanvas.ctx.drawImage(this.reflectionCanvas.element, 0, y + this.startY, this.distortionCanvas.element.width, 1, 0, y + this.startY, this.distortionCanvas.element.width, 1); + } + this.distortionCanvas.ctx.setTransform(1, 0, 0, 1, 0, 0); + // Remove any distortions that are outside of the water mask + this.distortionCanvas.ctx.globalCompositeOperation = 'destination-in'; + this.distortionCanvas.drawImage(this.distortionMask.element); + // Draw the final frame + this.canvas.drawImage(this.distortionCanvas.element); }, }); Weather.Sky.Effects.create({ name: "locationImageAnimation", - // Make it asyncronous to wait for the image to load before animating without slowing down the main flow async init() { const loadImage = async (key, obj) => { + // Make it asyncronous to wait for the image to load before animating without slowing down the main flow return new Promise((resolve, reject) => { let image = new Image(); const imagePath = typeof obj === "object" && obj.image ? obj.image : this.obj; @@ -120,13 +358,14 @@ Weather.Sky.Effects.create({ const handleLoadedImage = () => { if (typeof obj !== "object") obj = {}; - if (V.options.locationAnimations && obj.animation) { + // If it's an animation, add it to the animation group + if (obj.animation) { const animationOptions = { image, canvas: this.canvas, alwaysDisplay: obj.alwaysDisplay, waitForAnimation: obj.waitForAnimation, - frameDelay: obj.animation.frameDelay, // Will never be lower than the layer updateRate + frameDelay: obj.animation.frameDelay, cycleDelay: obj.animation.cycleDelay, startDelay: obj.animation.startDelay, startY: this.canvas.element.height - image.height, @@ -142,7 +381,7 @@ Weather.Sky.Effects.create({ obj.animation.enable(); this.parentLayer.animationGroup.add(key, obj.animation); } - else { + else { // If it's a static image obj.image = image; } this.animations.set(key, obj); @@ -188,19 +427,20 @@ Weather.Sky.Effects.create({ } }, - draw(onDraw) { - for (const obj of this.animations.values()) { + draw(onDraw = {}) { + this.canvas.ctx.reset(); + for (const [key, obj] of this.animations.entries()) { // Preprocess based on effect - if (onDraw) { - onDraw(this.canvas, obj); + if (onDraw.start) { + if (!onDraw.start(key, obj, this.canvas)) continue; } // Check conditions - if (V.options.locationAnimations && obj.condition && !obj.condition(this.parentLayer.animationGroup)) { + if (obj.condition && !obj.condition(this.parentLayer.animationGroup)) { continue; } - if (V.options.locationAnimations && obj.animation) { // If animation, let the animation object draw the right frame + if (obj.animation) { // If animation, let the animation object draw the right frame obj.animation.draw(); } else { // If static image const yPosition = this.canvas.element.height - obj.image.height; @@ -211,6 +451,10 @@ Weather.Sky.Effects.create({ this.canvas.ctx.drawImage(obj.image, frameX, 0, this.canvas.element.width, this.canvas.element.height, 0, yPosition, this.canvas.element.width, this.canvas.element.height); } + + if (onDraw.end) { + onDraw.end(key, obj, this.canvas); + } } }, }); diff --git a/game/03-JavaScript/weather/03-canvas/02-lib/01-layers/layer-location.js b/game/03-JavaScript/weather/03-canvas/02-lib/01-layers/layer-location.js index 5c0fb42bd9..d3806f1c58 100644 --- a/game/03-JavaScript/weather/03-canvas/02-lib/01-layers/layer-location.js +++ b/game/03-JavaScript/weather/03-canvas/02-lib/01-layers/layer-location.js @@ -26,34 +26,46 @@ Weather.Sky.Layers.add({ }, }, { - effect: "colorOverlay", - compositeOperation: "source-atop", - drawCondition: () => !Weather.Sky.skyDisabled, + effect: "locationEmissive", + drawCondition: () => { + return setup.LocationImages[setup.Locations.get()].emissive; + }, params: { - color: { - nightDark: "#00001ceb", - nightBright: "#0d0d26bf", - day: "#00000000", - dawnDusk: "#4f3605a5", - bloodMoon: "#380101bf", - }, + path: "img/misc/locations", }, bindings: { - sunFactor() { - return Weather.Sky.orbitals.sun.factor; + location() { + const location = setup.Locations.get(); + return setup.LocationImages[location]; }, - moonFactor() { - return Weather.Sky.moonBrightnessFactor; + key() { + return "emissive"; }, - bloodMoon() { - return Weather.bloodMoon; + }, + }, + { + effect: "locationReflective", + drawCondition: () => { + return V.options.reflections && !!setup.LocationImages[setup.Locations.get()].reflective; + }, + params: { + path: "img/misc/locations", + }, + bindings: { + location() { + const location = setup.Locations.get(); + return setup.LocationImages[location]; + }, + key() { + return "reflective"; }, }, }, + // Fallback only if reflections are disabled { - effect: "locationEmissive", + effect: "locationWater", drawCondition: () => { - return setup.LocationImages[setup.Locations.get()].emissive; + return !V.options.reflections && !!setup.LocationImages[setup.Locations.get()].reflective; }, params: { path: "img/misc/locations", @@ -64,28 +76,26 @@ Weather.Sky.Layers.add({ return setup.LocationImages[location]; }, key() { - return "emissive"; + return "reflective"; + }, + }, + }, + // Draw on top + { + effect: "locationImage", + params: { + path: "img/misc/locations", + }, + bindings: { + location() { + const location = setup.Locations.get(); + return setup.LocationImages[location]; + }, + key() { + return "layerTop"; }, }, }, - // { - // effect: "locationReflective", - // drawCondition: () => { - // return false;//!!setup.LocationImages[setup.Locations.get()].reflective; - // }, - // params: { - // path: "img/misc/locations", - // }, - // bindings: { - // location() { - // const location = setup.Locations.get(); - // return setup.LocationImages[location]; - // }, - // key() { - // return "reflective"; - // }, - // }, - // }, ], }); diff --git a/game/04-Variables/variables-start.twee b/game/04-Variables/variables-start.twee index 891f1f30e6..448208b83c 100644 --- a/game/04-Variables/variables-start.twee +++ b/game/04-Variables/variables-start.twee @@ -75,6 +75,7 @@ sidebarFontSize: _globalThemeDefaults.sidebarFontSize, font: _globalThemeDefaults.font, weatherUpdate: true, + reflections: true, fahrenheit: false, }>> <<setFont>> diff --git a/game/04-Variables/variables-versionUpdate.twee b/game/04-Variables/variables-versionUpdate.twee index b63c03b700..141955bd0b 100644 --- a/game/04-Variables/variables-versionUpdate.twee +++ b/game/04-Variables/variables-versionUpdate.twee @@ -5200,9 +5200,6 @@ <<if $options.clothingCaption is undefined>> <<set $options.clothingCaption to true>> <</if>> - <<if $options.locationAnimations is undefined>> - <<set $options.locationAnimations to true>> - <</if>> <!-- Remove before final push, only for active testers --> <<if $weatherObj.ice is undefined>> diff --git a/game/base-system/overlays/options.twee b/game/base-system/overlays/options.twee index e7119290c7..5f3e7d8ff4 100644 --- a/game/base-system/overlays/options.twee +++ b/game/base-system/overlays/options.twee @@ -400,15 +400,6 @@ IMPORTANT: <<widget "optionsperformance">> <<setupOptions>> <<if StartConfig.enableImages is true>> - <span class="gold">Experimental features</span> - <br> - <div class="description">Close the options menu for the change to apply.</div> - <div> - <div class="settingsToggle"> - <label data-target="options.images" data-disabledif="V.options.images===0"><<checkbox "$options.weatherUpdate" false true autocheck>> New weather renderer for sidebar</label> - </div> - </div> - <br><br><br> <span class="gold">Images</span> <br> <div> @@ -444,13 +435,26 @@ IMPORTANT: <div style="clear:both;">/*Keep at end of toggles*/</div> </div> <br> - <span class="gold">Animations</span> + <span class="gold">Sidebar</span> + <br> + <div class="description">Close the options menu for the change to apply.</div> <div> <div class="settingsToggle"> - <label data-target="options.images" data-disabledif="V.options.images===0"><<checkbox "$options.sidebarAnimations" false true autocheck>> Sidebar character animations</label> + <label data-target="options.images" data-disabledif="V.options.images===0"><<checkbox "$options.weatherUpdate" false true autocheck>> Use new weather renderer for sidebar</label> </div> + </div> + <div> + <div class="settingsToggle"> + <label data-target='["options.images", "weatherupdate"]' data-disabledif="V.options.images===0||V.options.weatherUpdate===false"><<checkbox "$options.reflections" false true autocheck>> Render water reflections + <span class="linkBlue" tooltip="Could cause performance issues on old devices.">(?)</span> + </label> + </div> + </div> + <br><br><br> + <span class="gold">Animations</span> + <div> <div class="settingsToggle"> - <label data-target="options.images" data-disabledif="V.options.images===0"><<checkbox "$options.locationAnimations" false true autocheck>> Location image animations</label> + <label data-target="options.images" data-disabledif="V.options.images===0"><<checkbox "$options.sidebarAnimations" false true autocheck>> Sidebar character animations</label> </div> <div class="settingsToggle"> <label data-target='["options.images", "sidebaranimations"]' data-disabledif="V.options.images===0||V.options.sidebarAnimations===false"><<checkbox "$options.blinkingEnabled" false true autocheck>> Animate eyes blinking</label> diff --git a/img/misc/locations/docks/reflective.png b/img/misc/locations/docks/reflective.png index 872f769ec69a877a19410c241e92f6353ac57a7b..0b4149537b6fcd2e23fafcfce80874699a4800bf 100644 GIT binary patch delta 154 zcmcb`c$jg5VLeZPPlzi61H*q9usC470?2163GxeOIQ{znzK6Fa0{NkyE{-7<y>HJ1 zavf0MVZNZY_y64R#9d`a8y6+ZcXN2fD}Q=!%ovd8#k@tiiaUnc@d4+GXH7v>rV)&f z@AQBECM0lFPT+{lha?7;GdxR}BOc#>{(q@VkMITNYoEe;{{wAc@O1TaS?83{1OVRk BLz4gi delta 177 zcmX@ic#Cm@VLg9<Plzi61H*qL(C>6F11Q8=666=mP_pm!|H?Y`Q$T?XPZ!4!i{7_= zC%FzN@VIO~^S{1WSUXqjrK84|3+j%B`iouc=R3OWk($0>n}@cdW8_T34aYQ87(Zxz z);!20(s|=s<{kby(c)?Ob58O5To5pH^wgTMz458M)0xQ^l+9RkI3wRNz2$me%%F0b VRY2EUuN7!1gQu&X%Q~loCIGX;N<RPq diff --git a/img/misc/locations/docks/water.png b/img/misc/locations/docks/water.png index ddc75ec34c17380007fb071db77cac65a19a443e..58fd3a7ed80c566fb87283f53ae1aa5ef885553e 100644 GIT binary patch delta 361 zcmV-v0ha!S1CaxeF@H-*L_t(og=1hC1*2dTjDk@x(g9=tvp4lHPN9@YBQl>FpioMr z5uZHEqWiBH&Rc6W8Xmn?57Pi+Qv%LgYvHvR?f?b`28Q(V`ubB)nt_3VVb{rPSQQJH zYr)wcPF};{qpKmx0Uu6YW7s9G%W&UXtMNPo1A}7UntEakLVuTs@sUH57{CZqY_@47 z0|NsC!zsUTxNk@W0_Ivo2R#h1z%1J(t_u;tVmY>;#wJG&U?IjbY#{<uM{eAsI6%N$ z3$JDPEI=2Jv2gjn<is9Ek{m$D7avYuBRXA3^K%euH^KqP7Lfw5I(R2ZMqs7^QUEpw ztBP}z6b($2IAQ=BXf+6JASD{8?f@8wv2giMj5>;<02fdd=O$V$(IIfZD5UXzQ3z3` z-0hcN85kId3k9MaK&-{+fS9yElQgi(J)r@{CN?dMf&l{no%?zHdv~H900000NkvXX Hu0mjfG#`<A delta 349 zcmV-j0iynq1BC;SF@HZvL_t(og=1hC1*2dTjDk@x(g9=tvp4@?oI)uP6h1XTp_B+C zUI!Q+z4rgSwHBOaU|?V{JbH~{N5IS?1Xvgt7#Pya>;IpE(hLj?47*NV!>U-oTno<r zaPk@kA6*SGfKjn;&3^_428R1ZA&d+R3=A+nsRqFSdT0`K0DmmF%{HxMU|?WiIOP`( z_YJ8)z+8*ypoak#m}R@fbs-{HEXNkq*yP9oEW}ubEkt1I$c=jx2MCyJ;k68(1?b{2 z7B2snoY+HjG_W9Bip!D@C$ABmE~NQ6h_xHx0A!0u0azWplO!WB(*P*|n}b!wxk-u! zCQ2ND4YV4Bl2#NARCfRj#8|ixRh&~41-O8!I5*L1i4KALMInr^;+hb+{qidV0|RlP vK$HWBwHO@`lNM-_23ENz(8AbJKpOx6^R0EqA}}Bw00000NkvXXu0mjf-A##p diff --git a/img/misc/locations/lake/water.png b/img/misc/locations/lake/water.png index bde749d6623a092f12a14f647bae8a627397e53d..fa7c28ff4319a37b312a9065b82632fca2628ef0 100644 GIT binary patch delta 160 zcmV;R0AK&k0<{8=oqvN#L_t(oh3$|_3cw%?M00~0I`=<oO*DR}U5Q=I1108VASglz zA%qY@2q6aU3|i#?|9E(<uw$j&P<x{&Z#vZ@p51TcMO()+Is)x>iOapbynQNsre?pJ zm$7|0DSyp6cEU@Q$QCf|bIGO<Yo7G6K&%%`pLo*80)w$t5g9}2w0{6%s6fj+|KR`t O0000<MNUMnLSTZUD@D}+ delta 185 zcmV;q07n0{0?q=EoqwE3L_t(oh3%113dA4`L~{cTJNG~9n&@gu)fR;Png=cFn-NhF zLI@#*5JCts@F}2=9N<45K33RgL;FK*j;egoX&%4n@CSX-zTzE?K!=^;@~kg6pBwGW z_g3(}$10Ft_2sgjoILBD>2*M=Ly<s{HI^-ee##I?Pl4GEyE49vTS*tfEgkB8pxr<a nc2YZP(*9J7z<A0{;Hm!wfU`i_fr*9Z00000NkvXXu0mjf0vA^U diff --git a/img/misc/locations/sea/base.png b/img/misc/locations/sea/base.png deleted file mode 100644 index 1f4234d2e589296a31c13d9ce0263ae5a19f496b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 526 zcmV+p0`dKcP)<h;3K|Lk000e1NJLTq00961001Be0{{R3%6C@Q0000IP)t-s0000P zlC(6n)hNv0itF)-;r+}eVrBpU00wkYPE!E?|NsC0dU@df0004{Nkl<ZXo2mP*^a{? z3`N0A{{NS&ge2H_11ya+Q|?pSW3G+MmeSVN*4Eb6*4Eb6)>o>WI({x4&xiV@>G}CU z90z!OG=avW`3?J&Zvhl6$h~Mll=FZ&KAm5)Pb8}Lg_}2i2CVae1SXH>7wmBZP?JQD z#^n@fyn`M4HUF9VT0r7a;^4+@>~YsV=<o9`fUJP4IY&Jq1S5EHl*{XMoCosv^Lrc6 z4o10$?c`kJLkSEX&9B+hEr3r-d7q*Hhr8pYfC;`*pI~1b0QB1AcL=>#=XtRcxICI) zw?``gD!IFKZk4`D$eHY<o)?RAdzj*$|44np{8Af(#cH$g-{aCX$b+cOf8{B^YeU^8 zH_zHOD-MTZC-C@y{|J}uliD9vquF68;1~IN{nrZMy&+fX0B60&ukQ1VmkkEF=O3w` z9)gg&W_A5@IAovG|6-m;>XUfVkB8^gJkRgOtILlJU*(4XO#KNhC_&G93vp2K7?0*R z=cl{_@MsN>at*g5z~<5X_WbBAK-+b+w-g{piPwOx-;8$#(_HuW>eJ`<0a|1$BiNZY Qh5!Hn07*qoM6N<$g7CNo@c;k- diff --git a/img/misc/locations/sea/reflective.png b/img/misc/locations/sea/reflective.png index aa9c933c0a92ebf7a93dad303dacadd28d32c5d9..c6b1ef982c7bf81d84994d7f20f4bf13d04730f7 100644 GIT binary patch delta 90 zcmey)STR8*gfqY=#Fc@8;XfD@S1e5ia+phk{DK*_{`WgpxDLpb_H=O!vFJ@skYH_Q oGZ13n@nJczqA8H|N+<(^8W+pM^|{mffXWy=UHx3vIVCg!0OeaAt^fc4 delta 218 zcmV<0044u;_W_V3e*tq+M?wIu&K&6g000tDOjJbx000@1v^2HVD9qoA>+y-<{mdp} zW&i*H26R$RQvm<}|NsAbdEov4004nWL_t(IjqQ*@62Kq`18G|R|K*OLLR9WOG^ffe z+o;H&&*W~=$D-rI4;Hz4fIA0cJ^_pGA&)YUBsc<{;xiedRIpCR1fn=f!*Eay2N3sO z9YX5}ORF*gexu@=JLHsv`&>iH{cPBG`vhxAtTF?tdO&obe=u3R1D6bhb=uzY1+%0D UXUMdPYXATM07*qoM6N<$g6U*nJOBUy diff --git a/img/misc/locations/sea/water.png b/img/misc/locations/sea/water.png new file mode 100644 index 0000000000000000000000000000000000000000..8513c6f3814bfcbb2216ff526119f164ed5e9cdb GIT binary patch literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|L<4+6T!FND zaff2x8l9D=O}1Xmzw;jm^i~GU07|fz1o;IsfCT>kUs}KHH&Cd;)5S5wqWA3tN1+A- z4(5x`{^ysuENGeb@zudQx72PooLGPUMa9H9Y6&R^-ycrUdvZL>Lm-Fu*&}1Yc^kAO zR;s+=-ola|z^nPORZ-;lk~<R(KJ?8p48P3Lw0XPflC$ZDW^I+eGHHc@Y01}1Gw$@y jJuA24$rb~@@=C_@AJ|Hr*cD2FZeZ|q^>bP0l+XkK>i}fk literal 0 HcmV?d00001 -- GitLab