Create GameMapProperties index
This commit is contained in:
parent
ec3120cf8f
commit
5a3d510f05
@ -1,6 +1,7 @@
|
||||
import type { ITiledMapObject } from "../Map/ITiledMap";
|
||||
import type { GameScene } from "../Game/GameScene";
|
||||
import { type } from "os";
|
||||
import { GameMapProperties } from "../Game/GameMapProperties";
|
||||
|
||||
export class TextUtils {
|
||||
public static createTextFromITiledMapObject(scene: GameScene, object: ITiledMapObject): void {
|
||||
@ -32,7 +33,7 @@ export class TextUtils {
|
||||
}
|
||||
if (object.properties !== undefined) {
|
||||
for (const property of object.properties) {
|
||||
if (property.name === "font-family" && typeof property.value === "string") {
|
||||
if (property.name === GameMapProperties.FONT_FAMILY && typeof property.value === "string") {
|
||||
options.fontFamily = property.value;
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import type { ITiledMap, ITiledMapLayer, ITiledMapProperty } from "../Map/ITiled
|
||||
import { flattenGroupLayersMap } from "../Map/LayersFlattener";
|
||||
import TilemapLayer = Phaser.Tilemaps.TilemapLayer;
|
||||
import { DEPTH_OVERLAY_INDEX } from "./DepthIndexes";
|
||||
import { iframeListener } from "../../Api/IframeListener";
|
||||
import { GameMapProperties } from "./GameMapProperties";
|
||||
|
||||
export type PropertyChangeCallback = (
|
||||
newValue: string | number | boolean | undefined,
|
||||
@ -59,12 +59,12 @@ export class GameMap {
|
||||
if (tile.properties) {
|
||||
this.tileSetPropertyMap[tileset.firstgid + tile.id] = tile.properties;
|
||||
tile.properties.forEach((prop) => {
|
||||
if (prop.name == "name" && typeof prop.value == "string") {
|
||||
if (prop.name == GameMapProperties.NAME && typeof prop.value == "string") {
|
||||
this.tileNameMap.set(prop.value, tileset.firstgid + tile.id);
|
||||
}
|
||||
if (prop.name == "exitUrl" && typeof prop.value == "string") {
|
||||
if (prop.name == GameMapProperties.EXIT_URL && typeof prop.value == "string") {
|
||||
this.exitUrls.push(prop.value);
|
||||
} else if (prop.name == "start") {
|
||||
} else if (prop.name == GameMapProperties.START) {
|
||||
this.hasStartTile = true;
|
||||
}
|
||||
});
|
||||
@ -304,7 +304,7 @@ export class GameMap {
|
||||
this.putTileInFlatLayer(tileIndex, x, y, layer);
|
||||
const phaserTile = phaserLayer.putTileAt(tileIndex, x, y);
|
||||
for (const property of this.getTileProperty(tileIndex)) {
|
||||
if (property.name === "collides" && property.value) {
|
||||
if (property.name === GameMapProperties.COLLIDES && property.value) {
|
||||
phaserTile.setCollision(true);
|
||||
}
|
||||
}
|
||||
|
37
front/src/Phaser/Game/GameMapProperties.ts
Normal file
37
front/src/Phaser/Game/GameMapProperties.ts
Normal file
@ -0,0 +1,37 @@
|
||||
export enum GameMapProperties {
|
||||
ALLOW_API = 'allowApi',
|
||||
AUDIO_LOOP = 'audioLoop',
|
||||
AUDIO_VOLUME = 'audioVolume',
|
||||
COLLIDES = 'collides',
|
||||
DEFAULT = 'default',
|
||||
EXIT_URL = 'exitUrl',
|
||||
EXIT_SCENE_URL = 'exitSceneUrl',
|
||||
FONT_FAMILY = 'font-family',
|
||||
JITSI_ADMIN_ROOM_TAG = 'jitsiRoomAdminTag',
|
||||
JITSI_CONFIG = 'jitsiConfig',
|
||||
JITSI_INTERFACE_CONFIG = 'jitsiInterfaceConfig',
|
||||
JITSI_ROOM = 'jitsiRoom',
|
||||
JITSI_TRIGGER = 'jitsiTrigger',
|
||||
JITSI_TRIGGER_MESSAGE = 'jitsiTriggerMessage',
|
||||
JITSI_URL = 'jitsiUrl',
|
||||
JITSI_WIDTH = 'jitsiWidth',
|
||||
NAME = 'name',
|
||||
OPEN_TAB = 'openTab',
|
||||
OPEN_WEBSITE = 'openWebsite',
|
||||
OPEN_WEBSITE_ALLOW_API = 'openWebsiteAllowApi',
|
||||
OPEN_WEBSITE_POLICY = 'openWebsitePolicy',
|
||||
OPEN_WEBSITE_WIDTH = 'openWebsiteWidth',
|
||||
OPEN_WEBSITE_POSITION = 'openWebsitePosition',
|
||||
OPEN_WEBSITE_TRIGGER = 'openWebsiteTrigger',
|
||||
OPEN_WEBSITE_TRIGGER_MESSAGE = 'openWebsiteTriggerMessage',
|
||||
PLAY_AUDIO = 'playAudio',
|
||||
PLAY_AUDIO_LOOP = 'playAudioLoop',
|
||||
READABLE_BY = 'readableBy',
|
||||
SCRIPT = 'script',
|
||||
SILENT = 'silent',
|
||||
START = 'start',
|
||||
START_LAYER = 'startLayer',
|
||||
URL = 'url',
|
||||
WRITABLE_BY = 'writableBy',
|
||||
ZONE = 'zone',
|
||||
}
|
@ -7,10 +7,9 @@ import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
|
||||
import { get } from 'svelte/store';
|
||||
import {
|
||||
ON_ACTION_TRIGGER_BUTTON,
|
||||
TRIGGER_WEBSITE_PROPERTIES,
|
||||
WEBSITE_MESSAGE_PROPERTIES,
|
||||
} from "../../WebRtc/LayoutManager";
|
||||
import type { ITiledMapLayer } from "../Map/ITiledMap";
|
||||
import { GameMapProperties } from "./GameMapProperties";
|
||||
|
||||
enum OpenCoWebsiteState {
|
||||
LOADING,
|
||||
@ -30,14 +29,14 @@ export class GameMapPropertiesListener {
|
||||
constructor(private scene: GameScene, private gameMap: GameMap) {}
|
||||
|
||||
register() {
|
||||
this.gameMap.onPropertyChange("openTab", (newValue, oldvalue, allProps) => {
|
||||
this.gameMap.onPropertyChange(GameMapProperties.OPEN_TAB, (newValue, oldvalue, allProps) => {
|
||||
if (newValue === undefined) {
|
||||
layoutManagerActionStore.removeAction("openTab");
|
||||
}
|
||||
if (typeof newValue == "string" && newValue.length) {
|
||||
const openWebsiteTriggerValue = allProps.get(TRIGGER_WEBSITE_PROPERTIES);
|
||||
const openWebsiteTriggerValue = allProps.get(GameMapProperties.OPEN_WEBSITE_TRIGGER);
|
||||
if (openWebsiteTriggerValue && openWebsiteTriggerValue === ON_ACTION_TRIGGER_BUTTON) {
|
||||
let message = allProps.get(WEBSITE_MESSAGE_PROPERTIES);
|
||||
let message = allProps.get(GameMapProperties.OPEN_WEBSITE_TRIGGER_MESSAGE);
|
||||
if (message === undefined) {
|
||||
message = "Press SPACE or touch here to open web site in new tab";
|
||||
}
|
||||
@ -72,25 +71,25 @@ export class GameMapPropertiesListener {
|
||||
|
||||
layer.properties.forEach(property => {
|
||||
switch(property.name) {
|
||||
case 'openWebsite':
|
||||
case GameMapProperties.OPEN_WEBSITE:
|
||||
openWebsiteProperty = property.value as string | undefined;
|
||||
break;
|
||||
case 'openWebsiteAllowApi':
|
||||
case GameMapProperties.OPEN_WEBSITE_ALLOW_API:
|
||||
allowApiProperty = property.value as boolean | undefined;
|
||||
break;
|
||||
case 'openWebsitePolicy':
|
||||
case GameMapProperties.OPEN_WEBSITE_POLICY:
|
||||
websitePolicyProperty = property.value as string | undefined;
|
||||
break;
|
||||
case 'openWebsiteWidth':
|
||||
case GameMapProperties.OPEN_WEBSITE_WIDTH:
|
||||
websiteWidthProperty = property.value as number | undefined;
|
||||
break;
|
||||
case 'openWebsitePosition':
|
||||
case GameMapProperties.OPEN_WEBSITE_POSITION:
|
||||
websitePositionProperty = property.value as number | undefined;
|
||||
break;
|
||||
case TRIGGER_WEBSITE_PROPERTIES:
|
||||
case GameMapProperties.OPEN_WEBSITE_TRIGGER:
|
||||
websiteTriggerProperty = property.value as string | undefined;
|
||||
break;
|
||||
case WEBSITE_MESSAGE_PROPERTIES:
|
||||
case GameMapProperties.OPEN_WEBSITE_TRIGGER_MESSAGE:
|
||||
websiteTriggerMessageProperty = property.value as string | undefined;
|
||||
break;
|
||||
}
|
||||
@ -172,10 +171,10 @@ export class GameMapPropertiesListener {
|
||||
|
||||
layer.properties.forEach(property => {
|
||||
switch(property.name) {
|
||||
case 'openWebsite':
|
||||
case GameMapProperties.OPEN_WEBSITE:
|
||||
openWebsiteProperty = property.value as string | undefined;
|
||||
break;
|
||||
case TRIGGER_WEBSITE_PROPERTIES:
|
||||
case GameMapProperties.OPEN_WEBSITE_TRIGGER:
|
||||
websiteTriggerProperty = property.value as string | undefined;
|
||||
break;
|
||||
}
|
||||
|
@ -16,14 +16,8 @@ import { DEBUG_MODE, JITSI_PRIVATE_MODE, MAX_PER_GROUP, POSITION_DELAY } from ".
|
||||
|
||||
import { Queue } from "queue-typescript";
|
||||
import {
|
||||
AUDIO_LOOP_PROPERTY,
|
||||
AUDIO_VOLUME_PROPERTY,
|
||||
Box,
|
||||
JITSI_MESSAGE_PROPERTIES,
|
||||
ON_ACTION_TRIGGER_BUTTON,
|
||||
TRIGGER_JITSI_PROPERTIES,
|
||||
TRIGGER_WEBSITE_PROPERTIES,
|
||||
WEBSITE_MESSAGE_PROPERTIES,
|
||||
} from "../../WebRtc/LayoutManager";
|
||||
import { CoWebsite, coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
|
||||
import type { UserMovedMessage } from "../../Messages/generated/messages_pb";
|
||||
@ -96,6 +90,7 @@ import { GameMapPropertiesListener } from "./GameMapPropertiesListener";
|
||||
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
||||
import { get } from "svelte/store";
|
||||
import { contactPageStore } from "../../Stores/MenuStore";
|
||||
import { GameMapProperties } from "./GameMapProperties";
|
||||
|
||||
export interface GameSceneInitInterface {
|
||||
initPosition: PointInterface | null;
|
||||
@ -498,11 +493,11 @@ export class GameScene extends DirtyScene {
|
||||
if (object.type === "website") {
|
||||
// Let's load iframes in the map
|
||||
const url = PropertyUtils.mustFindStringProperty(
|
||||
"url",
|
||||
GameMapProperties.URL,
|
||||
object.properties,
|
||||
'in the "' + object.name + '" object of type "website"'
|
||||
);
|
||||
const allowApi = PropertyUtils.findBooleanProperty("allowApi", object.properties);
|
||||
const allowApi = PropertyUtils.findBooleanProperty(GameMapProperties.ALLOW_API, object.properties);
|
||||
|
||||
// TODO: add a "allow" property to iframe
|
||||
this.embeddedWebsiteManager.createEmbeddedWebsite(
|
||||
@ -828,7 +823,7 @@ export class GameScene extends DirtyScene {
|
||||
}
|
||||
|
||||
private triggerOnMapLayerPropertyChange() {
|
||||
this.gameMap.onPropertyChange("exitSceneUrl", (newValue, oldValue) => {
|
||||
this.gameMap.onPropertyChange(GameMapProperties.EXIT_SCENE_URL, (newValue, oldValue) => {
|
||||
if (newValue) {
|
||||
this.onMapExit(
|
||||
Room.getRoomPathFromExitSceneUrl(newValue as string, window.location.toString(), this.MapUrlFile)
|
||||
@ -839,7 +834,7 @@ export class GameScene extends DirtyScene {
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
this.gameMap.onPropertyChange("exitUrl", (newValue, oldValue) => {
|
||||
this.gameMap.onPropertyChange(GameMapProperties.EXIT_URL, (newValue, oldValue) => {
|
||||
if (newValue) {
|
||||
this.onMapExit(Room.getRoomPathFromExitUrl(newValue as string, window.location.toString()));
|
||||
} else {
|
||||
@ -849,16 +844,16 @@ export class GameScene extends DirtyScene {
|
||||
}
|
||||
});
|
||||
|
||||
this.gameMap.onPropertyChange("jitsiRoom", (newValue, oldValue, allProps) => {
|
||||
this.gameMap.onPropertyChange(GameMapProperties.JITSI_ROOM, (newValue, oldValue, allProps) => {
|
||||
if (newValue === undefined) {
|
||||
layoutManagerActionStore.removeAction("jitsi");
|
||||
this.stopJitsi();
|
||||
} else {
|
||||
const openJitsiRoomFunction = () => {
|
||||
const roomName = jitsiFactory.getRoomName(newValue.toString(), this.instance);
|
||||
const jitsiUrl = allProps.get("jitsiUrl") as string | undefined;
|
||||
const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined;
|
||||
if (JITSI_PRIVATE_MODE && !jitsiUrl) {
|
||||
const adminTag = allProps.get("jitsiRoomAdminTag") as string | undefined;
|
||||
const adminTag = allProps.get(GameMapProperties.JITSI_ADMIN_ROOM_TAG) as string | undefined;
|
||||
|
||||
this.connection?.emitQueryJitsiJwtMessage(roomName, adminTag);
|
||||
} else {
|
||||
@ -867,9 +862,9 @@ export class GameScene extends DirtyScene {
|
||||
layoutManagerActionStore.removeAction("jitsi");
|
||||
};
|
||||
|
||||
const jitsiTriggerValue = allProps.get(TRIGGER_JITSI_PROPERTIES);
|
||||
const jitsiTriggerValue = allProps.get(GameMapProperties.JITSI_TRIGGER);
|
||||
if (jitsiTriggerValue && jitsiTriggerValue === ON_ACTION_TRIGGER_BUTTON) {
|
||||
let message = allProps.get(JITSI_MESSAGE_PROPERTIES);
|
||||
let message = allProps.get(GameMapProperties.JITSI_TRIGGER_MESSAGE);
|
||||
if (message === undefined) {
|
||||
message = "Press SPACE or touch here to enter Jitsi Meet room";
|
||||
}
|
||||
@ -885,7 +880,7 @@ export class GameScene extends DirtyScene {
|
||||
}
|
||||
}
|
||||
});
|
||||
this.gameMap.onPropertyChange("silent", (newValue, oldValue) => {
|
||||
this.gameMap.onPropertyChange(GameMapProperties.SILENT, (newValue, oldValue) => {
|
||||
if (newValue === undefined || newValue === false || newValue === "") {
|
||||
this.connection?.setSilent(false);
|
||||
this.CurrentPlayer.noSilent();
|
||||
@ -894,16 +889,16 @@ export class GameScene extends DirtyScene {
|
||||
this.CurrentPlayer.isSilent();
|
||||
}
|
||||
});
|
||||
this.gameMap.onPropertyChange("playAudio", (newValue, oldValue, allProps) => {
|
||||
const volume = allProps.get(AUDIO_VOLUME_PROPERTY) as number | undefined;
|
||||
const loop = allProps.get(AUDIO_LOOP_PROPERTY) as boolean | undefined;
|
||||
this.gameMap.onPropertyChange(GameMapProperties.PLAY_AUDIO, (newValue, oldValue, allProps) => {
|
||||
const volume = allProps.get(GameMapProperties.AUDIO_VOLUME) as number | undefined;
|
||||
const loop = allProps.get(GameMapProperties.AUDIO_LOOP) as boolean | undefined;
|
||||
newValue === undefined
|
||||
? audioManagerFileStore.unloadAudio()
|
||||
: audioManagerFileStore.playAudio(newValue, this.getMapDirUrl(), volume, loop);
|
||||
audioManagerVisibilityStore.set(!(newValue === undefined));
|
||||
});
|
||||
// TODO: This legacy property should be removed at some point
|
||||
this.gameMap.onPropertyChange("playAudioLoop", (newValue, oldValue) => {
|
||||
this.gameMap.onPropertyChange(GameMapProperties.PLAY_AUDIO_LOOP, (newValue, oldValue) => {
|
||||
newValue === undefined
|
||||
? audioManagerFileStore.unloadAudio()
|
||||
: audioManagerFileStore.playAudio(newValue, this.getMapDirUrl(), undefined, true);
|
||||
@ -911,7 +906,7 @@ export class GameScene extends DirtyScene {
|
||||
});
|
||||
|
||||
// TODO: Legacy functionnality replace by layer change
|
||||
this.gameMap.onPropertyChange("zone", (newValue, oldValue) => {
|
||||
this.gameMap.onPropertyChange(GameMapProperties.ZONE, (newValue, oldValue) => {
|
||||
if (oldValue) {
|
||||
iframeListener.sendLeaveEvent(oldValue as string);
|
||||
}
|
||||
@ -1265,7 +1260,7 @@ ${escapedMessage}
|
||||
propertyName: string,
|
||||
propertyValue: string | number | boolean | undefined
|
||||
): void {
|
||||
if (propertyName === "exitUrl" && typeof propertyValue === "string") {
|
||||
if (propertyName === GameMapProperties.EXIT_URL && typeof propertyValue === "string") {
|
||||
this.loadNextGameFromExitUrl(propertyValue);
|
||||
}
|
||||
this.gameMap.setLayerProperty(layerName, propertyName, propertyValue);
|
||||
@ -1403,18 +1398,18 @@ ${escapedMessage}
|
||||
}
|
||||
|
||||
private getExitUrl(layer: ITiledMapLayer): string | undefined {
|
||||
return this.getProperty(layer, "exitUrl") as string | undefined;
|
||||
return this.getProperty(layer, GameMapProperties.EXIT_URL) as string | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated the map property exitSceneUrl is deprecated
|
||||
*/
|
||||
private getExitSceneUrl(layer: ITiledMapLayer): string | undefined {
|
||||
return this.getProperty(layer, "exitSceneUrl") as string | undefined;
|
||||
return this.getProperty(layer, GameMapProperties.EXIT_SCENE_URL) as string | undefined;
|
||||
}
|
||||
|
||||
private getScriptUrls(map: ITiledMap): string[] {
|
||||
return (this.getProperties(map, "script") as string[]).map((script) =>
|
||||
return (this.getProperties(map, GameMapProperties.SCRIPT) as string[]).map((script) =>
|
||||
new URL(script, this.MapUrlFile).toString()
|
||||
);
|
||||
}
|
||||
@ -1872,13 +1867,15 @@ ${escapedMessage}
|
||||
|
||||
public startJitsi(roomName: string, jwt?: string): void {
|
||||
const allProps = this.gameMap.getCurrentProperties();
|
||||
const jitsiConfig = this.safeParseJSONstring(allProps.get("jitsiConfig") as string | undefined, "jitsiConfig");
|
||||
const jitsiInterfaceConfig = this.safeParseJSONstring(
|
||||
allProps.get("jitsiInterfaceConfig") as string | undefined,
|
||||
"jitsiInterfaceConfig"
|
||||
const jitsiConfig = this.safeParseJSONstring(
|
||||
allProps.get(GameMapProperties.JITSI_CONFIG) as string | undefined, GameMapProperties.JITSI_CONFIG
|
||||
);
|
||||
const jitsiUrl = allProps.get("jitsiUrl") as string | undefined;
|
||||
const jitsiWidth = allProps.get("jitsiWidth") as number | undefined;
|
||||
const jitsiInterfaceConfig = this.safeParseJSONstring(
|
||||
allProps.get(GameMapProperties.JITSI_INTERFACE_CONFIG) as string | undefined,
|
||||
GameMapProperties.JITSI_INTERFACE_CONFIG
|
||||
);
|
||||
const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined;
|
||||
const jitsiWidth = allProps.get(GameMapProperties.JITSI_WIDTH) as number | undefined;
|
||||
|
||||
jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl, jitsiWidth);
|
||||
this.connection?.setSilent(true);
|
||||
@ -1892,7 +1889,7 @@ ${escapedMessage}
|
||||
}
|
||||
|
||||
public stopJitsi(): void {
|
||||
const silent = this.gameMap.getCurrentProperties().get("silent");
|
||||
const silent = this.gameMap.getCurrentProperties().get(GameMapProperties.SILENT);
|
||||
this.connection?.setSilent(!!silent);
|
||||
jitsiFactory.stop();
|
||||
mediaManager.showGameOverlay();
|
||||
|
@ -2,6 +2,7 @@ import type { RoomConnection } from "../../Connexion/RoomConnection";
|
||||
import { iframeListener } from "../../Api/IframeListener";
|
||||
import type { GameMap } from "./GameMap";
|
||||
import type { ITiledMapLayer, ITiledMapObject, ITiledMapObjectLayer } from "../Map/ITiledMap";
|
||||
import { GameMapProperties } from "./GameMapProperties";
|
||||
|
||||
interface Variable {
|
||||
defaultValue: unknown;
|
||||
@ -133,10 +134,10 @@ export class SharedVariablesManager {
|
||||
for (const property of object.properties) {
|
||||
const value = property.value;
|
||||
switch (property.name) {
|
||||
case "default":
|
||||
case GameMapProperties.DEFAULT:
|
||||
variable.defaultValue = value;
|
||||
break;
|
||||
case "writableBy":
|
||||
case GameMapProperties.WRITABLE_BY:
|
||||
if (typeof value !== "string") {
|
||||
throw new Error(
|
||||
'The writableBy property of variable "' + object.name + '" must be a string'
|
||||
@ -146,7 +147,7 @@ export class SharedVariablesManager {
|
||||
variable.writableBy = value;
|
||||
}
|
||||
break;
|
||||
case "readableBy":
|
||||
case GameMapProperties.READABLE_BY:
|
||||
if (typeof value !== "string") {
|
||||
throw new Error(
|
||||
'The readableBy property of variable "' + object.name + '" must be a string'
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { PositionInterface } from "../../Connexion/ConnexionModels";
|
||||
import type { ITiledMap, ITiledMapLayer, ITiledMapProperty, ITiledMapTileLayer } from "../Map/ITiledMap";
|
||||
import type { GameMap } from "./GameMap";
|
||||
import { GameMapProperties } from "./GameMapProperties";
|
||||
|
||||
const defaultStartLayerName = "start";
|
||||
|
||||
@ -76,7 +77,7 @@ export class StartPositionCalculator {
|
||||
}
|
||||
|
||||
private isStartLayer(layer: ITiledMapLayer): boolean {
|
||||
return this.getProperty(layer, "startLayer") == true;
|
||||
return this.getProperty(layer, GameMapProperties.START_LAYER) == true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -14,13 +14,4 @@ export enum DivImportance {
|
||||
|
||||
export const ON_ACTION_TRIGGER_BUTTON = "onaction";
|
||||
|
||||
export const TRIGGER_WEBSITE_PROPERTIES = "openWebsiteTrigger";
|
||||
export const TRIGGER_JITSI_PROPERTIES = "jitsiTrigger";
|
||||
|
||||
export const WEBSITE_MESSAGE_PROPERTIES = "openWebsiteTriggerMessage";
|
||||
export const JITSI_MESSAGE_PROPERTIES = "jitsiTriggerMessage";
|
||||
|
||||
export const AUDIO_VOLUME_PROPERTY = "audioVolume";
|
||||
export const AUDIO_LOOP_PROPERTY = "audioLoop";
|
||||
|
||||
export type Box = { xStart: number; yStart: number; xEnd: number; yEnd: number };
|
||||
|
Loading…
Reference in New Issue
Block a user