Skip to content

Commit

Permalink
Add support for parallax scrolling backgrounds in web client
Browse files Browse the repository at this point in the history
  • Loading branch information
AntumDeluge committed Jun 6, 2024
1 parent 13f136a commit 9257fe4
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 0 deletions.
4 changes: 4 additions & 0 deletions buildtools/ant_modules/dist.xml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@
<exclude name="logic/layer/"/>
</fileset>
</copy>
<mkdir dir="${maps}/parallax"/> <!-- in case no parallax images available -->
<copy todir="${build_client_data}/data/maps/parallax">
<fileset dir="${maps}/parallax" includes="**/*.png"/>
</copy>
<copy todir="${build_client_data}/data/font">
<fileset dir="data/font"/>
</copy>
Expand Down
1 change: 1 addition & 0 deletions doc/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Changelog
*web client*
- added visuals tab to settings dialog
- animated fog
- added support for parallax scrolling backgrounds


1.47
Expand Down
3 changes: 3 additions & 0 deletions src/js/stendhal/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,9 @@ export class Client {
stendhal.sound.playSingleGlobalizedMusic(zoneinfo["music"],
!Number.isNaN(musicVolume) ? musicVolume : 1.0);

// parallax background
stendhal.data.map.setParallax(zoneinfo["parallax"]);

// coloring information
if (zoneinfo["color"] && stendhal.config.getBoolean("effect.lighting")) {
if (zoneinfo["color_method"]) {
Expand Down
23 changes: 23 additions & 0 deletions src/js/stendhal/data/Map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Paths } from "./Paths";

import { LandscapeRenderingStrategy, CombinedTilesetRenderingStrategy } from "../landscape/LandscapeRenderingStrategy";
import { IndividualTilesetRenderingStrategy } from "../landscape/IndividualTilesetRenderingStrategy";
import { ParallaxBackground } from "../landscape/ParallaxBackground";


export class Map {
Expand Down Expand Up @@ -63,6 +64,9 @@ export class Map {
Paths.tileset + "/item/blood/small_stains"
];

private parallax: ParallaxBackground;
private parallaxImage?: string;

/** Singleton instance. */
private static instance: Map;

Expand All @@ -86,6 +90,7 @@ export class Map {
} else {
this.strategy = new CombinedTilesetRenderingStrategy();
}
this.parallax = ParallaxBackground.get();
}

/**
Expand Down Expand Up @@ -122,6 +127,11 @@ export class Map {
this.layerGroupIndexes = this.mapLayerGroup();

this.strategy.onMapLoaded(this);

if (this.parallaxImage) {
this.parallax.setImage(this.parallaxImage, this.zoneSizeX * this.tileWidth,
this.zoneSizeY * this.tileHeight);
}
}

decodeTileset(content: any, name: string) {
Expand Down Expand Up @@ -222,4 +232,17 @@ export class Map {
hasSafeTileset(filename: string) {
return this.knownSafeTilesets.indexOf(filename) > -1;
}

/**
* Sets or unsets parallax image.
*
* @param {string=} name
* Background image filename.
*/
setParallax(name?: string) {
this.parallaxImage = name;
if (typeof(name) === "undefined") {
this.parallax.reset();
}
}
}
1 change: 1 addition & 0 deletions src/js/stendhal/data/Paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class Paths {
public static readonly achievements = Paths.sprites + "/achievements";
public static readonly maps = Paths.data + "/maps";
public static readonly tileset = Paths.maps + "/tileset";
public static readonly parallax = Paths.maps + "/parallax";
public static readonly ws = Paths.extractPath("data-ws");

/**
Expand Down
98 changes: 98 additions & 0 deletions src/js/stendhal/landscape/ParallaxBackground.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/***************************************************************************
* Copyright © 2024 - Faiumoni e. V. *
***************************************************************************
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation; either version 3 of the *
* License, or (at your option) any later version. *
* *
***************************************************************************/

import { singletons } from "../SingletonRepo";


export class ParallaxBackground {

/** Default scrol rate of parallax background (1/4). */
public static readonly SCROLL = 0.25;

/** Tiled image to be drawn. */
private image?: HTMLImageElement;

/** Singleton instance. */
private static instance: ParallaxBackground;


/**
* Retrieves singleton instance.
*/
static get(): ParallaxBackground {
if (!ParallaxBackground.instance) {
ParallaxBackground.instance = new ParallaxBackground();
}
return ParallaxBackground.instance;
}

/**
* Hidden singleton constructor.
*/
private constructor() {
// do nothing
}

/**
* Sets image to be drawn.
*
* @param {string} name
* Relative path (exluding .png filename suffix) to image inside "data/maps/parallax" directory
* or `undefined` to unset.
* @param {number} width
* Map pixel width.
* @param {number} height
* Map pixel height.
*/
setImage(name: string, width: number, height: number) {
const fullPath = singletons.getPaths().parallax + "/" + name + ".png";
this.image = singletons.getSpriteStore().get(fullPath);

/* FIXME:
//this.image = singletons.getTileStore().getParallax(name, ParallaxBackground.SCROLL, width, height);
singletons.getTileStore().getParallaxPromise(name, ParallaxBackground.SCROLL, width, height)
.then(image => {
this.image = image;
}).catch(error => {
console.error("Error setting parallax background \"" + name + "\"\n", error);
});
*/
}

/**
* Unsets parallax background image.
*/
reset() {
this.image = undefined;
}

draw(ctx: CanvasRenderingContext2D, offsetX: number, offsetY: number) {
if (!this.image || !this.image.height) {
return;
}

// FIXME: seams are visible when walking
let dy = offsetY - ((offsetY / 4) % this.image.height);
for (dy; dy < this.image.height * 100; dy += this.image.height) {
let dx = offsetX - ((offsetX / 4) % this.image.width);
for (dx; dx < this.image.width * 100; dx += this.image.width) {
ctx.drawImage(this.image, dx, dy);
}
}

/* TODO: use the following when `setImage` fixed
const tileLeft = offsetX - (offsetX * ParallaxBackground.SCROLL);
const tileTop = offsetY - (offsetY * ParallaxBackground.SCROLL);
ctx.drawImage(this.image, tileLeft, tileTop);
*/
}
}
1 change: 1 addition & 0 deletions src/js/stendhal/ui/ViewPort.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export class ViewPort {

// FIXME: filter should not be applied to "blend" layers
//this.applyFilter();
stendhal.data.map.parallax.draw(this.ctx, this.offsetX, this.offsetY);
stendhal.data.map.strategy.render(this.ctx.canvas, this, tileOffsetX, tileOffsetY, this.targetTileWidth, this.targetTileHeight);

this.weatherRenderer.draw(this.ctx);
Expand Down

0 comments on commit 9257fe4

Please sign in to comment.