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: {
query: undefined,
answer: GameStateEvent,
callback: () => GameStateEvent|PromiseLike<GameStateEvent>
},
getMapData: {
query: undefined,
answer: MapDataEvent,
callback: () => MapDataEvent|PromiseLike<GameStateEvent>
}
},
}
export interface IframeQuery<T extends keyof IframeQueryMap> {

View file

@ -1,18 +1,12 @@
import {Observable, Subject} from "rxjs";
import { isMapDataEvent } from "../Events/MapDataEvent";
import { EnterLeaveEvent, isEnterLeaveEvent } from "../Events/EnterLeaveEvent";
import { isGameStateEvent } from "../Events/GameStateEvent";
import {IframeApiContribution, queryWorkadventure, sendToWorkadventure} from "./IframeApiContribution";
import { apiCallback } from "./registeredCallbacks";
import type {LayerEvent} from "../Events/LayerEvent";
import type {SetPropertyEvent} from "../Events/setPropertyEvent";
import {isSetVariableEvent, SetVariableEvent} from "../Events/SetVariableEvent";
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 leaveStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>();
@ -39,6 +33,16 @@ export const setMapURL = (url: string) => {
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) => {
variables.set(event.key, event.value);
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 TilemapLayer = Phaser.Tilemaps.TilemapLayer;
import { DEPTH_OVERLAY_INDEX } from "./DepthIndexes";
@ -19,7 +19,7 @@ export class GameMap {
private callbacks = new Map<string, Array<PropertyChangeCallback>>();
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 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]) {
return this.tileSetPropertyMap[index];
}
@ -151,7 +151,7 @@ export class GameMap {
return this.map;
}
private getTileProperty(index: number): Array<ITiledMapLayerProperty> {
private getTileProperty(index: number): Array<ITiledMapProperty> {
return this.tileSetPropertyMap[index];
}

View file

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

View file

@ -5,18 +5,28 @@ import type {RoomConnection} from "../../Connexion/RoomConnection";
import {iframeListener} from "../../Api/IframeListener";
import type {Subscription} from "rxjs";
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 {
private _variables = new Map<string, unknown>();
private iframeListenerSubscription: Subscription;
private variableObjects: Map<string, ITiledMapObject>;
private variableObjects: Map<string, Variable>;
constructor(private roomConnection: RoomConnection, private gameMap: GameMap) {
// 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)
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
this.iframeListenerSubscription = iframeListener.setVariableStream.subscribe((event) => {
const key = event.key;
@ -33,14 +43,14 @@ export class SharedVariablesManager {
});
}
private static findVariablesInMap(gameMap: GameMap): Map<string, ITiledMapObject> {
const objects = new Map<string, ITiledMapObject>();
private static findVariablesInMap(gameMap: GameMap): Map<string, Variable> {
const objects = new Map<string, Variable>();
for (const layer of gameMap.getMap().layers) {
if (layer.type === 'objectgroup') {
for (const object of layer.objects) {
if (object.type === 'variable') {
// 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;
}
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 {
this.iframeListenerSubscription.unsubscribe();

View file

@ -1,5 +1,5 @@
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";
const defaultStartLayerName = "start";
@ -112,12 +112,12 @@ export class StartPositionCalculator {
}
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) {
return undefined;
}
const obj = properties.find(
(property: ITiledMapLayerProperty) => property.name.toLowerCase() === name.toLowerCase()
(property: ITiledMapProperty) => property.name.toLowerCase() === name.toLowerCase()
);
if (obj === undefined) {
return undefined;

View file

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

View file

@ -11,7 +11,7 @@ import nav from "./Api/iframe/nav";
import controls from "./Api/iframe/controls";
import ui from "./Api/iframe/ui";
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 type { ButtonDescriptor } from "./Api/iframe/Ui/ButtonDescriptor";
import type { Popup } from "./Api/iframe/Ui/Popup";
@ -29,6 +29,7 @@ const initPromise = new Promise<void>((resolve) => {
setMapURL(state.mapUrl);
setTags(state.tags);
setUuid(state.uuid);
initVariables(state.variables as Map<string, unknown>);
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.')
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 "not_exists". This should display an error in the console.')
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'));
});

View file

@ -72,6 +72,24 @@
"template":"config.tx",
"x":57.5,
"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,
"type":"objectgroup",
@ -80,7 +98,7 @@
"y":0
}],
"nextlayerid":8,
"nextobjectid":6,
"nextobjectid":8,
"orientation":"orthogonal",
"properties":[
{