Adding support for default variables values

This commit is contained in:
David Négrier 2021-07-05 17:25:23 +02:00
parent abd53b6251
commit c30de8c6db
10 changed files with 92 additions and 44 deletions

View File

@ -88,13 +88,11 @@ export type IframeQueryMap = {
getState: { getState: {
query: undefined, query: undefined,
answer: GameStateEvent, answer: GameStateEvent,
callback: () => GameStateEvent|PromiseLike<GameStateEvent>
}, },
getMapData: { getMapData: {
query: undefined, query: undefined,
answer: MapDataEvent, answer: MapDataEvent,
callback: () => MapDataEvent|PromiseLike<GameStateEvent> },
}
} }
export interface IframeQuery<T extends keyof IframeQueryMap> { export interface IframeQuery<T extends keyof IframeQueryMap> {

View File

@ -1,18 +1,12 @@
import {Observable, Subject} from "rxjs"; import {Observable, Subject} from "rxjs";
import { isMapDataEvent } from "../Events/MapDataEvent";
import { EnterLeaveEvent, isEnterLeaveEvent } from "../Events/EnterLeaveEvent"; import { EnterLeaveEvent, isEnterLeaveEvent } from "../Events/EnterLeaveEvent";
import { isGameStateEvent } from "../Events/GameStateEvent";
import {IframeApiContribution, queryWorkadventure, sendToWorkadventure} from "./IframeApiContribution"; import {IframeApiContribution, queryWorkadventure, sendToWorkadventure} from "./IframeApiContribution";
import { apiCallback } from "./registeredCallbacks"; import { apiCallback } from "./registeredCallbacks";
import type {LayerEvent} from "../Events/LayerEvent";
import type {SetPropertyEvent} from "../Events/setPropertyEvent";
import {isSetVariableEvent, SetVariableEvent} from "../Events/SetVariableEvent"; import {isSetVariableEvent, SetVariableEvent} from "../Events/SetVariableEvent";
import type { ITiledMap } from "../../Phaser/Map/ITiledMap"; import type { ITiledMap } from "../../Phaser/Map/ITiledMap";
import type { MapDataEvent } from "../Events/MapDataEvent";
import type { GameStateEvent } from "../Events/GameStateEvent";
const enterStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>(); const enterStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>();
const leaveStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>(); const leaveStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>();
@ -39,6 +33,16 @@ export const setMapURL = (url: string) => {
mapURL = url; mapURL = url;
} }
export const initVariables = (_variables: Map<string, unknown>): void => {
for (const [name, value] of _variables.entries()) {
// In case the user already decided to put values in the variables (before onInit), let's make sure onInit does not override this.
if (!variables.has(name)) {
variables.set(name, value);
}
}
}
setVariableResolvers.subscribe((event) => { setVariableResolvers.subscribe((event) => {
variables.set(event.key, event.value); variables.set(event.key, event.value);
const subject = variableSubscribers.get(event.key); const subject = variableSubscribers.get(event.key);

View File

@ -1,4 +1,4 @@
import type { ITiledMap, ITiledMapLayer, ITiledMapLayerProperty } from "../Map/ITiledMap"; import type { ITiledMap, ITiledMapLayer, ITiledMapProperty } 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";
@ -19,7 +19,7 @@ export class GameMap {
private callbacks = new Map<string, Array<PropertyChangeCallback>>(); private callbacks = new Map<string, Array<PropertyChangeCallback>>();
private tileNameMap = new Map<string, number>(); private tileNameMap = new Map<string, number>();
private tileSetPropertyMap: { [tile_index: number]: Array<ITiledMapLayerProperty> } = {}; private tileSetPropertyMap: { [tile_index: number]: Array<ITiledMapProperty> } = {};
public readonly flatLayers: ITiledMapLayer[]; public readonly flatLayers: ITiledMapLayer[];
public readonly phaserLayers: TilemapLayer[] = []; public readonly phaserLayers: TilemapLayer[] = [];
@ -61,7 +61,7 @@ export class GameMap {
} }
} }
public getPropertiesForIndex(index: number): Array<ITiledMapLayerProperty> { public getPropertiesForIndex(index: number): Array<ITiledMapProperty> {
if (this.tileSetPropertyMap[index]) { if (this.tileSetPropertyMap[index]) {
return this.tileSetPropertyMap[index]; return this.tileSetPropertyMap[index];
} }
@ -151,7 +151,7 @@ export class GameMap {
return this.map; return this.map;
} }
private getTileProperty(index: number): Array<ITiledMapLayerProperty> { private getTileProperty(index: number): Array<ITiledMapProperty> {
return this.tileSetPropertyMap[index]; return this.tileSetPropertyMap[index];
} }

View File

@ -50,7 +50,7 @@ import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectC
import type { import type {
ITiledMap, ITiledMap,
ITiledMapLayer, ITiledMapLayer,
ITiledMapLayerProperty, ITiledMapProperty,
ITiledMapObject, ITiledMapObject,
ITiledTileSet, ITiledTileSet,
} from "../Map/ITiledMap"; } from "../Map/ITiledMap";
@ -1197,12 +1197,12 @@ ${escapedMessage}
} }
private getProperty(layer: ITiledMapLayer | ITiledMap, name: string): string | boolean | number | undefined { private getProperty(layer: ITiledMapLayer | ITiledMap, name: string): string | boolean | number | undefined {
const properties: ITiledMapLayerProperty[] | undefined = layer.properties; const properties: ITiledMapProperty[] | undefined = layer.properties;
if (!properties) { if (!properties) {
return undefined; return undefined;
} }
const obj = properties.find( const obj = properties.find(
(property: ITiledMapLayerProperty) => property.name.toLowerCase() === name.toLowerCase() (property: ITiledMapProperty) => property.name.toLowerCase() === name.toLowerCase()
); );
if (obj === undefined) { if (obj === undefined) {
return undefined; return undefined;
@ -1211,12 +1211,12 @@ ${escapedMessage}
} }
private getProperties(layer: ITiledMapLayer | ITiledMap, name: string): (string | number | boolean | undefined)[] { private getProperties(layer: ITiledMapLayer | ITiledMap, name: string): (string | number | boolean | undefined)[] {
const properties: ITiledMapLayerProperty[] | undefined = layer.properties; const properties: ITiledMapProperty[] | undefined = layer.properties;
if (!properties) { if (!properties) {
return []; return [];
} }
return properties return properties
.filter((property: ITiledMapLayerProperty) => property.name.toLowerCase() === name.toLowerCase()) .filter((property: ITiledMapProperty) => property.name.toLowerCase() === name.toLowerCase())
.map((property) => property.value); .map((property) => property.value);
} }

View File

@ -5,18 +5,28 @@ import type {RoomConnection} from "../../Connexion/RoomConnection";
import {iframeListener} from "../../Api/IframeListener"; import {iframeListener} from "../../Api/IframeListener";
import type {Subscription} from "rxjs"; import type {Subscription} from "rxjs";
import type {GameMap} from "./GameMap"; import type {GameMap} from "./GameMap";
import type {ITiledMapObject} from "../Map/ITiledMap"; import type {ITile, ITiledMapObject} from "../Map/ITiledMap";
import type {Var} from "svelte/types/compiler/interfaces";
interface Variable {
defaultValue: unknown
}
export class SharedVariablesManager { export class SharedVariablesManager {
private _variables = new Map<string, unknown>(); private _variables = new Map<string, unknown>();
private iframeListenerSubscription: Subscription; private iframeListenerSubscription: Subscription;
private variableObjects: Map<string, ITiledMapObject>; private variableObjects: Map<string, Variable>;
constructor(private roomConnection: RoomConnection, private gameMap: GameMap) { constructor(private roomConnection: RoomConnection, private gameMap: GameMap) {
// We initialize the list of variable object at room start. The objects cannot be edited later // We initialize the list of variable object at room start. The objects cannot be edited later
// (otherwise, this would cause a security issue if the scripting API can edit this list of objects) // (otherwise, this would cause a security issue if the scripting API can edit this list of objects)
this.variableObjects = SharedVariablesManager.findVariablesInMap(gameMap); this.variableObjects = SharedVariablesManager.findVariablesInMap(gameMap);
// Let's initialize default values
for (const [name, variableObject] of this.variableObjects.entries()) {
this._variables.set(name, variableObject.defaultValue);
}
// When a variable is modified from an iFrame // When a variable is modified from an iFrame
this.iframeListenerSubscription = iframeListener.setVariableStream.subscribe((event) => { this.iframeListenerSubscription = iframeListener.setVariableStream.subscribe((event) => {
const key = event.key; const key = event.key;
@ -33,14 +43,14 @@ export class SharedVariablesManager {
}); });
} }
private static findVariablesInMap(gameMap: GameMap): Map<string, ITiledMapObject> { private static findVariablesInMap(gameMap: GameMap): Map<string, Variable> {
const objects = new Map<string, ITiledMapObject>(); const objects = new Map<string, Variable>();
for (const layer of gameMap.getMap().layers) { for (const layer of gameMap.getMap().layers) {
if (layer.type === 'objectgroup') { if (layer.type === 'objectgroup') {
for (const object of layer.objects) { for (const object of layer.objects) {
if (object.type === 'variable') { if (object.type === 'variable') {
// We store a copy of the object (to make it immutable) // We store a copy of the object (to make it immutable)
objects.set(object.name, {...object}); objects.set(object.name, this.iTiledObjectToVariable(object));
} }
} }
} }
@ -48,6 +58,21 @@ export class SharedVariablesManager {
return objects; return objects;
} }
private static iTiledObjectToVariable(object: ITiledMapObject): Variable {
const variable: Variable = {
defaultValue: undefined
};
if (object.properties) {
for (const property of object.properties) {
if (property.name === 'default') {
variable.defaultValue = property.value;
}
}
}
return variable;
}
public close(): void { public close(): void {
this.iframeListenerSubscription.unsubscribe(); this.iframeListenerSubscription.unsubscribe();

View File

@ -1,5 +1,5 @@
import type { PositionInterface } from "../../Connexion/ConnexionModels"; import type { PositionInterface } from "../../Connexion/ConnexionModels";
import type { ITiledMap, ITiledMapLayer, ITiledMapLayerProperty, ITiledMapTileLayer } from "../Map/ITiledMap"; import type { ITiledMap, ITiledMapLayer, ITiledMapProperty, ITiledMapTileLayer } from "../Map/ITiledMap";
import type { GameMap } from "./GameMap"; import type { GameMap } from "./GameMap";
const defaultStartLayerName = "start"; const defaultStartLayerName = "start";
@ -112,12 +112,12 @@ export class StartPositionCalculator {
} }
private getProperty(layer: ITiledMapLayer | ITiledMap, name: string): string | boolean | number | undefined { private getProperty(layer: ITiledMapLayer | ITiledMap, name: string): string | boolean | number | undefined {
const properties: ITiledMapLayerProperty[] | undefined = layer.properties; const properties: ITiledMapProperty[] | undefined = layer.properties;
if (!properties) { if (!properties) {
return undefined; return undefined;
} }
const obj = properties.find( const obj = properties.find(
(property: ITiledMapLayerProperty) => property.name.toLowerCase() === name.toLowerCase() (property: ITiledMapProperty) => property.name.toLowerCase() === name.toLowerCase()
); );
if (obj === undefined) { if (obj === undefined) {
return undefined; return undefined;

View File

@ -16,7 +16,7 @@ export interface ITiledMap {
* Map orientation (orthogonal) * Map orientation (orthogonal)
*/ */
orientation: string; orientation: string;
properties?: ITiledMapLayerProperty[]; properties?: ITiledMapProperty[];
/** /**
* Render order (right-down) * Render order (right-down)
@ -33,7 +33,7 @@ export interface ITiledMap {
type?: string; type?: string;
} }
export interface ITiledMapLayerProperty { export interface ITiledMapProperty {
name: string; name: string;
type: string; type: string;
value: string | boolean | number | undefined; value: string | boolean | number | undefined;
@ -51,7 +51,7 @@ export interface ITiledMapGroupLayer {
id?: number; id?: number;
name: string; name: string;
opacity: number; opacity: number;
properties?: ITiledMapLayerProperty[]; properties?: ITiledMapProperty[];
type: "group"; type: "group";
visible: boolean; visible: boolean;
@ -69,7 +69,7 @@ export interface ITiledMapTileLayer {
height: number; height: number;
name: string; name: string;
opacity: number; opacity: number;
properties?: ITiledMapLayerProperty[]; properties?: ITiledMapProperty[];
encoding?: string; encoding?: string;
compression?: string; compression?: string;
@ -91,7 +91,7 @@ export interface ITiledMapObjectLayer {
height: number; height: number;
name: string; name: string;
opacity: number; opacity: number;
properties?: ITiledMapLayerProperty[]; properties?: ITiledMapProperty[];
encoding?: string; encoding?: string;
compression?: string; compression?: string;
@ -117,7 +117,7 @@ export interface ITiledMapObject {
gid: number; gid: number;
height: number; height: number;
name: string; name: string;
properties: { [key: string]: string }; properties?: ITiledMapProperty[];
rotation: number; rotation: number;
type: string; type: string;
visible: boolean; visible: boolean;
@ -163,7 +163,7 @@ export interface ITiledTileSet {
imagewidth: number; imagewidth: number;
margin: number; margin: number;
name: string; name: string;
properties: { [key: string]: string }; properties?: ITiledMapProperty[];
spacing: number; spacing: number;
tilecount: number; tilecount: number;
tileheight: number; tileheight: number;
@ -182,7 +182,7 @@ export interface ITile {
id: number; id: number;
type?: string; type?: string;
properties?: Array<ITiledMapLayerProperty>; properties?: ITiledMapProperty[];
} }
export interface ITiledMapTerrain { export interface ITiledMapTerrain {

View File

@ -11,7 +11,7 @@ import nav from "./Api/iframe/nav";
import controls from "./Api/iframe/controls"; import controls from "./Api/iframe/controls";
import ui from "./Api/iframe/ui"; import ui from "./Api/iframe/ui";
import sound from "./Api/iframe/sound"; import sound from "./Api/iframe/sound";
import room, {setMapURL, setRoomId} from "./Api/iframe/room"; import room, {initVariables, setMapURL, setRoomId} from "./Api/iframe/room";
import player, {setPlayerName, setTags, setUuid} from "./Api/iframe/player"; import player, {setPlayerName, setTags, setUuid} from "./Api/iframe/player";
import type { ButtonDescriptor } from "./Api/iframe/Ui/ButtonDescriptor"; import type { ButtonDescriptor } from "./Api/iframe/Ui/ButtonDescriptor";
import type { Popup } from "./Api/iframe/Ui/Popup"; import type { Popup } from "./Api/iframe/Ui/Popup";
@ -29,6 +29,7 @@ const initPromise = new Promise<void>((resolve) => {
setMapURL(state.mapUrl); setMapURL(state.mapUrl);
setTags(state.tags); setTags(state.tags);
setUuid(state.uuid); setUuid(state.uuid);
initVariables(state.variables as Map<string, unknown>);
resolve(); resolve();
})); }));
}); });

View File

@ -1,11 +1,13 @@
WA.onInit().then(() => {
console.log('Trying to read variable "doorOpened" whose default property is true. This should display "true".');
console.log('doorOpened', WA.room.loadVariable('doorOpened'));
console.log('Trying to set variable "not_exists". This should display an error in the console.') console.log('Trying to set variable "not_exists". This should display an error in the console.')
WA.room.saveVariable('not_exists', 'foo'); WA.room.saveVariable('not_exists', 'foo');
console.log('Trying to set variable "config". This should work.');
WA.room.saveVariable('config', {'foo': 'bar'});
console.log('Trying to read variable "config". This should display a {"foo": "bar"} object.');
console.log(WA.room.loadVariable('config'));
console.log('Trying to set variable "config". This should work.');
WA.room.saveVariable('config', {'foo': 'bar'});
console.log('Trying to read variable "config". This should display a {"foo": "bar"} object.');
console.log(WA.room.loadVariable('config'));
});

View File

@ -72,6 +72,24 @@
"template":"config.tx", "template":"config.tx",
"x":57.5, "x":57.5,
"y":111 "y":111
},
{
"height":0,
"id":6,
"name":"doorOpened",
"point":true,
"properties":[
{
"name":"default",
"type":"bool",
"value":true
}],
"rotation":0,
"type":"variable",
"visible":true,
"width":0,
"x":131.38069962269,
"y":106.004988169086
}], }],
"opacity":1, "opacity":1,
"type":"objectgroup", "type":"objectgroup",
@ -80,7 +98,7 @@
"y":0 "y":0
}], }],
"nextlayerid":8, "nextlayerid":8,
"nextobjectid":6, "nextobjectid":8,
"orientation":"orthogonal", "orientation":"orthogonal",
"properties":[ "properties":[
{ {