diff --git a/CHANGELOG.md b/CHANGELOG.md
index 13335737..560365f2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
- Enabled outlines on actionable item again (they were disabled when migrating to Phaser 3.50) #1218
- Enabled outlines on player names (when the mouse hovers on a player you can interact with) #1219
- Migrated the admin console to Svelte, and redesigned the console #1211
+- Layer properties (like `exitUrl`, `silent`, etc...) can now also used in tile properties #1210 (@jonnytest1)
## Version 1.4.1
diff --git a/docs/maps/wa-maps.md b/docs/maps/wa-maps.md
new file mode 100644
index 00000000..98fdce2d
--- /dev/null
+++ b/docs/maps/wa-maps.md
@@ -0,0 +1,90 @@
+{.section-title.accent.text-primary}
+# About WorkAdventure maps
+
+A WorkAdventure map is a map in "JSON" format generated by [Tiled](https://www.mapeditor.org/).
+
+## Tiles
+
+A map is made of "tiles" (we can also call them "sprites"). In WorkAdventure, the tiles are small images of 32x32 pixels.
+
+Tiles may have transparent parts. Many tiles can be stored in a single PNG file. We call this file a "tileset".
+
+There are many tilesets available on the internet. Some examples of websites offering awesome tiles:
+
+* [itch.io](https://itch.io/)
+* [opengameart.org](https://opengameart.org/)
+* [deviantart.com](https://www.deviantart.com/)
+
+Keep in mind the size of tiles and do not forget to check the license of the tileset you are using!
+
+
+## How to design "pixel" tiles
+
+You can design your own tiles as well as change existing tiles, this is usually referred to as "pixeling". You can start drawing your own tiles with [Piskel](https://www.piskelapp.com/). It is easy to use and well targeted at "pixeling". If you are getting serious about pixeling, the awesome folks at the Chaos Computer Club recommend the use of the editor [Krita](https://krita.org/). There are plenty of other editors as well.
+
+If you are using Krita:
+
+* Please double check that your tiles are 32x32 pixels in size. You can enable a grid under view -> show grid and under settings -> dockers -> grid you can select the grid size.
+* Use transparency if you have to model transitions between different materials. This is more flexible and saves you time by not modeling every transition.
+* You can follow the Pixel-Art Workshop by blinry: [media.ccc.de/v/34C3-jugend-hackt-1016-pixel_art_workshop](https://media.ccc.de/v/34C3-jugend-hackt-1016-pixel_art_workshop)
+
+## WorkAdventure Map Rules
+
+In order to design a map that will be readable by WorkAdventure, you will have to respect some constraints.
+
+In particular, you will need to:
+
+* set a start position for the players
+* configure the "floor layer" (so that WorkAdventure can correctly display characters above the floor, but under the ceiling)
+* eventually, you can place exits that link to other maps
+
+A few things to notice:
+
+* your map can have as many layers as you want
+* your map MUST contain a layer named "floorLayer" of type "objectgroup" that represents the layer on which characters will be drawn. Every layer above the "floorLayer" will be displayed on top of the characters.
+* the tilesets in your map MUST be embedded. You cannot refer to an external typeset in a TSX file. Click the "embed tileset" button in the tileset tab to embed tileset data.
+* your map MUST be exported in JSON format. You need to use a recent version of Tiled to get JSON format export (1.3+)
+* WorkAdventure doesn't support object layers and will ignore them
+* If you are starting from a blank map, your map MUST be orthogonal and tiles size should be 32x32.
+
+
+
+
+
+## Building walls and "collidable" areas
+
+By default, the characters can traverse any tiles. If you want to prevent your characeter from going through a tile (like a wall or a desktop), you must make this tile "collidable". You can do this by settings the `collides` property on a given tile.
+
+To make a tile "collidable", you should:
+
+1. select the relevant tileset and switch to "edit" mode:
+ {.document-img}
+ ![](https://workadventu.re/img/docs/collides-1.png)
+2. right click on a tile of the tileset to select it:
+ {.document-img}
+ ![](https://workadventu.re/img/docs/collides-2.png)
+3. on the left pane in the custom properties section, right click and select "Add properties":
+ {.document-img}
+ ![](https://workadventu.re/img/docs/collides-3.png)
+
+ Please add a `collides` property. The type of the property must be **bool**.
+
+4. finally, check the checkbox for the `collides` property:
+ {.document-img}
+ ![](https://workadventu.re/img/docs/collides-4.png)
+
+Repeat for every tile that should be "collidable".
+
+## Adding behaviour with properties
+
+In the next sections, you will see how you can add behaviour on your map by adding "properties".
+You can add properties for a variety of features: putting exits, opening websites, meeting rooms, silent zones, etc...
+
+You can add properties either on individual tiles of a tileset OR on a complete layer.
+
+If you put a property on a layer, it will be triggered if your Woka walks on any tile of the layer.
+
+The exception is the "collides" property that can only be set on tiles, but not on a complete layer.
diff --git a/front/package.json b/front/package.json
index 74edf9d7..8652ba83 100644
--- a/front/package.json
+++ b/front/package.json
@@ -54,7 +54,8 @@
"standardized-audio-context": "^25.2.4"
},
"scripts": {
- "start": "run-p serve svelte-check-watch",
+ "start": "run-p templater serve svelte-check-watch",
+ "templater": "cross-env ./templater.sh",
"serve": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" webpack serve --open",
"build": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" NODE_ENV=production webpack",
"test": "TS_NODE_PROJECT=\"tsconfig-for-jasmine.json\" ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json",
diff --git a/front/src/Phaser/Game/GameMap.ts b/front/src/Phaser/Game/GameMap.ts
index 7c93a702..ffcee287 100644
--- a/front/src/Phaser/Game/GameMap.ts
+++ b/front/src/Phaser/Game/GameMap.ts
@@ -1,5 +1,5 @@
-import type {ITiledMap, ITiledMapLayer} from "../Map/ITiledMap";
-import {LayersIterator} from "../Map/LayersIterator";
+import type { ITiledMap, ITiledMapLayerProperty } from "../Map/ITiledMap";
+import { LayersIterator } from "../Map/LayersIterator";
export type PropertyChangeCallback = (newValue: string | number | boolean | undefined, oldValue: string | number | boolean | undefined, allProps: Map) => void;
@@ -8,15 +8,34 @@ export type PropertyChangeCallback = (newValue: string | number | boolean | unde
* It is used to handle layer properties.
*/
export class GameMap {
- private key: number|undefined;
- private lastProperties = new Map();
+ private key: number | undefined;
+ private lastProperties = new Map();
private callbacks = new Map>();
+
+ private tileSetPropertyMap: { [tile_index: number]: Array } = {}
public readonly layersIterator: LayersIterator;
+ public exitUrls: Array = []
+
public constructor(private map: ITiledMap) {
this.layersIterator = new LayersIterator(map);
+
+ for (const tileset of map.tilesets) {
+ tileset?.tiles?.forEach(tile => {
+ if (tile.properties) {
+ this.tileSetPropertyMap[tileset.firstgid + tile.id] = tile.properties
+ tile.properties.forEach(prop => {
+ if (prop.name == "exitUrl" && typeof prop.value == "string") {
+ this.exitUrls.push(prop.value);
+ }
+ })
+ }
+ })
+ }
}
+
+
/**
* Sets the position of the current player (in pixels)
* This will trigger events if properties are changing.
@@ -51,21 +70,27 @@ export class GameMap {
}
}
- public getCurrentProperties(): Map {
+ public getCurrentProperties(): Map {
return this.lastProperties;
}
- private getProperties(key: number): Map {
- const properties = new Map();
+ private getProperties(key: number): Map {
+ const properties = new Map();
for (const layer of this.layersIterator) {
if (layer.type !== 'tilelayer') {
continue;
}
- const tiles = layer.data as number[];
- if (tiles[key] == 0) {
- continue;
+
+ let tileIndex: number | undefined = undefined;
+ if (layer.data) {
+ const tiles = layer.data as number[];
+ if (tiles[key] == 0) {
+ continue;
+ }
+ tileIndex = tiles[key]
}
+
// There is a tile in this layer, let's embed the properties
if (layer.properties !== undefined) {
for (const layerProperty of layer.properties) {
@@ -75,6 +100,16 @@ export class GameMap {
properties.set(layerProperty.name, layerProperty.value);
}
}
+
+ if (tileIndex) {
+ this.tileSetPropertyMap[tileIndex]?.forEach(property => {
+ if (property.value) {
+ properties.set(property.name, property.value)
+ } else if (properties.has(property.name)) {
+ properties.delete(property.name)
+ }
+ })
+ }
}
return properties;
diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts
index 37afba64..61f1db2a 100644
--- a/front/src/Phaser/Game/GameScene.ts
+++ b/front/src/Phaser/Game/GameScene.ts
@@ -449,6 +449,11 @@ export class GameScene extends DirtyScene implements CenterListener {
}
}
}
+
+ this.gameMap.exitUrls.forEach(exitUrl => {
+ this.loadNextGame(exitUrl)
+ })
+
if (depth === -2) {
throw new Error('Your map MUST contain a layer of type "objectgroup" whose name is "floorLayer" that represents the layer characters are drawn at. This layer cannot be contained in a group.');
}
diff --git a/front/src/Phaser/Map/ITiledMap.ts b/front/src/Phaser/Map/ITiledMap.ts
index c4828911..91a29397 100644
--- a/front/src/Phaser/Map/ITiledMap.ts
+++ b/front/src/Phaser/Map/ITiledMap.ts
@@ -167,7 +167,7 @@ export interface ITiledTileSet {
tilewidth: number;
transparentcolor: string;
terrains: ITiledMapTerrain[];
- tiles: {[key: string]: { terrain: number[] }};
+ tiles?: Array;
/**
* Refers to external tileset file (should be JSON)
@@ -175,6 +175,13 @@ export interface ITiledTileSet {
source: string;
}
+export interface ITile {
+ id: number,
+ type?: string
+
+ properties?: Array
+}
+
export interface ITiledMapTerrain {
name: string;
tile: number;