Adding the ability to add several entry points
We can now have several start layers and choose an entry point using a # in the URL
This commit is contained in:
parent
d630106ff0
commit
db3ef81842
30
README.md
30
README.md
@ -38,16 +38,24 @@ If you want to design your own map, you can use [Tiled](https://www.mapeditor.or
|
|||||||
|
|
||||||
A few things to notice:
|
A few things to notice:
|
||||||
|
|
||||||
- your map can have as many layers as your want
|
- 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.
|
- your map MUST contain a layer named "floorLayer" of type "objectgroup" that represents the layer on which characters will be drawn.
|
||||||
- the tilesets in your map MUST be embedded. You can refer to an external typeset in a TSX file. Click the "embed tileset" button in the tileset tab to embed tileset data.
|
- 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+)
|
- 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
|
- 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.
|
- If you are starting from a blank map, your map MUST be orthogonal and tiles size should be 32x32.
|
||||||
|
|
||||||
![](doc/images/tiled_screenshot_1.png)
|
![](doc/images/tiled_screenshot_1.png)
|
||||||
|
|
||||||
In order to place an on your scene that leads to another scene:
|
### Defining a default entry point
|
||||||
|
|
||||||
|
In order to define a default start position, you MUST create a layer named "start" on your map.
|
||||||
|
This layer MUST contain at least one tile. The players will start on the tile of this layer.
|
||||||
|
If the layer contains many tiles selected, the players will start randomly on one of those tiles.
|
||||||
|
|
||||||
|
### Defining exits
|
||||||
|
|
||||||
|
In order to place an exit on your scene that leads to another scene:
|
||||||
|
|
||||||
- You must create a specific layer. When a character reaches ANY tile of that layer, it will exit the scene.
|
- You must create a specific layer. When a character reaches ANY tile of that layer, it will exit the scene.
|
||||||
- In layer properties, you MUST add "exitSceneUrl" property. It represents the map URL of the next scene. For example : `/<map folder>/<map>.json`. Be careful, if you want the next map to be correctly loaded, you must check that the map files are in folder `back/src/Assets/Maps/<your map folder>`. The files will be accessible by url `<HOST>/map/files/<your map folder>/...`.
|
- In layer properties, you MUST add "exitSceneUrl" property. It represents the map URL of the next scene. For example : `/<map folder>/<map>.json`. Be careful, if you want the next map to be correctly loaded, you must check that the map files are in folder `back/src/Assets/Maps/<your map folder>`. The files will be accessible by url `<HOST>/map/files/<your map folder>/...`.
|
||||||
@ -56,6 +64,22 @@ In order to place an on your scene that leads to another scene:
|
|||||||
|
|
||||||
![](doc/images/exit_layer_map.png)
|
![](doc/images/exit_layer_map.png)
|
||||||
|
|
||||||
|
### Defining several entry points
|
||||||
|
|
||||||
|
Often your map will have several exits, and therefore, several entry points. For instance, if there
|
||||||
|
is an exit by a door that leads to the garden map, when you come back from the garden you expect to
|
||||||
|
come back by the same door. Therefore, a map can have several entry points.
|
||||||
|
Those entry points are "named" (they have a name).
|
||||||
|
|
||||||
|
In order to create a named entry point:
|
||||||
|
|
||||||
|
- You must create a specific layer. When a character enters the map by this entry point, it will enter the map randomly on ANY tile of that layer.
|
||||||
|
- In layer properties, you MUST add a boolean "startLayer" property. It should be set to true.
|
||||||
|
- The name of the entry point is the name of the layer
|
||||||
|
- To enter via this entry point, simply add a hash with the entry point name to the URL ("#[*startLayerName*]"). For instance: "https://workadventu.re/_/global/mymap.com/path/map.json#my-entry-point".
|
||||||
|
- You can of course use the "#" notation in an exit scene URL (so an exit scene URL will point to a given entry scene URL)
|
||||||
|
|
||||||
|
|
||||||
### MacOS developers, your environment with Vagrant
|
### MacOS developers, your environment with Vagrant
|
||||||
|
|
||||||
If you are using MacOS, you can increase Docker performance using Vagrant. If you want more explanations, you can read [this medium article](https://medium.com/better-programming/vagrant-to-increase-docker-performance-with-macos-25b354b0c65c).
|
If you are using MacOS, you can increase Docker performance using Vagrant. If you want more explanations, you can read [this medium article](https://medium.com/better-programming/vagrant-to-increase-docker-performance-with-macos-25b354b0c65c).
|
||||||
|
File diff suppressed because one or more lines are too long
@ -43,7 +43,7 @@
|
|||||||
{
|
{
|
||||||
"name":"exitSceneUrl",
|
"name":"exitSceneUrl",
|
||||||
"type":"string",
|
"type":"string",
|
||||||
"value":"..\/Floor0\/floor0.json"
|
"value":"..\/Floor0\/floor0.json#down-the-stairs"
|
||||||
}],
|
}],
|
||||||
"type":"tilelayer",
|
"type":"tilelayer",
|
||||||
"visible":true,
|
"visible":true,
|
||||||
|
@ -21,8 +21,9 @@ export enum Textures {
|
|||||||
Player = "male1"
|
Player = "male1"
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GameSceneInitInterface {
|
export interface GameSceneInitInterface {
|
||||||
initPosition: PointInterface|null
|
initPosition: PointInterface|null,
|
||||||
|
startLayerName: string|undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GameScene extends Phaser.Scene {
|
export class GameScene extends Phaser.Scene {
|
||||||
@ -36,8 +37,8 @@ export class GameScene extends Phaser.Scene {
|
|||||||
Objects : Array<Phaser.Physics.Arcade.Sprite>;
|
Objects : Array<Phaser.Physics.Arcade.Sprite>;
|
||||||
mapFile: ITiledMap;
|
mapFile: ITiledMap;
|
||||||
groups: Map<string, Sprite>;
|
groups: Map<string, Sprite>;
|
||||||
startX = 704;// 22 case
|
startX: number;
|
||||||
startY = 32; // 1 case
|
startY: number;
|
||||||
circleTexture: CanvasTexture;
|
circleTexture: CanvasTexture;
|
||||||
private initPosition: PositionInterface|null = null;
|
private initPosition: PositionInterface|null = null;
|
||||||
private playersPositionInterpolator = new PlayersPositionInterpolator();
|
private playersPositionInterpolator = new PlayersPositionInterpolator();
|
||||||
@ -57,6 +58,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PositionNextScene: Array<any> = new Array<any>();
|
PositionNextScene: Array<any> = new Array<any>();
|
||||||
|
private startLayerName: string|undefined;
|
||||||
|
|
||||||
static createFromUrl(mapUrlFile: string, instance: string): GameScene {
|
static createFromUrl(mapUrlFile: string, instance: string): GameScene {
|
||||||
let key = GameScene.getMapKeyByUrl(mapUrlFile);
|
let key = GameScene.getMapKeyByUrl(mapUrlFile);
|
||||||
@ -124,6 +126,8 @@ export class GameScene extends Phaser.Scene {
|
|||||||
init(initData : GameSceneInitInterface) {
|
init(initData : GameSceneInitInterface) {
|
||||||
if (initData.initPosition !== undefined) {
|
if (initData.initPosition !== undefined) {
|
||||||
this.initPosition = initData.initPosition;
|
this.initPosition = initData.initPosition;
|
||||||
|
} else if (initData.startLayerName !== undefined) {
|
||||||
|
this.startLayerName = initData.startLayerName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,27 +145,49 @@ export class GameScene extends Phaser.Scene {
|
|||||||
//add layer on map
|
//add layer on map
|
||||||
this.Layers = new Array<Phaser.Tilemaps.StaticTilemapLayer>();
|
this.Layers = new Array<Phaser.Tilemaps.StaticTilemapLayer>();
|
||||||
let depth = -2;
|
let depth = -2;
|
||||||
this.mapFile.layers.forEach((layer : ITiledMapLayer) => {
|
for (let layer of this.mapFile.layers) {
|
||||||
if (layer.type === 'tilelayer') {
|
if (layer.type === 'tilelayer') {
|
||||||
this.addLayer(this.Map.createStaticLayer(layer.name, this.Terrains, 0, 0).setDepth(depth));
|
this.addLayer(this.Map.createStaticLayer(layer.name, this.Terrains, 0, 0).setDepth(depth));
|
||||||
}
|
}
|
||||||
if (layer.type === 'tilelayer' && this.getExitSceneUrl(layer) !== undefined) {
|
if (layer.type === 'tilelayer' && this.getExitSceneUrl(layer) !== undefined) {
|
||||||
this.loadNextGame(layer, this.mapFile.width, this.mapFile.tilewidth, this.mapFile.tileheight);
|
this.loadNextGame(layer, this.mapFile.width, this.mapFile.tilewidth, this.mapFile.tileheight);
|
||||||
}
|
}
|
||||||
if (layer.type === 'tilelayer' && layer.name === "start") {
|
|
||||||
let startPosition = this.startUser(layer);
|
|
||||||
this.startX = startPosition.x;
|
|
||||||
this.startY = startPosition.y;
|
|
||||||
}
|
|
||||||
if (layer.type === 'objectgroup' && layer.name === 'floorLayer') {
|
if (layer.type === 'objectgroup' && layer.name === 'floorLayer') {
|
||||||
depth = 10000;
|
depth = 10000;
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
if (depth === -2) {
|
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.');
|
throw new Error('Your map MUST contain a layer of type "objectgroup" whose name is "floorLayer" that represents the layer characters are drawn at.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now, let's find the start layer
|
||||||
|
if (this.startLayerName) {
|
||||||
|
for (let layer of this.mapFile.layers) {
|
||||||
|
if (this.startLayerName === layer.name && layer.type === 'tilelayer' && this.isStartLayer(layer)) {
|
||||||
|
let startPosition = this.startUser(layer);
|
||||||
|
this.startX = startPosition.x;
|
||||||
|
this.startY = startPosition.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.startX === undefined) {
|
||||||
|
// If we have no start layer specified or if the hash passed does not exist, let's go with the default start position.
|
||||||
|
for (let layer of this.mapFile.layers) {
|
||||||
|
if (layer.type === 'tilelayer' && layer.name === "start") {
|
||||||
|
let startPosition = this.startUser(layer);
|
||||||
|
this.startX = startPosition.x;
|
||||||
|
this.startY = startPosition.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Still no start position? Something is wrong with the map, we need a "start" layer.
|
||||||
|
if (this.startX === undefined) {
|
||||||
|
console.warn('This map is missing a layer named "start" that contains the available default start positions.');
|
||||||
|
// Let's start in the middle of the map
|
||||||
|
this.startX = this.mapFile.width * 16;
|
||||||
|
this.startY = this.mapFile.height * 16;
|
||||||
|
}
|
||||||
|
|
||||||
//add entities
|
//add entities
|
||||||
this.Objects = new Array<Phaser.Physics.Arcade.Sprite>();
|
this.Objects = new Array<Phaser.Physics.Arcade.Sprite>();
|
||||||
|
|
||||||
@ -195,31 +221,30 @@ export class GameScene extends Phaser.Scene {
|
|||||||
// Let's alter browser history
|
// Let's alter browser history
|
||||||
let url = new URL(this.MapUrlFile);
|
let url = new URL(this.MapUrlFile);
|
||||||
let path = '/_/'+this.instance+'/'+url.host+url.pathname;
|
let path = '/_/'+this.instance+'/'+url.host+url.pathname;
|
||||||
if (url.hash) {
|
if (this.startLayerName) {
|
||||||
// FIXME: entry should be dictated by a property passed to init()
|
path += '#'+this.startLayerName;
|
||||||
path += '#'+url.hash;
|
|
||||||
}
|
}
|
||||||
window.history.pushState({}, 'WorkAdventure', path);
|
window.history.pushState({}, 'WorkAdventure', path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getExitSceneUrl(layer: ITiledMapLayer): string|undefined {
|
private getExitSceneUrl(layer: ITiledMapLayer): string|undefined {
|
||||||
let properties : any = layer.properties;
|
return this.getProperty(layer, "exitSceneUrl") as string|undefined;
|
||||||
if (!properties) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
let obj = properties.find((property:any) => property.name === "exitSceneUrl");
|
|
||||||
if (obj === undefined) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return obj.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getExitSceneInstance(layer: ITiledMapLayer): string|undefined {
|
private getExitSceneInstance(layer: ITiledMapLayer): string|undefined {
|
||||||
|
return this.getProperty(layer, "exitInstance") as string|undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isStartLayer(layer: ITiledMapLayer): boolean {
|
||||||
|
return this.getProperty(layer, "startLayer") == true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getProperty(layer: ITiledMapLayer, name: string): string|boolean|number|undefined {
|
||||||
let properties : any = layer.properties;
|
let properties : any = layer.properties;
|
||||||
if (!properties) {
|
if (!properties) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
let obj = properties.find((property:any) => property.name === "exitInstance");
|
let obj = properties.find((property:any) => property.name === name);
|
||||||
if (obj === undefined) {
|
if (obj === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import {ClickButton} from "../Components/ClickButton";
|
|||||||
import Image = Phaser.GameObjects.Image;
|
import Image = Phaser.GameObjects.Image;
|
||||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||||
import {PLAYER_RESOURCES} from "../Entity/Character";
|
import {PLAYER_RESOURCES} from "../Entity/Character";
|
||||||
|
import {GameSceneInitInterface} from "../Game/GameScene";
|
||||||
|
|
||||||
//todo: put this constants in a dedicated file
|
//todo: put this constants in a dedicated file
|
||||||
export const SelectCharacterSceneName = "SelectCharacterScene";
|
export const SelectCharacterSceneName = "SelectCharacterScene";
|
||||||
@ -121,7 +122,9 @@ export class SelectCharacterScene extends Phaser.Scene {
|
|||||||
if (instanceAndMapUrl !== null) {
|
if (instanceAndMapUrl !== null) {
|
||||||
let [mapUrl, instance] = instanceAndMapUrl;
|
let [mapUrl, instance] = instanceAndMapUrl;
|
||||||
let key = gameManager.loadMap(mapUrl, this.scene, instance);
|
let key = gameManager.loadMap(mapUrl, this.scene, instance);
|
||||||
this.scene.start(key);
|
this.scene.start(key, {
|
||||||
|
startLayerName: window.location.hash ? window.location.hash.substr(1) : undefined
|
||||||
|
} as GameSceneInitInterface);
|
||||||
return mapUrl;
|
return mapUrl;
|
||||||
} else {
|
} else {
|
||||||
// If we do not have a map address in the URL, let's ask the server for a start map.
|
// If we do not have a map address in the URL, let's ask the server for a start map.
|
||||||
|
Loading…
Reference in New Issue
Block a user