New rain and thunder effects added

This commit is contained in:
Mal 2024-12-14 12:09:49 +01:00
parent a583005814
commit f6ebd8f3aa
14 changed files with 183 additions and 11 deletions

View File

@ -74,7 +74,7 @@ export class Game
for (const effect of this.level.fullscreenEffects) { for (const effect of this.level.fullscreenEffects) {
effect.update(timestamp); effect.update(timestamp);
effect.render(this.context); effect.render(this.context, this.camera);
} }
this.userInterface.draw(this.context); this.userInterface.draw(this.context);
@ -238,6 +238,10 @@ export class Game
this.isPaused = false; this.isPaused = false;
for (const effect of this.level.fullscreenEffects) {
effect.init();
}
window.requestAnimationFrame(loopFunction); window.requestAnimationFrame(loopFunction);
} }

View File

@ -7,11 +7,15 @@ export class FullscreenEffect
this.canvas = canvas; this.canvas = canvas;
} }
init()
{
}
update(timestamp) update(timestamp)
{ {
} }
render(context) render(context, camera)
{ {
} }

View File

@ -1,9 +1,13 @@
import {SnowEffect} from "./SnowEffect.js"; import {SnowEffect} from "./SnowEffect.js";
import {RainEffect} from "./RainEffect.js";
import {ThunderstormEffect} from "./ThunderstormEffect.js";
export class FullscreenEffectFactory export class FullscreenEffectFactory
{ {
static EFFECTS = { static EFFECTS = {
[SnowEffect.NAME]: SnowEffect, [SnowEffect.NAME]: SnowEffect,
[RainEffect.NAME]: RainEffect,
[ThunderstormEffect.NAME]: ThunderstormEffect,
} }
getEffect(name) getEffect(name)
@ -16,7 +20,6 @@ export class FullscreenEffectFactory
const names = []; const names = [];
for (const name in FullscreenEffectFactory.EFFECTS) { for (const name in FullscreenEffectFactory.EFFECTS) {
console.log(name);
names.push(name); names.push(name);
} }

51
js/effects/RainEffect.js Normal file
View File

@ -0,0 +1,51 @@
import {FullscreenEffect} from "./FullscreenEffect.js";
export class RainEffect extends FullscreenEffect
{
static NAME = 'rain';
constructor()
{
super();
this.image = new Image();
this.image.src = 'js/effects/rain.png';
this.sound = new Audio('js/effects/rain.mp3');
this.sound.loop = true;
this.offset = 0;
this.speed = 1.3;
}
init()
{
super.init();
this.sound.play();
}
update(timestamp)
{
super.update(timestamp);
this.offsetX = (timestamp * 0.65 * this.speed) % this.image.width;
this.offsetY = (timestamp * 1.5 * this.speed) % this.image.height;
}
render(context, camera)
{
super.render(context, camera);
const cameraX = -1 * (camera.position.x % this.image.width);
const cameraY = -1 * (camera.position.y % this.image.height);
for (let y = -1; y < Math.ceil(context.canvas.height / this.image.height) + 1; y++) {
for (let x = -1; x < Math.ceil(context.canvas.width / this.image.width) + 1; x++) {
context.drawImage(
this.image,
cameraX + this.offsetX + this.image.width * x,
cameraY + this.offsetY + this.image.height * y,
);
}
}
}
}

View File

@ -7,24 +7,44 @@ export class SnowEffect extends FullscreenEffect
constructor() constructor()
{ {
super(); super();
this.audio = new Audio('js/effects/storm.mp3');
this.audio.loop = true;
this.image = new Image(); this.image = new Image();
this.image.src = 'js/effects/snow.png'; this.image.src = 'js/effects/snow.png';
this.offset = 0; this.offset = 0;
this.speed = 1.0;
}
init()
{
super.init();
this.audio.play();
} }
update(timestamp) update(timestamp)
{ {
super.update(timestamp); super.update(timestamp);
this.offset = timestamp % 512;
this.offsetX = (timestamp * 0.65 * this.speed) % this.image.width;
this.offsetY = (timestamp * 1.5 * this.speed) % this.image.height;
} }
render(context) render(context, camera)
{ {
super.render(context); super.render(context);
for (let y = -1; y < Math.ceil(context.canvas.height / 512); y++) { const cameraX = -1 * (camera.position.x % this.image.width);
for (let x = -1; x < Math.ceil(context.canvas.width / 512); x++) { const cameraY = -1 * (camera.position.y % this.image.height);
context.drawImage(this.image, this.offset + 512 * x, this.offset + 512 * y);
for (let y = -1; y < Math.ceil(context.canvas.height / this.image.height) + 1; y++) {
for (let x = -1; x < Math.ceil(context.canvas.width / this.image.width) + 1; x++) {
context.drawImage(
this.image,
cameraX + this.offsetX + this.image.width * x,
cameraY + this.offsetY + this.image.height * y,
);
} }
} }
} }

View File

@ -0,0 +1,88 @@
import {FullscreenEffect} from "./FullscreenEffect.js";
export class ThunderstormEffect extends FullscreenEffect
{
static NAME = 'thunderstorm';
constructor()
{
super();
this.currentAlpha = 0.0;
this.nextFlash = undefined;
this.duration = 150;
this.sounds = [
new Audio('js/effects/thunder01.mp3'),
new Audio('js/effects/thunder02.mp3'),
new Audio('js/effects/thunder03.mp3'),
new Audio('js/effects/thunder04.mp3'),
];
this.currentSound = 0;
}
shuffleSounds(iterations = 10)
{
const buffer = this.sounds.slice(0, this.sounds.length - 1);
for (let i = 0; i < iterations; i++) {
buffer.sort(
(a, b) => {
return 1 - Math.round(Math.random()) * 2
}
);
}
const index = 1 + Math.round(Math.random() * (buffer.length - 1));
this.sounds = buffer.slice(0, index)
.concat([this.sounds[this.sounds.length - 1]])
.concat(buffer.slice(index));
}
update(timestamp)
{
super.update(timestamp);
if (this.nextFlash > timestamp) {
this.currentAlpha = 0.0;
return;
}
if (timestamp < this.nextFlash + this.duration) {
const progress = (timestamp - this.nextFlash) / this.duration;
this.currentAlpha = Math.sin(Math.PI * progress) * 0.75;
} else {
const duration = 3000 + Math.round(Math.random() * 27000);
this.nextFlash = timestamp + duration;
setTimeout(
() => {
if (this.currentSound === 0) {
this.shuffleSounds();
}
this.sounds[this.currentSound].currentTime = 0;
this.sounds[this.currentSound].play();
this.currentSound = (this.currentSound + 1) % this.sounds.length;
},
duration + 1000
);
}
}
render(context, camera)
{
super.render(context, camera);
if (this.currentAlpha === 0.0) {
return;
}
context.fillStyle = 'rgba(255, 255, 255, ' + this.currentAlpha + ')';
context.fillRect(0, 0, context.canvas.width, context.canvas.height);
}
}

BIN
js/effects/rain.mp3 Normal file

Binary file not shown.

BIN
js/effects/rain.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
js/effects/storm.mp3 Normal file

Binary file not shown.

BIN
js/effects/thunder01.mp3 Normal file

Binary file not shown.

BIN
js/effects/thunder02.mp3 Normal file

Binary file not shown.

BIN
js/effects/thunder03.mp3 Normal file

Binary file not shown.

BIN
js/effects/thunder04.mp3 Normal file

Binary file not shown.

View File

@ -30,12 +30,16 @@ import TilorswiftSavedEvent from "./events/TilorswiftSavedEvent.js";
import Level from "../../js/Level.js"; import Level from "../../js/Level.js";
import {IntelligentBrushSwitch} from "./menu/IntelligentBrushSwitch.js"; import {IntelligentBrushSwitch} from "./menu/IntelligentBrushSwitch.js";
import {SnowEffect} from "../../js/effects/SnowEffect.js"; import {SnowEffect} from "../../js/effects/SnowEffect.js";
import {RainEffect} from "../../js/effects/RainEffect.js";
import {ThunderstormEffect} from "../../js/effects/ThunderstormEffect.js";
import {FullscreenEffectFactory} from "../../js/effects/FullscreenEffectFactory.js"; import {FullscreenEffectFactory} from "../../js/effects/FullscreenEffectFactory.js";
export default class Tilorswift export default class Tilorswift
{ {
static EFFECT_NAMES = { static EFFECT_NAMES = {
[SnowEffect.NAME]: 'Schnee', [SnowEffect.NAME]: 'Schnee',
[RainEffect.NAME]: 'Regen',
[ThunderstormEffect.NAME]: 'Gewitter',
} }
constructor(level) { constructor(level) {
@ -320,9 +324,7 @@ export default class Tilorswift
new DialogEffects( new DialogEffects(
FullscreenEffectFactory.getNames(), FullscreenEffectFactory.getNames(),
effects, effects,
{ Tilorswift.EFFECT_NAMES,
[SnowEffect.NAME]: 'Schnee',
}
); );
} }
); );