Merge branch 'develop' of github.com:thecodingmachine/workadventure into metadataScriptingApi

This commit is contained in:
GRL 2021-06-23 11:40:08 +02:00
commit 2a2cea2cd5
6 changed files with 148 additions and 11 deletions

View File

@ -6,6 +6,7 @@
- Enabled outlines on actionable item again (they were disabled when migrating to Phaser 3.50) #1218 - 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 - 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 - 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)
- New scripting API features : - New scripting API features :
- Use `WA.room.showLayer(): void` to show a layer - Use `WA.room.showLayer(): void` to show a layer
- Use `WA.room.hideLayer(): void` to hide a layer - Use `WA.room.hideLayer(): void` to hide a layer

90
docs/maps/wa-maps.md Normal file
View File

@ -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.
<div>
<figure class="figure">
<img src="https://workadventu.re/img/docs/tiled_screenshot_1.png" class="figure-img img-fluid rounded" alt="" style="width: 70%" />
<figcaption class="figure-caption">"floorLayer" is compulsory</figcaption>
</figure>
</div>
## 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.

View File

@ -54,7 +54,8 @@
"standardized-audio-context": "^25.2.4" "standardized-audio-context": "^25.2.4"
}, },
"scripts": { "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", "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", "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", "test": "TS_NODE_PROJECT=\"tsconfig-for-jasmine.json\" ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json",

View File

@ -1,4 +1,4 @@
import type { ITiledMap, ITiledMapLayer } from "../Map/ITiledMap"; import type {ITiledMap, ITiledMapLayer, ITiledMapLayerProperty} from "../Map/ITiledMap";
import { flattenGroupLayersMap } from "../Map/LayersFlattener"; import { flattenGroupLayersMap } from "../Map/LayersFlattener";
import TilemapLayer = Phaser.Tilemaps.TilemapLayer; import TilemapLayer = Phaser.Tilemaps.TilemapLayer;
import { DEPTH_OVERLAY_INDEX } from "./DepthIndexes"; import { DEPTH_OVERLAY_INDEX } from "./DepthIndexes";
@ -10,12 +10,16 @@ export type PropertyChangeCallback = (newValue: string | number | boolean | unde
* It is used to handle layer properties. * It is used to handle layer properties.
*/ */
export class GameMap { export class GameMap {
private key: number|undefined; private key: number | undefined;
private lastProperties = new Map<string, string|boolean|number>(); private lastProperties = new Map<string, string | boolean | number>();
private callbacks = new Map<string, Array<PropertyChangeCallback>>(); private callbacks = new Map<string, Array<PropertyChangeCallback>>();
private tileSetPropertyMap: { [tile_index: number]: Array<ITiledMapLayerProperty> } = {}
public readonly flatLayers: ITiledMapLayer[]; public readonly flatLayers: ITiledMapLayer[];
public readonly phaserLayers: TilemapLayer[] = []; public readonly phaserLayers: TilemapLayer[] = [];
public exitUrls: Array<string> = []
public constructor(private map: ITiledMap, phaserMap: Phaser.Tilemaps.Tilemap, terrains: Array<Phaser.Tilemaps.Tileset>) { public constructor(private map: ITiledMap, phaserMap: Phaser.Tilemaps.Tilemap, terrains: Array<Phaser.Tilemaps.Tileset>) {
this.flatLayers = flattenGroupLayersMap(map); this.flatLayers = flattenGroupLayersMap(map);
let depth = -2; let depth = -2;
@ -27,7 +31,21 @@ export class GameMap {
depth = DEPTH_OVERLAY_INDEX; depth = DEPTH_OVERLAY_INDEX;
} }
} }
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) * Sets the position of the current player (in pixels)
@ -63,21 +81,27 @@ export class GameMap {
} }
} }
public getCurrentProperties(): Map<string, string|boolean|number> { public getCurrentProperties(): Map<string, string | boolean | number> {
return this.lastProperties; return this.lastProperties;
} }
private getProperties(key: number): Map<string, string|boolean|number> { private getProperties(key: number): Map<string, string | boolean | number> {
const properties = new Map<string, string|boolean|number>(); const properties = new Map<string, string | boolean | number>();
for (const layer of this.flatLayers) { for (const layer of this.flatLayers) {
if (layer.type !== 'tilelayer') { if (layer.type !== 'tilelayer') {
continue; continue;
} }
let tileIndex: number | undefined = undefined;
if (layer.data) {
const tiles = layer.data as number[]; const tiles = layer.data as number[];
if (tiles[key] == 0) { if (tiles[key] == 0) {
continue; continue;
} }
tileIndex = tiles[key]
}
// There is a tile in this layer, let's embed the properties // There is a tile in this layer, let's embed the properties
if (layer.properties !== undefined) { if (layer.properties !== undefined) {
for (const layerProperty of layer.properties) { for (const layerProperty of layer.properties) {
@ -87,6 +111,16 @@ export class GameMap {
properties.set(layerProperty.name, layerProperty.value); 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; return properties;

View File

@ -441,6 +441,10 @@ export class GameScene extends DirtyScene implements CenterListener {
} }
} }
this.gameMap.exitUrls.forEach(exitUrl => {
this.loadNextGame(exitUrl)
})
this.initStartXAndStartY(); this.initStartXAndStartY();
//add entities //add entities

View File

@ -170,7 +170,7 @@ export interface ITiledTileSet {
tilewidth: number; tilewidth: number;
transparentcolor: string; transparentcolor: string;
terrains: ITiledMapTerrain[]; terrains: ITiledMapTerrain[];
tiles: {[key: string]: { terrain: number[] }}; tiles?: Array<ITile>;
/** /**
* Refers to external tileset file (should be JSON) * Refers to external tileset file (should be JSON)
@ -178,6 +178,13 @@ export interface ITiledTileSet {
source: string; source: string;
} }
export interface ITile {
id: number,
type?: string
properties?: Array<ITiledMapLayerProperty>
}
export interface ITiledMapTerrain { export interface ITiledMapTerrain {
name: string; name: string;
tile: number; tile: number;