Merge branch 'develop' of github.com:thecodingmachine/workadventure
@@ -1,12 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Handles variables shared between the scripting API and the server.
|
* Handles variables shared between the scripting API and the server.
|
||||||
*/
|
*/
|
||||||
import {
|
import { ITiledMap, ITiledMapLayer, ITiledMapObject } from "@workadventure/tiled-map-type-guard/dist";
|
||||||
ITiledMap,
|
|
||||||
ITiledMapLayer,
|
|
||||||
ITiledMapObject,
|
|
||||||
ITiledMapObjectLayer,
|
|
||||||
} from "@workadventure/tiled-map-type-guard/dist";
|
|
||||||
import { User } from "_Model/User";
|
import { User } from "_Model/User";
|
||||||
import { variablesRepository } from "./Repository/VariablesRepository";
|
import { variablesRepository } from "./Repository/VariablesRepository";
|
||||||
import { redisClient } from "./RedisClient";
|
import { redisClient } from "./RedisClient";
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
{.section-title.accent.text-primary}
|
||||||
|
# Working with camera
|
||||||
|
|
||||||
|
## Focusable Zones
|
||||||
|
|
||||||
|
It is possible to define special regions on the map that can make the camera zoom and center on themselves. We call them "Focusable Zones". When player gets inside, his camera view will be altered - focused, zoomed and locked on defined zone, like this:
|
||||||
|
|
||||||
|
<div class="px-5 card rounded d-inline-block">
|
||||||
|
<img class="document-img" src="images/camera/0_focusable_zone.png" alt="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
### Adding new **Focusable Zone**:
|
||||||
|
|
||||||
|
1. Make sure you are editing an **Object Layer**
|
||||||
|
|
||||||
|
<div class="px-5 card rounded d-inline-block">
|
||||||
|
<img class="document-img" src="images/camera/1_object_layer.png" alt="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
2. Select **Insert Rectangle** tool
|
||||||
|
|
||||||
|
<div class="px-5 card rounded d-inline-block">
|
||||||
|
<img class="document-img" src="images/camera/2_rectangle_zone.png" alt="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
3. Define new object wherever you want. For example, you can make your chilling room event cosier!
|
||||||
|
|
||||||
|
<div class="px-5 card rounded d-inline-block">
|
||||||
|
<img class="document-img" src="images/camera/3_define_new_zone.png" alt="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
4. Edit this new object and click on **Add Property**, like this:
|
||||||
|
|
||||||
|
<div class="px-5 card rounded d-inline-block">
|
||||||
|
<img class="document-img" src="images/camera/4_click_add_property.png" alt="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
5. Add a **bool** property of name *focusable*:
|
||||||
|
|
||||||
|
<div class="px-5 card rounded d-inline-block">
|
||||||
|
<img class="document-img" src="images/camera/5_add_focusable_prop.png" alt="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
6. Make sure it's checked! :)
|
||||||
|
|
||||||
|
<div class="px-5 card rounded d-inline-block">
|
||||||
|
<img class="document-img" src="images/camera/6_make_sure_checked.png" alt="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
All should be set up now and your new **Focusable Zone** should be working fine!
|
||||||
|
|
||||||
|
### Defining custom zoom margin:
|
||||||
|
|
||||||
|
If you want, you can add an additional property to control how much should the camera zoom onto focusable zone.
|
||||||
|
|
||||||
|
1. Like before, click on **Add Property**
|
||||||
|
|
||||||
|
<div class="px-5 card rounded d-inline-block">
|
||||||
|
<img class="document-img" src="images/camera/4_click_add_property.png" alt="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
2. Add a **float** property of name *zoom_margin*:
|
||||||
|
|
||||||
|
<div class="px-5 card rounded d-inline-block">
|
||||||
|
<img class="document-img" src="images/camera/7_add_zoom_margin.png" alt="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
2. Define how much (in percentage value) should the zoom be decreased:
|
||||||
|
|
||||||
|
<div class="px-5 card rounded d-inline-block">
|
||||||
|
<img class="document-img" src="images/camera/8_optional_zoom_margin_defined.png" alt="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
For example, if you define your zone as a 300x200 rectangle, setting this property to 0.5 *(50%)* means the camera will try to fit within the viewport the entire zone + margin of 50% of its dimensions, so 450x300.
|
||||||
|
|
||||||
|
- No margin defined
|
||||||
|
|
||||||
|
<div class="px-5 card rounded d-inline-block">
|
||||||
|
<img class="document-img" src="images/camera/no_margin.png" alt="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
- Margin set to **0.35**
|
||||||
|
|
||||||
|
<div class="px-5 card rounded d-inline-block">
|
||||||
|
<img class="document-img" src="images/camera/with_margin.png" alt="" />
|
||||||
|
</div>
|
||||||
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 51 KiB |
@@ -51,6 +51,12 @@ return [
|
|||||||
'markdown' => 'maps.website-in-map',
|
'markdown' => 'maps.website-in-map',
|
||||||
'editUrl' => 'https://github.com/thecodingmachine/workadventure/edit/develop/docs/maps/website-in-map.md',
|
'editUrl' => 'https://github.com/thecodingmachine/workadventure/edit/develop/docs/maps/website-in-map.md',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'title' => 'Camera',
|
||||||
|
'url' => '/map-building/camera.md',
|
||||||
|
'markdown' => 'maps.camera',
|
||||||
|
'editUrl' => 'https://github.com/thecodingmachine/workadventure/edit/develop/docs/maps/camera.md',
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'title' => 'Variables',
|
'title' => 'Variables',
|
||||||
'url' => '/map-building/variables.md',
|
'url' => '/map-building/variables.md',
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import * as tg from "generic-type-guard";
|
||||||
|
|
||||||
|
export const isChangeZoneEvent = new tg.IsInterface()
|
||||||
|
.withProperties({
|
||||||
|
name: tg.isString,
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
/**
|
||||||
|
* A message sent from the game to the iFrame when a user enters or leaves a zone.
|
||||||
|
*/
|
||||||
|
export type ChangeZoneEvent = tg.GuardedType<typeof isChangeZoneEvent>;
|
||||||
@@ -28,6 +28,7 @@ import type { MessageReferenceEvent } from "./ui/TriggerActionMessageEvent";
|
|||||||
import { isMessageReferenceEvent, isTriggerActionMessageEvent } from "./ui/TriggerActionMessageEvent";
|
import { isMessageReferenceEvent, isTriggerActionMessageEvent } from "./ui/TriggerActionMessageEvent";
|
||||||
import type { MenuRegisterEvent, UnregisterMenuEvent } from "./ui/MenuRegisterEvent";
|
import type { MenuRegisterEvent, UnregisterMenuEvent } from "./ui/MenuRegisterEvent";
|
||||||
import type { ChangeLayerEvent } from "./ChangeLayerEvent";
|
import type { ChangeLayerEvent } from "./ChangeLayerEvent";
|
||||||
|
import type { ChangeZoneEvent } from "./ChangeZoneEvent";
|
||||||
import { isGetPropertyEvent } from "./GetPropertyEvent";
|
import { isGetPropertyEvent } from "./GetPropertyEvent";
|
||||||
|
|
||||||
export interface TypedMessageEvent<T> extends MessageEvent {
|
export interface TypedMessageEvent<T> extends MessageEvent {
|
||||||
@@ -77,6 +78,8 @@ export interface IframeResponseEventMap {
|
|||||||
leaveEvent: EnterLeaveEvent;
|
leaveEvent: EnterLeaveEvent;
|
||||||
enterLayerEvent: ChangeLayerEvent;
|
enterLayerEvent: ChangeLayerEvent;
|
||||||
leaveLayerEvent: ChangeLayerEvent;
|
leaveLayerEvent: ChangeLayerEvent;
|
||||||
|
enterZoneEvent: ChangeZoneEvent;
|
||||||
|
leaveZoneEvent: ChangeZoneEvent;
|
||||||
buttonClickedEvent: ButtonClickedEvent;
|
buttonClickedEvent: ButtonClickedEvent;
|
||||||
hasPlayerMoved: HasPlayerMovedEvent;
|
hasPlayerMoved: HasPlayerMovedEvent;
|
||||||
menuItemClicked: MenuItemClickedEvent;
|
menuItemClicked: MenuItemClickedEvent;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import type { SetVariableEvent } from "./Events/SetVariableEvent";
|
|||||||
import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent";
|
import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent";
|
||||||
import { handleMenuRegistrationEvent, handleMenuUnregisterEvent } from "../Stores/MenuStore";
|
import { handleMenuRegistrationEvent, handleMenuUnregisterEvent } from "../Stores/MenuStore";
|
||||||
import type { ChangeLayerEvent } from "./Events/ChangeLayerEvent";
|
import type { ChangeLayerEvent } from "./Events/ChangeLayerEvent";
|
||||||
|
import type { ChangeZoneEvent } from "./Events/ChangeZoneEvent";
|
||||||
|
|
||||||
type AnswererCallback<T extends keyof IframeQueryMap> = (
|
type AnswererCallback<T extends keyof IframeQueryMap> = (
|
||||||
query: IframeQueryMap[T]["query"],
|
query: IframeQueryMap[T]["query"],
|
||||||
@@ -420,6 +421,24 @@ class IframeListener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendEnterZoneEvent(zoneName: string) {
|
||||||
|
this.postMessage({
|
||||||
|
type: "enterZoneEvent",
|
||||||
|
data: {
|
||||||
|
name: zoneName,
|
||||||
|
} as ChangeZoneEvent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sendLeaveZoneEvent(zoneName: string) {
|
||||||
|
this.postMessage({
|
||||||
|
type: "leaveZoneEvent",
|
||||||
|
data: {
|
||||||
|
name: zoneName,
|
||||||
|
} as ChangeZoneEvent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
hasPlayerMoved(event: HasPlayerMovedEvent) {
|
hasPlayerMoved(event: HasPlayerMovedEvent) {
|
||||||
if (this.sendPlayerMove) {
|
if (this.sendPlayerMove) {
|
||||||
this.postMessage({
|
this.postMessage({
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<script lang="typescript">
|
||||||
|
import { gameManager } from "../../Phaser/Game/GameManager";
|
||||||
|
import type { PictureStore } from "../../Stores/PictureStore";
|
||||||
|
import { onDestroy } from "svelte";
|
||||||
|
|
||||||
|
export let userId: number;
|
||||||
|
export let placeholderSrc: string;
|
||||||
|
export let width: string = "62px";
|
||||||
|
export let height: string = "62px";
|
||||||
|
|
||||||
|
const gameScene = gameManager.getCurrentGameScene();
|
||||||
|
let companionWokaPictureStore: PictureStore | undefined;
|
||||||
|
if (userId === -1) {
|
||||||
|
companionWokaPictureStore = gameScene.CurrentPlayer.companion?.pictureStore;
|
||||||
|
} else {
|
||||||
|
companionWokaPictureStore = gameScene.MapPlayersByKey.getNestedStore(
|
||||||
|
userId,
|
||||||
|
(item) => item.companion?.pictureStore
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let src = placeholderSrc;
|
||||||
|
|
||||||
|
if (companionWokaPictureStore) {
|
||||||
|
const unsubscribe = companionWokaPictureStore.subscribe((source) => {
|
||||||
|
src = source ?? placeholderSrc;
|
||||||
|
});
|
||||||
|
|
||||||
|
onDestroy(unsubscribe);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<img {src} alt="" class="nes-pointer" style="--theme-width: {width}; --theme-height: {height}" />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
img {
|
||||||
|
display: inline-block;
|
||||||
|
pointer-events: auto;
|
||||||
|
width: var(--theme-width);
|
||||||
|
height: var(--theme-height);
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: static;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -15,7 +15,8 @@
|
|||||||
import btnProfileSubMenuCamera from "../images/btn-menu-profile-camera.svg";
|
import btnProfileSubMenuCamera from "../images/btn-menu-profile-camera.svg";
|
||||||
import btnProfileSubMenuIdentity from "../images/btn-menu-profile-identity.svg";
|
import btnProfileSubMenuIdentity from "../images/btn-menu-profile-identity.svg";
|
||||||
import btnProfileSubMenuCompanion from "../images/btn-menu-profile-companion.svg";
|
import btnProfileSubMenuCompanion from "../images/btn-menu-profile-companion.svg";
|
||||||
import btnProfileSubMenuWoka from "../images/btn-menu-profile-woka.svg";
|
import Woka from "../Woka/Woka.svelte";
|
||||||
|
import Companion from "../Companion/Companion.svelte";
|
||||||
|
|
||||||
function disableMenuStores() {
|
function disableMenuStores() {
|
||||||
menuVisiblilityStore.set(false);
|
menuVisiblilityStore.set(false);
|
||||||
@@ -67,12 +68,12 @@
|
|||||||
</button>
|
</button>
|
||||||
-->
|
-->
|
||||||
<button type="button" class="nes-btn" on:click|preventDefault={openEditSkinScene}>
|
<button type="button" class="nes-btn" on:click|preventDefault={openEditSkinScene}>
|
||||||
<img src={btnProfileSubMenuWoka} alt="Edit your WOKA" />
|
<Woka userId={-1} placeholderSrc="" width="26px" height="26px" />
|
||||||
<span class="btn-hover">Edit your Avatar</span>
|
<span class="btn-hover">Edit your Avatar</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="nes-btn" on:click|preventDefault={openEditCompanionScene}>
|
<button type="button" class="nes-btn" on:click|preventDefault={openEditCompanionScene}>
|
||||||
<img src={btnProfileSubMenuCompanion} alt="Edit your companion" />
|
<Companion userId={-1} placeholderSrc={btnProfileSubMenuCompanion} width="26px" height="26px" />
|
||||||
<span class="btn-hover">Change your companion</span>
|
<span class="btn-hover">Edit your companion</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="nes-btn" on:click|preventDefault={openEnableCameraScene}>
|
<button type="button" class="nes-btn" on:click|preventDefault={openEnableCameraScene}>
|
||||||
<img src={btnProfileSubMenuCamera} alt="Edit your camera" />
|
<img src={btnProfileSubMenuCamera} alt="Edit your camera" />
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
import { showReportScreenStore } from "../../Stores/ShowReportScreenStore";
|
import { showReportScreenStore } from "../../Stores/ShowReportScreenStore";
|
||||||
import { getColorByString, srcObject } from "./utils";
|
import { getColorByString, srcObject } from "./utils";
|
||||||
|
|
||||||
|
import Woka from "../Woka/Woka.svelte";
|
||||||
|
|
||||||
export let peer: VideoPeer;
|
export let peer: VideoPeer;
|
||||||
let streamStore = peer.streamStore;
|
let streamStore = peer.streamStore;
|
||||||
let name = peer.userName;
|
let name = peer.userName;
|
||||||
@@ -26,9 +28,15 @@
|
|||||||
{#if $statusStore === "error"}
|
{#if $statusStore === "error"}
|
||||||
<div class="rtc-error" />
|
<div class="rtc-error" />
|
||||||
{/if}
|
{/if}
|
||||||
{#if !$constraintStore || $constraintStore.video === false}
|
<!-- {#if !$constraintStore || $constraintStore.video === false} -->
|
||||||
<i style="background-color: {getColorByString(name)};">{name}</i>
|
<i
|
||||||
{/if}
|
class="container {!$constraintStore || $constraintStore.video === false ? '' : 'minimized'}"
|
||||||
|
style="background-color: {getColorByString(name)};"
|
||||||
|
>
|
||||||
|
<span>{peer.userName}</span>
|
||||||
|
<div class="woka-icon"><Woka userId={peer.userId} placeholderSrc={""} /></div>
|
||||||
|
</i>
|
||||||
|
<!-- {/if} -->
|
||||||
{#if $constraintStore && $constraintStore.audio === false}
|
{#if $constraintStore && $constraintStore.audio === false}
|
||||||
<img src={microphoneCloseImg} class="active" alt="Muted" />
|
<img src={microphoneCloseImg} class="active" alt="Muted" />
|
||||||
{/if}
|
{/if}
|
||||||
@@ -43,3 +51,21 @@
|
|||||||
<SoundMeterWidget stream={$streamStore} />
|
<SoundMeterWidget stream={$streamStore} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minimized {
|
||||||
|
left: auto;
|
||||||
|
transform: scale(0.5);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woka-icon {
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<script lang="typescript">
|
||||||
|
import { onDestroy } from "svelte";
|
||||||
|
|
||||||
|
import { gameManager } from "../../Phaser/Game/GameManager";
|
||||||
|
|
||||||
|
export let userId: number;
|
||||||
|
export let placeholderSrc: string;
|
||||||
|
export let width: string = "62px";
|
||||||
|
export let height: string = "62px";
|
||||||
|
|
||||||
|
const gameScene = gameManager.getCurrentGameScene();
|
||||||
|
let playerWokaPictureStore;
|
||||||
|
if (userId === -1) {
|
||||||
|
playerWokaPictureStore = gameScene.CurrentPlayer.pictureStore;
|
||||||
|
} else {
|
||||||
|
playerWokaPictureStore = gameScene.MapPlayersByKey.getNestedStore(userId, (item) => item.pictureStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
let src = placeholderSrc;
|
||||||
|
|
||||||
|
const unsubscribe = playerWokaPictureStore.subscribe((source) => {
|
||||||
|
src = source ?? placeholderSrc;
|
||||||
|
});
|
||||||
|
|
||||||
|
onDestroy(unsubscribe);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<img {src} alt="" class="nes-pointer" style="--theme-width: {width}; --theme-height: {height}" />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
img {
|
||||||
|
display: inline-block;
|
||||||
|
pointer-events: auto;
|
||||||
|
width: var(--theme-width);
|
||||||
|
height: var(--theme-height);
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: static;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
import Sprite = Phaser.GameObjects.Sprite;
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
import Container = Phaser.GameObjects.Container;
|
import Container = Phaser.GameObjects.Container;
|
||||||
import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation";
|
import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation";
|
||||||
|
import { TexturesHelper } from "../Helpers/TexturesHelper";
|
||||||
|
import { Writable, writable } from "svelte/store";
|
||||||
|
import type { PictureStore } from "../../Stores/PictureStore";
|
||||||
|
|
||||||
export interface CompanionStatus {
|
export interface CompanionStatus {
|
||||||
x: number;
|
x: number;
|
||||||
@@ -21,6 +24,7 @@ export class Companion extends Container {
|
|||||||
private companionName: string;
|
private companionName: string;
|
||||||
private direction: PlayerAnimationDirections;
|
private direction: PlayerAnimationDirections;
|
||||||
private animationType: PlayerAnimationTypes;
|
private animationType: PlayerAnimationTypes;
|
||||||
|
private readonly _pictureStore: Writable<string | undefined>;
|
||||||
|
|
||||||
constructor(scene: Phaser.Scene, x: number, y: number, name: string, texturePromise: Promise<string>) {
|
constructor(scene: Phaser.Scene, x: number, y: number, name: string, texturePromise: Promise<string>) {
|
||||||
super(scene, x + 14, y + 4);
|
super(scene, x + 14, y + 4);
|
||||||
@@ -35,10 +39,14 @@ export class Companion extends Container {
|
|||||||
this.animationType = PlayerAnimationTypes.Idle;
|
this.animationType = PlayerAnimationTypes.Idle;
|
||||||
|
|
||||||
this.companionName = name;
|
this.companionName = name;
|
||||||
|
this._pictureStore = writable(undefined);
|
||||||
|
|
||||||
texturePromise.then((resource) => {
|
texturePromise.then((resource) => {
|
||||||
this.addResource(resource);
|
this.addResource(resource);
|
||||||
this.invisible = false;
|
this.invisible = false;
|
||||||
|
return this.getSnapshot().then((htmlImageElementSrc) => {
|
||||||
|
this._pictureStore.set(htmlImageElementSrc);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.scene.physics.world.enableBody(this);
|
this.scene.physics.world.enableBody(this);
|
||||||
@@ -123,6 +131,22 @@ export class Companion extends Container {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getSnapshot(): Promise<string> {
|
||||||
|
const sprites = Array.from(this.sprites.values()).map((sprite) => {
|
||||||
|
return { sprite, frame: 1 };
|
||||||
|
});
|
||||||
|
return TexturesHelper.getSnapshot(this.scene, ...sprites).catch((reason) => {
|
||||||
|
console.warn(reason);
|
||||||
|
for (const sprite of this.sprites.values()) {
|
||||||
|
// it can be either cat or dog prefix
|
||||||
|
if (sprite.texture.key.includes("cat") || sprite.texture.key.includes("dog")) {
|
||||||
|
return this.scene.textures.getBase64(sprite.texture.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "cat1";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private playAnimation(direction: PlayerAnimationDirections, type: PlayerAnimationTypes): void {
|
private playAnimation(direction: PlayerAnimationDirections, type: PlayerAnimationTypes): void {
|
||||||
if (this.invisible) return;
|
if (this.invisible) return;
|
||||||
|
|
||||||
@@ -220,4 +244,8 @@ export class Companion extends Container {
|
|||||||
|
|
||||||
super.destroy();
|
super.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get pictureStore(): PictureStore {
|
||||||
|
return this._pictureStore;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ import { TextureError } from "../../Exception/TextureError";
|
|||||||
import { Companion } from "../Companion/Companion";
|
import { Companion } from "../Companion/Companion";
|
||||||
import type { GameScene } from "../Game/GameScene";
|
import type { GameScene } from "../Game/GameScene";
|
||||||
import { DEPTH_INGAME_TEXT_INDEX } from "../Game/DepthIndexes";
|
import { DEPTH_INGAME_TEXT_INDEX } from "../Game/DepthIndexes";
|
||||||
import { waScaleManager } from "../Services/WaScaleManager";
|
|
||||||
import type OutlinePipelinePlugin from "phaser3-rex-plugins/plugins/outlinepipeline-plugin.js";
|
import type OutlinePipelinePlugin from "phaser3-rex-plugins/plugins/outlinepipeline-plugin.js";
|
||||||
import { isSilentStore } from "../../Stores/MediaStore";
|
import { isSilentStore } from "../../Stores/MediaStore";
|
||||||
import { lazyLoadPlayerCharacterTextures } from "./PlayerTexturesLoadingManager";
|
import { lazyLoadPlayerCharacterTextures, loadAllDefaultModels } from "./PlayerTexturesLoadingManager";
|
||||||
|
import { TexturesHelper } from "../Helpers/TexturesHelper";
|
||||||
|
import type { PictureStore } from "../../Stores/PictureStore";
|
||||||
|
import { Writable, writable } from "svelte/store";
|
||||||
|
|
||||||
const playerNameY = -25;
|
const playerNameY = -25;
|
||||||
|
|
||||||
@@ -37,6 +39,7 @@ export abstract class Character extends Container {
|
|||||||
private emote: Phaser.GameObjects.DOMElement | null = null;
|
private emote: Phaser.GameObjects.DOMElement | null = null;
|
||||||
private emoteTween: Phaser.Tweens.Tween | null = null;
|
private emoteTween: Phaser.Tweens.Tween | null = null;
|
||||||
scene: GameScene;
|
scene: GameScene;
|
||||||
|
private readonly _pictureStore: Writable<string | undefined>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
scene: GameScene,
|
scene: GameScene,
|
||||||
@@ -57,6 +60,7 @@ export abstract class Character extends Container {
|
|||||||
this.invisible = true;
|
this.invisible = true;
|
||||||
|
|
||||||
this.sprites = new Map<string, Sprite>();
|
this.sprites = new Map<string, Sprite>();
|
||||||
|
this._pictureStore = writable(undefined);
|
||||||
|
|
||||||
//textures are inside a Promise in case they need to be lazyloaded before use.
|
//textures are inside a Promise in case they need to be lazyloaded before use.
|
||||||
texturesPromise
|
texturesPromise
|
||||||
@@ -64,6 +68,9 @@ export abstract class Character extends Container {
|
|||||||
this.addTextures(textures, frame);
|
this.addTextures(textures, frame);
|
||||||
this.invisible = false;
|
this.invisible = false;
|
||||||
this.playAnimation(direction, moving);
|
this.playAnimation(direction, moving);
|
||||||
|
return this.getSnapshot().then((htmlImageElementSrc) => {
|
||||||
|
this._pictureStore.set(htmlImageElementSrc);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
return lazyLoadPlayerCharacterTextures(scene.load, ["color_22", "eyes_23"]).then((textures) => {
|
return lazyLoadPlayerCharacterTextures(scene.load, ["color_22", "eyes_23"]).then((textures) => {
|
||||||
@@ -117,8 +124,20 @@ export abstract class Character extends Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOutlinePlugin(): OutlinePipelinePlugin | undefined {
|
private async getSnapshot(): Promise<string> {
|
||||||
return this.scene.plugins.get("rexOutlinePipeline") as unknown as OutlinePipelinePlugin | undefined;
|
const sprites = Array.from(this.sprites.values()).map((sprite) => {
|
||||||
|
return { sprite, frame: 1 };
|
||||||
|
});
|
||||||
|
return TexturesHelper.getSnapshot(this.scene, ...sprites).catch((reason) => {
|
||||||
|
console.warn(reason);
|
||||||
|
for (const sprite of this.sprites.values()) {
|
||||||
|
// we can be sure that either predefined woka or body texture is at this point loaded
|
||||||
|
if (sprite.texture.key.includes("color") || sprite.texture.key.includes("male")) {
|
||||||
|
return this.scene.textures.getBase64(sprite.texture.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "male1";
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public addCompanion(name: string, texturePromise?: Promise<string>): void {
|
public addCompanion(name: string, texturePromise?: Promise<string>): void {
|
||||||
@@ -154,6 +173,10 @@ export abstract class Character extends Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getOutlinePlugin(): OutlinePipelinePlugin | undefined {
|
||||||
|
return this.scene.plugins.get("rexOutlinePipeline") as unknown as OutlinePipelinePlugin | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
private getPlayerAnimations(name: string): AnimationData[] {
|
private getPlayerAnimations(name: string): AnimationData[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -374,4 +397,8 @@ export abstract class Character extends Container {
|
|||||||
this.emote = null;
|
this.emote = null;
|
||||||
this.playerName.setVisible(true);
|
this.playerName.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get pictureStore(): PictureStore {
|
||||||
|
return this._pictureStore;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
import { Easing } from "../../types";
|
||||||
|
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
||||||
|
import type { Box } from "../../WebRtc/LayoutManager";
|
||||||
|
import type { Player } from "../Player/Player";
|
||||||
|
import type { WaScaleManager } from "../Services/WaScaleManager";
|
||||||
|
import type { GameScene } from "./GameScene";
|
||||||
|
|
||||||
|
export enum CameraMode {
|
||||||
|
Free = "Free",
|
||||||
|
Follow = "Follow",
|
||||||
|
Focus = "Focus",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CameraManager extends Phaser.Events.EventEmitter {
|
||||||
|
private scene: GameScene;
|
||||||
|
private camera: Phaser.Cameras.Scene2D.Camera;
|
||||||
|
private cameraBounds: { x: number; y: number };
|
||||||
|
private waScaleManager: WaScaleManager;
|
||||||
|
|
||||||
|
private cameraMode: CameraMode = CameraMode.Free;
|
||||||
|
|
||||||
|
private restoreZoomTween?: Phaser.Tweens.Tween;
|
||||||
|
private startFollowTween?: Phaser.Tweens.Tween;
|
||||||
|
|
||||||
|
private cameraFollowTarget?: { x: number; y: number };
|
||||||
|
|
||||||
|
constructor(scene: GameScene, cameraBounds: { x: number; y: number }, waScaleManager: WaScaleManager) {
|
||||||
|
super();
|
||||||
|
this.scene = scene;
|
||||||
|
|
||||||
|
this.camera = scene.cameras.main;
|
||||||
|
this.cameraBounds = cameraBounds;
|
||||||
|
|
||||||
|
this.waScaleManager = waScaleManager;
|
||||||
|
|
||||||
|
this.initCamera();
|
||||||
|
|
||||||
|
this.bindEventHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy(): void {
|
||||||
|
this.scene.game.events.off("wa-scale-manager:refresh-focus-on-target");
|
||||||
|
super.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCamera(): Phaser.Cameras.Scene2D.Camera {
|
||||||
|
return this.camera;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enterFocusMode(
|
||||||
|
focusOn: { x: number; y: number; width: number; height: number },
|
||||||
|
margin: number = 0,
|
||||||
|
duration: number = 1000
|
||||||
|
): void {
|
||||||
|
this.setCameraMode(CameraMode.Focus);
|
||||||
|
this.waScaleManager.saveZoom();
|
||||||
|
this.waScaleManager.setFocusTarget(focusOn);
|
||||||
|
|
||||||
|
this.restoreZoomTween?.stop();
|
||||||
|
this.startFollowTween?.stop();
|
||||||
|
const marginMult = 1 + margin;
|
||||||
|
const targetZoomModifier = this.waScaleManager.getTargetZoomModifierFor(
|
||||||
|
focusOn.width * marginMult,
|
||||||
|
focusOn.height * marginMult
|
||||||
|
);
|
||||||
|
const currentZoomModifier = this.waScaleManager.zoomModifier;
|
||||||
|
const zoomModifierChange = targetZoomModifier - currentZoomModifier;
|
||||||
|
this.camera.stopFollow();
|
||||||
|
this.cameraFollowTarget = undefined;
|
||||||
|
this.camera.pan(
|
||||||
|
focusOn.x + focusOn.width * 0.5 * marginMult,
|
||||||
|
focusOn.y + focusOn.height * 0.5 * marginMult,
|
||||||
|
duration,
|
||||||
|
Easing.SineEaseOut,
|
||||||
|
true,
|
||||||
|
(camera, progress, x, y) => {
|
||||||
|
this.waScaleManager.zoomModifier = currentZoomModifier + progress * zoomModifierChange;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public leaveFocusMode(player: Player): void {
|
||||||
|
this.waScaleManager.setFocusTarget();
|
||||||
|
this.startFollow(player, 1000);
|
||||||
|
this.restoreZoom(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public startFollow(target: object | Phaser.GameObjects.GameObject, duration: number = 0): void {
|
||||||
|
this.cameraFollowTarget = target as { x: number; y: number };
|
||||||
|
this.setCameraMode(CameraMode.Follow);
|
||||||
|
if (duration === 0) {
|
||||||
|
this.camera.startFollow(target, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const oldPos = { x: this.camera.scrollX, y: this.camera.scrollY };
|
||||||
|
this.startFollowTween = this.scene.tweens.addCounter({
|
||||||
|
from: 0,
|
||||||
|
to: 1,
|
||||||
|
duration,
|
||||||
|
ease: Easing.SineEaseOut,
|
||||||
|
onUpdate: (tween: Phaser.Tweens.Tween) => {
|
||||||
|
if (!this.cameraFollowTarget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const shiftX =
|
||||||
|
(this.cameraFollowTarget.x - this.camera.worldView.width * 0.5 - oldPos.x) * tween.getValue();
|
||||||
|
const shiftY =
|
||||||
|
(this.cameraFollowTarget.y - this.camera.worldView.height * 0.5 - oldPos.y) * tween.getValue();
|
||||||
|
this.camera.setScroll(oldPos.x + shiftX, oldPos.y + shiftY);
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
this.camera.startFollow(target, true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the offset of the character compared to the center of the screen according to the layout manager
|
||||||
|
* (tries to put the character in the center of the remaining space if there is a discussion going on.
|
||||||
|
*/
|
||||||
|
public updateCameraOffset(array: Box): void {
|
||||||
|
const xCenter = (array.xEnd - array.xStart) / 2 + array.xStart;
|
||||||
|
const yCenter = (array.yEnd - array.yStart) / 2 + array.yStart;
|
||||||
|
|
||||||
|
const game = HtmlUtils.querySelectorOrFail<HTMLCanvasElement>("#game canvas");
|
||||||
|
// Let's put this in Game coordinates by applying the zoom level:
|
||||||
|
|
||||||
|
this.camera.setFollowOffset(
|
||||||
|
((xCenter - game.offsetWidth / 2) * window.devicePixelRatio) / this.scene.scale.zoom,
|
||||||
|
((yCenter - game.offsetHeight / 2) * window.devicePixelRatio) / this.scene.scale.zoom
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isCameraLocked(): boolean {
|
||||||
|
return this.cameraMode === CameraMode.Focus;
|
||||||
|
}
|
||||||
|
|
||||||
|
private setCameraMode(mode: CameraMode): void {
|
||||||
|
if (this.cameraMode === mode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.cameraMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private restoreZoom(duration: number = 0): void {
|
||||||
|
if (duration === 0) {
|
||||||
|
this.waScaleManager.zoomModifier = this.waScaleManager.getSaveZoom();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.restoreZoomTween?.stop();
|
||||||
|
this.restoreZoomTween = this.scene.tweens.addCounter({
|
||||||
|
from: this.waScaleManager.zoomModifier,
|
||||||
|
to: this.waScaleManager.getSaveZoom(),
|
||||||
|
duration,
|
||||||
|
ease: Easing.SineEaseOut,
|
||||||
|
onUpdate: (tween: Phaser.Tweens.Tween) => {
|
||||||
|
this.waScaleManager.zoomModifier = tween.getValue();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private initCamera() {
|
||||||
|
this.camera = this.scene.cameras.main;
|
||||||
|
this.camera.setBounds(0, 0, this.cameraBounds.x, this.cameraBounds.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bindEventHandlers(): void {
|
||||||
|
this.scene.game.events.on(
|
||||||
|
"wa-scale-manager:refresh-focus-on-target",
|
||||||
|
(focusOn: { x: number; y: number; width: number; height: number }) => {
|
||||||
|
if (!focusOn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.camera.centerOn(focusOn.x + focusOn.width * 0.5, focusOn.y + focusOn.height * 0.5);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,15 @@
|
|||||||
import type { ITiledMap, ITiledMapLayer, ITiledMapProperty } from "../Map/ITiledMap";
|
import type {
|
||||||
|
ITiledMap,
|
||||||
|
ITiledMapLayer,
|
||||||
|
ITiledMapObject,
|
||||||
|
ITiledMapObjectLayer,
|
||||||
|
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";
|
||||||
import { GameMapProperties } from "./GameMapProperties";
|
import { GameMapProperties } from "./GameMapProperties";
|
||||||
|
import { MathUtils } from "../../Utils/MathUtils";
|
||||||
|
|
||||||
export type PropertyChangeCallback = (
|
export type PropertyChangeCallback = (
|
||||||
newValue: string | number | boolean | undefined,
|
newValue: string | number | boolean | undefined,
|
||||||
@@ -15,24 +22,48 @@ export type layerChangeCallback = (
|
|||||||
allLayersOnNewPosition: Array<ITiledMapLayer>
|
allLayersOnNewPosition: Array<ITiledMapLayer>
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
|
export type zoneChangeCallback = (
|
||||||
|
zonesChangedByAction: Array<ITiledMapObject>,
|
||||||
|
allZonesOnNewPosition: Array<ITiledMapObject>
|
||||||
|
) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper around a ITiledMap interface to provide additional capabilities.
|
* A wrapper around a ITiledMap interface to provide additional capabilities.
|
||||||
* It is used to handle layer properties.
|
* It is used to handle layer properties.
|
||||||
*/
|
*/
|
||||||
export class GameMap {
|
export class GameMap {
|
||||||
// oldKey is the index of the previous tile.
|
/**
|
||||||
|
* oldKey is the index of the previous tile.
|
||||||
|
*/
|
||||||
private oldKey: number | undefined;
|
private oldKey: number | undefined;
|
||||||
// key is the index of the current tile.
|
/**
|
||||||
|
* key is the index of the current tile.
|
||||||
|
*/
|
||||||
private key: number | undefined;
|
private key: number | undefined;
|
||||||
|
/**
|
||||||
|
* oldPosition is the previous position of the player.
|
||||||
|
*/
|
||||||
|
private oldPosition: { x: number; y: number } | undefined;
|
||||||
|
/**
|
||||||
|
* position is the current position of the player.
|
||||||
|
*/
|
||||||
|
private position: { x: number; y: number } | undefined;
|
||||||
|
|
||||||
private lastProperties = new Map<string, string | boolean | number>();
|
private lastProperties = new Map<string, string | boolean | number>();
|
||||||
private propertiesChangeCallbacks = new Map<string, Array<PropertyChangeCallback>>();
|
private propertiesChangeCallbacks = new Map<string, Array<PropertyChangeCallback>>();
|
||||||
|
|
||||||
private enterLayerCallbacks = Array<layerChangeCallback>();
|
private enterLayerCallbacks = Array<layerChangeCallback>();
|
||||||
private leaveLayerCallbacks = Array<layerChangeCallback>();
|
private leaveLayerCallbacks = Array<layerChangeCallback>();
|
||||||
|
private enterZoneCallbacks = Array<zoneChangeCallback>();
|
||||||
|
private leaveZoneCallbacks = Array<zoneChangeCallback>();
|
||||||
|
|
||||||
private tileNameMap = new Map<string, number>();
|
private tileNameMap = new Map<string, number>();
|
||||||
|
|
||||||
private tileSetPropertyMap: { [tile_index: number]: Array<ITiledMapProperty> } = {};
|
private tileSetPropertyMap: { [tile_index: number]: Array<ITiledMapProperty> } = {};
|
||||||
public readonly flatLayers: ITiledMapLayer[];
|
public readonly flatLayers: ITiledMapLayer[];
|
||||||
|
public readonly tiledObjects: ITiledMapObject[];
|
||||||
public readonly phaserLayers: TilemapLayer[] = [];
|
public readonly phaserLayers: TilemapLayer[] = [];
|
||||||
|
public readonly zones: ITiledMapObject[] = [];
|
||||||
|
|
||||||
public exitUrls: Array<string> = [];
|
public exitUrls: Array<string> = [];
|
||||||
|
|
||||||
@@ -44,6 +75,9 @@ export class GameMap {
|
|||||||
terrains: Array<Phaser.Tilemaps.Tileset>
|
terrains: Array<Phaser.Tilemaps.Tileset>
|
||||||
) {
|
) {
|
||||||
this.flatLayers = flattenGroupLayersMap(map);
|
this.flatLayers = flattenGroupLayersMap(map);
|
||||||
|
this.tiledObjects = this.getObjectsFromLayers(this.flatLayers);
|
||||||
|
this.zones = this.tiledObjects.filter((object) => object.type === "zone");
|
||||||
|
|
||||||
let depth = -2;
|
let depth = -2;
|
||||||
for (const layer of this.flatLayers) {
|
for (const layer of this.flatLayers) {
|
||||||
if (layer.type === "tilelayer") {
|
if (layer.type === "tilelayer") {
|
||||||
@@ -88,6 +122,10 @@ export class GameMap {
|
|||||||
* This will trigger events if properties are changing.
|
* This will trigger events if properties are changing.
|
||||||
*/
|
*/
|
||||||
public setPosition(x: number, y: number) {
|
public setPosition(x: number, y: number) {
|
||||||
|
this.oldPosition = this.position;
|
||||||
|
this.position = { x, y };
|
||||||
|
this.triggerZonesChange();
|
||||||
|
|
||||||
this.oldKey = this.key;
|
this.oldKey = this.key;
|
||||||
|
|
||||||
const xMap = Math.floor(x / this.map.tilewidth);
|
const xMap = Math.floor(x / this.map.tilewidth);
|
||||||
@@ -126,7 +164,7 @@ export class GameMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private triggerLayersChange() {
|
private triggerLayersChange(): void {
|
||||||
const layersByOldKey = this.oldKey ? this.getLayersByKey(this.oldKey) : [];
|
const layersByOldKey = this.oldKey ? this.getLayersByKey(this.oldKey) : [];
|
||||||
const layersByNewKey = this.key ? this.getLayersByKey(this.key) : [];
|
const layersByNewKey = this.key ? this.getLayersByKey(this.key) : [];
|
||||||
|
|
||||||
@@ -155,6 +193,53 @@ export class GameMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We use Tiled Objects with type "zone" as zones with defined x, y, width and height for easier event triggering.
|
||||||
|
*/
|
||||||
|
private triggerZonesChange(): void {
|
||||||
|
const zonesByOldPosition = this.oldPosition
|
||||||
|
? this.zones.filter((zone) => {
|
||||||
|
if (!this.oldPosition) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return MathUtils.isOverlappingWithRectangle(this.oldPosition, zone);
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const zonesByNewPosition = this.position
|
||||||
|
? this.zones.filter((zone) => {
|
||||||
|
if (!this.position) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return MathUtils.isOverlappingWithRectangle(this.position, zone);
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const enterZones = new Set(zonesByNewPosition);
|
||||||
|
const leaveZones = new Set(zonesByOldPosition);
|
||||||
|
|
||||||
|
enterZones.forEach((zone) => {
|
||||||
|
if (leaveZones.has(zone)) {
|
||||||
|
leaveZones.delete(zone);
|
||||||
|
enterZones.delete(zone);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (enterZones.size > 0) {
|
||||||
|
const zonesArray = Array.from(enterZones);
|
||||||
|
for (const callback of this.enterZoneCallbacks) {
|
||||||
|
callback(zonesArray, zonesByNewPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leaveZones.size > 0) {
|
||||||
|
const zonesArray = Array.from(leaveZones);
|
||||||
|
for (const callback of this.leaveZoneCallbacks) {
|
||||||
|
callback(zonesArray, zonesByNewPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public getCurrentProperties(): Map<string, string | boolean | number> {
|
public getCurrentProperties(): Map<string, string | boolean | number> {
|
||||||
return this.lastProperties;
|
return this.lastProperties;
|
||||||
}
|
}
|
||||||
@@ -251,6 +336,20 @@ export class GameMap {
|
|||||||
this.leaveLayerCallbacks.push(callback);
|
this.leaveLayerCallbacks.push(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a callback called when the user moves inside another zone.
|
||||||
|
*/
|
||||||
|
public onEnterZone(callback: zoneChangeCallback) {
|
||||||
|
this.enterZoneCallbacks.push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a callback called when the user moves outside another zone.
|
||||||
|
*/
|
||||||
|
public onLeaveZone(callback: zoneChangeCallback) {
|
||||||
|
this.leaveZoneCallbacks.push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
public findLayer(layerName: string): ITiledMapLayer | undefined {
|
public findLayer(layerName: string): ITiledMapLayer | undefined {
|
||||||
return this.flatLayers.find((layer) => layer.name === layerName);
|
return this.flatLayers.find((layer) => layer.name === layerName);
|
||||||
}
|
}
|
||||||
@@ -378,4 +477,17 @@ export class GameMap {
|
|||||||
this.trigger(oldPropName, oldPropValue, undefined, emptyProps);
|
this.trigger(oldPropName, oldPropValue, undefined, emptyProps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getObjectsFromLayers(layers: ITiledMapLayer[]): ITiledMapObject[] {
|
||||||
|
const objects: ITiledMapObject[] = [];
|
||||||
|
|
||||||
|
const objectLayers = layers.filter((layer) => layer.type === "objectgroup");
|
||||||
|
for (const objectLayer of objectLayers) {
|
||||||
|
if (objectLayer.type === "objectgroup") {
|
||||||
|
objects.push(...objectLayer.objects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return objects;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,54 @@
|
|||||||
import type { Subscription } from "rxjs";
|
import type { Subscription } from "rxjs";
|
||||||
|
import AnimatedTiles from "phaser-animated-tiles";
|
||||||
|
import { Queue } from "queue-typescript";
|
||||||
|
import { get } from "svelte/store";
|
||||||
|
|
||||||
import { userMessageManager } from "../../Administration/UserMessageManager";
|
import { userMessageManager } from "../../Administration/UserMessageManager";
|
||||||
import { iframeListener } from "../../Api/IframeListener";
|
|
||||||
import { connectionManager } from "../../Connexion/ConnectionManager";
|
import { connectionManager } from "../../Connexion/ConnectionManager";
|
||||||
|
import { CoWebsite, coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
|
||||||
|
import { urlManager } from "../../Url/UrlManager";
|
||||||
|
import { mediaManager } from "../../WebRtc/MediaManager";
|
||||||
|
import { UserInputManager } from "../UserInput/UserInputManager";
|
||||||
|
import { gameManager } from "./GameManager";
|
||||||
|
import { touchScreenManager } from "../../Touch/TouchScreenManager";
|
||||||
|
import { PinchManager } from "../UserInput/PinchManager";
|
||||||
|
import { waScaleManager } from "../Services/WaScaleManager";
|
||||||
|
import { EmoteManager } from "./EmoteManager";
|
||||||
|
import { soundManager } from "./SoundManager";
|
||||||
|
import { SharedVariablesManager } from "./SharedVariablesManager";
|
||||||
|
import { EmbeddedWebsiteManager } from "./EmbeddedWebsiteManager";
|
||||||
|
|
||||||
|
import { lazyLoadPlayerCharacterTextures, loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager";
|
||||||
|
import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager";
|
||||||
|
import { ON_ACTION_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager";
|
||||||
|
import { iframeListener } from "../../Api/IframeListener";
|
||||||
|
import { DEBUG_MODE, JITSI_PRIVATE_MODE, MAX_PER_GROUP, POSITION_DELAY } from "../../Enum/EnvironmentVariable";
|
||||||
|
import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils";
|
||||||
|
import { Room } from "../../Connexion/Room";
|
||||||
|
import { jitsiFactory } from "../../WebRtc/JitsiFactory";
|
||||||
|
import { TextureError } from "../../Exception/TextureError";
|
||||||
|
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||||
|
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
||||||
|
import { SimplePeer } from "../../WebRtc/SimplePeer";
|
||||||
|
import { Loader } from "../Components/Loader";
|
||||||
|
import { RemotePlayer } from "../Entity/RemotePlayer";
|
||||||
|
import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene";
|
||||||
|
import { PlayerAnimationDirections } from "../Player/Animation";
|
||||||
|
import { hasMovedEventName, Player, requestEmoteEventName } from "../Player/Player";
|
||||||
|
import { ErrorSceneName } from "../Reconnecting/ErrorScene";
|
||||||
|
import { ReconnectingSceneName } from "../Reconnecting/ReconnectingScene";
|
||||||
|
import { GameMap } from "./GameMap";
|
||||||
|
import { PlayerMovement } from "./PlayerMovement";
|
||||||
|
import { PlayersPositionInterpolator } from "./PlayersPositionInterpolator";
|
||||||
|
import { worldFullMessageStream } from "../../Connexion/WorldFullMessageStream";
|
||||||
|
import { DirtyScene } from "./DirtyScene";
|
||||||
|
import { TextUtils } from "../Components/TextUtils";
|
||||||
|
import { joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey } from "../Components/MobileJoystick";
|
||||||
|
import { StartPositionCalculator } from "./StartPositionCalculator";
|
||||||
|
import { PropertyUtils } from "../Map/PropertyUtils";
|
||||||
|
import { GameMapPropertiesListener } from "./GameMapPropertiesListener";
|
||||||
|
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
||||||
|
import { GameMapProperties } from "./GameMapProperties";
|
||||||
import type {
|
import type {
|
||||||
GroupCreatedUpdatedMessageInterface,
|
GroupCreatedUpdatedMessageInterface,
|
||||||
MessageUserJoined,
|
MessageUserJoined,
|
||||||
@@ -12,84 +59,35 @@ import type {
|
|||||||
PositionInterface,
|
PositionInterface,
|
||||||
RoomJoinedMessageInterface,
|
RoomJoinedMessageInterface,
|
||||||
} from "../../Connexion/ConnexionModels";
|
} from "../../Connexion/ConnexionModels";
|
||||||
import { DEBUG_MODE, JITSI_PRIVATE_MODE, MAX_PER_GROUP, POSITION_DELAY } from "../../Enum/EnvironmentVariable";
|
|
||||||
|
|
||||||
import { Queue } from "queue-typescript";
|
|
||||||
import { Box, ON_ACTION_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager";
|
|
||||||
import { CoWebsite, coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
|
|
||||||
import type { UserMovedMessage } from "../../Messages/generated/messages_pb";
|
import type { UserMovedMessage } from "../../Messages/generated/messages_pb";
|
||||||
import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils";
|
|
||||||
import type { RoomConnection } from "../../Connexion/RoomConnection";
|
import type { RoomConnection } from "../../Connexion/RoomConnection";
|
||||||
import { Room } from "../../Connexion/Room";
|
|
||||||
import { jitsiFactory } from "../../WebRtc/JitsiFactory";
|
|
||||||
import { urlManager } from "../../Url/UrlManager";
|
|
||||||
import { TextureError } from "../../Exception/TextureError";
|
|
||||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
|
||||||
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
|
||||||
import { mediaManager } from "../../WebRtc/MediaManager";
|
|
||||||
import { SimplePeer } from "../../WebRtc/SimplePeer";
|
|
||||||
import { Loader } from "../Components/Loader";
|
|
||||||
import { lazyLoadPlayerCharacterTextures, loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager";
|
|
||||||
import { RemotePlayer } from "../Entity/RemotePlayer";
|
|
||||||
import type { ActionableItem } from "../Items/ActionableItem";
|
import type { ActionableItem } from "../Items/ActionableItem";
|
||||||
import type { ItemFactoryInterface } from "../Items/ItemFactoryInterface";
|
import type { ItemFactoryInterface } from "../Items/ItemFactoryInterface";
|
||||||
import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene";
|
|
||||||
import type { ITiledMap, ITiledMapLayer, ITiledMapProperty, ITiledMapObject, ITiledTileSet } from "../Map/ITiledMap";
|
import type { ITiledMap, ITiledMapLayer, ITiledMapProperty, ITiledMapObject, ITiledTileSet } from "../Map/ITiledMap";
|
||||||
import { PlayerAnimationDirections } from "../Player/Animation";
|
|
||||||
import { hasMovedEventName, Player, requestEmoteEventName } from "../Player/Player";
|
|
||||||
import { ErrorSceneName } from "../Reconnecting/ErrorScene";
|
|
||||||
import { ReconnectingSceneName } from "../Reconnecting/ReconnectingScene";
|
|
||||||
import { UserInputManager } from "../UserInput/UserInputManager";
|
|
||||||
import type { AddPlayerInterface } from "./AddPlayerInterface";
|
import type { AddPlayerInterface } from "./AddPlayerInterface";
|
||||||
import { gameManager } from "./GameManager";
|
import { CameraManager } from "./CameraManager";
|
||||||
import { GameMap } from "./GameMap";
|
import type { HasPlayerMovedEvent } from "../../Api/Events/HasPlayerMovedEvent";
|
||||||
import { PlayerMovement } from "./PlayerMovement";
|
import type { Character } from "../Entity/Character";
|
||||||
import { PlayersPositionInterpolator } from "./PlayersPositionInterpolator";
|
|
||||||
|
import { peerStore } from "../../Stores/PeerStore";
|
||||||
|
import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStore";
|
||||||
|
import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
|
||||||
|
import { playersStore } from "../../Stores/PlayersStore";
|
||||||
|
import { emoteStore, emoteMenuStore } from "../../Stores/EmoteStore";
|
||||||
|
import { userIsAdminStore } from "../../Stores/GameStore";
|
||||||
|
import { contactPageStore } from "../../Stores/MenuStore";
|
||||||
|
import { audioManagerFileStore, audioManagerVisibilityStore } from "../../Stores/AudioManagerStore";
|
||||||
|
|
||||||
|
import EVENT_TYPE = Phaser.Scenes.Events;
|
||||||
import Texture = Phaser.Textures.Texture;
|
import Texture = Phaser.Textures.Texture;
|
||||||
import Sprite = Phaser.GameObjects.Sprite;
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
import CanvasTexture = Phaser.Textures.CanvasTexture;
|
import CanvasTexture = Phaser.Textures.CanvasTexture;
|
||||||
import GameObject = Phaser.GameObjects.GameObject;
|
import GameObject = Phaser.GameObjects.GameObject;
|
||||||
import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
|
|
||||||
import DOMElement = Phaser.GameObjects.DOMElement;
|
import DOMElement = Phaser.GameObjects.DOMElement;
|
||||||
import { worldFullMessageStream } from "../../Connexion/WorldFullMessageStream";
|
|
||||||
import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager";
|
|
||||||
import { DirtyScene } from "./DirtyScene";
|
|
||||||
import { TextUtils } from "../Components/TextUtils";
|
|
||||||
import { touchScreenManager } from "../../Touch/TouchScreenManager";
|
|
||||||
import { PinchManager } from "../UserInput/PinchManager";
|
|
||||||
import { joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey } from "../Components/MobileJoystick";
|
|
||||||
import { waScaleManager } from "../Services/WaScaleManager";
|
|
||||||
import { EmoteManager } from "./EmoteManager";
|
|
||||||
import EVENT_TYPE = Phaser.Scenes.Events;
|
|
||||||
import type { HasPlayerMovedEvent } from "../../Api/Events/HasPlayerMovedEvent";
|
|
||||||
|
|
||||||
import AnimatedTiles from "phaser-animated-tiles";
|
|
||||||
import { StartPositionCalculator } from "./StartPositionCalculator";
|
|
||||||
import { soundManager } from "./SoundManager";
|
|
||||||
import { peerStore, screenSharingPeerStore } from "../../Stores/PeerStore";
|
|
||||||
import { videoFocusStore } from "../../Stores/VideoFocusStore";
|
|
||||||
import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStore";
|
|
||||||
import { SharedVariablesManager } from "./SharedVariablesManager";
|
|
||||||
import { playersStore } from "../../Stores/PlayersStore";
|
|
||||||
import { chatVisibilityStore } from "../../Stores/ChatStore";
|
|
||||||
import { emoteStore, emoteMenuStore } from "../../Stores/EmoteStore";
|
|
||||||
import {
|
|
||||||
audioManagerFileStore,
|
|
||||||
audioManagerVisibilityStore,
|
|
||||||
audioManagerVolumeStore,
|
|
||||||
} from "../../Stores/AudioManagerStore";
|
|
||||||
import { PropertyUtils } from "../Map/PropertyUtils";
|
|
||||||
import Tileset = Phaser.Tilemaps.Tileset;
|
import Tileset = Phaser.Tilemaps.Tileset;
|
||||||
import { userIsAdminStore } from "../../Stores/GameStore";
|
|
||||||
import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
|
|
||||||
import { EmbeddedWebsiteManager } from "./EmbeddedWebsiteManager";
|
|
||||||
import { GameMapPropertiesListener } from "./GameMapPropertiesListener";
|
|
||||||
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
|
||||||
import { get } from "svelte/store";
|
|
||||||
import { contactPageStore } from "../../Stores/MenuStore";
|
|
||||||
import { GameMapProperties } from "./GameMapProperties";
|
|
||||||
import SpriteSheetFile = Phaser.Loader.FileTypes.SpriteSheetFile;
|
import SpriteSheetFile = Phaser.Loader.FileTypes.SpriteSheetFile;
|
||||||
|
import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
|
||||||
|
import { MapStore } from "../../Stores/Utils/MapStore";
|
||||||
export interface GameSceneInitInterface {
|
export interface GameSceneInitInterface {
|
||||||
initPosition: PointInterface | null;
|
initPosition: PointInterface | null;
|
||||||
reconnecting: boolean;
|
reconnecting: boolean;
|
||||||
@@ -129,7 +127,7 @@ export class GameScene extends DirtyScene {
|
|||||||
Terrains: Array<Phaser.Tilemaps.Tileset>;
|
Terrains: Array<Phaser.Tilemaps.Tileset>;
|
||||||
CurrentPlayer!: Player;
|
CurrentPlayer!: Player;
|
||||||
MapPlayers!: Phaser.Physics.Arcade.Group;
|
MapPlayers!: Phaser.Physics.Arcade.Group;
|
||||||
MapPlayersByKey: Map<number, RemotePlayer> = new Map<number, RemotePlayer>();
|
MapPlayersByKey: MapStore<number, RemotePlayer> = new MapStore<number, RemotePlayer>();
|
||||||
Map!: Phaser.Tilemaps.Tilemap;
|
Map!: Phaser.Tilemaps.Tilemap;
|
||||||
Objects!: Array<Phaser.Physics.Arcade.Sprite>;
|
Objects!: Array<Phaser.Physics.Arcade.Sprite>;
|
||||||
mapFile!: ITiledMap;
|
mapFile!: ITiledMap;
|
||||||
@@ -198,6 +196,7 @@ export class GameScene extends DirtyScene {
|
|||||||
private pinchManager: PinchManager | undefined;
|
private pinchManager: PinchManager | undefined;
|
||||||
private mapTransitioning: boolean = false; //used to prevent transitions happening at the same time.
|
private mapTransitioning: boolean = false; //used to prevent transitions happening at the same time.
|
||||||
private emoteManager!: EmoteManager;
|
private emoteManager!: EmoteManager;
|
||||||
|
private cameraManager!: CameraManager;
|
||||||
private preloading: boolean = true;
|
private preloading: boolean = true;
|
||||||
private startPositionCalculator!: StartPositionCalculator;
|
private startPositionCalculator!: StartPositionCalculator;
|
||||||
private sharedVariablesManager!: SharedVariablesManager;
|
private sharedVariablesManager!: SharedVariablesManager;
|
||||||
@@ -552,7 +551,13 @@ export class GameScene extends DirtyScene {
|
|||||||
this.createCurrentPlayer();
|
this.createCurrentPlayer();
|
||||||
this.removeAllRemotePlayers(); //cleanup the list of remote players in case the scene was rebooted
|
this.removeAllRemotePlayers(); //cleanup the list of remote players in case the scene was rebooted
|
||||||
|
|
||||||
this.initCamera();
|
this.cameraManager = new CameraManager(
|
||||||
|
this,
|
||||||
|
{ x: this.Map.widthInPixels, y: this.Map.heightInPixels },
|
||||||
|
waScaleManager
|
||||||
|
);
|
||||||
|
biggestAvailableAreaStore.recompute();
|
||||||
|
this.cameraManager.startFollow(this.CurrentPlayer);
|
||||||
|
|
||||||
this.animatedTiles.init(this.Map);
|
this.animatedTiles.init(this.Map);
|
||||||
this.events.on("tileanimationupdate", () => (this.dirty = true));
|
this.events.on("tileanimationupdate", () => (this.dirty = true));
|
||||||
@@ -593,7 +598,7 @@ export class GameScene extends DirtyScene {
|
|||||||
|
|
||||||
// From now, this game scene will be notified of reposition events
|
// From now, this game scene will be notified of reposition events
|
||||||
this.biggestAvailableAreaStoreUnsubscribe = biggestAvailableAreaStore.subscribe((box) =>
|
this.biggestAvailableAreaStoreUnsubscribe = biggestAvailableAreaStore.subscribe((box) =>
|
||||||
this.updateCameraOffset(box)
|
this.cameraManager.updateCameraOffset(box)
|
||||||
);
|
);
|
||||||
|
|
||||||
new GameMapPropertiesListener(this, this.gameMap).register();
|
new GameMapPropertiesListener(this, this.gameMap).register();
|
||||||
@@ -646,7 +651,7 @@ export class GameScene extends DirtyScene {
|
|||||||
* Initializes the connection to Pusher.
|
* Initializes the connection to Pusher.
|
||||||
*/
|
*/
|
||||||
private connect(): void {
|
private connect(): void {
|
||||||
const camera = this.cameras.main;
|
const camera = this.cameraManager.getCamera();
|
||||||
|
|
||||||
connectionManager
|
connectionManager
|
||||||
.connectToRoomSocket(
|
.connectToRoomSocket(
|
||||||
@@ -668,7 +673,6 @@ export class GameScene extends DirtyScene {
|
|||||||
this.connection = onConnect.connection;
|
this.connection = onConnect.connection;
|
||||||
|
|
||||||
playersStore.connectToRoomConnection(this.connection);
|
playersStore.connectToRoomConnection(this.connection);
|
||||||
|
|
||||||
userIsAdminStore.set(this.connection.hasTag("admin"));
|
userIsAdminStore.set(this.connection.hasTag("admin"));
|
||||||
|
|
||||||
this.connection.onUserJoins((message: MessageUserJoined) => {
|
this.connection.onUserJoins((message: MessageUserJoined) => {
|
||||||
@@ -781,6 +785,42 @@ export class GameScene extends DirtyScene {
|
|||||||
iframeListener.sendLeaveLayerEvent(layer.name);
|
iframeListener.sendLeaveLayerEvent(layer.name);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.gameMap.onEnterZone((zones) => {
|
||||||
|
for (const zone of zones) {
|
||||||
|
const focusable = zone.properties?.find((property) => property.name === "focusable");
|
||||||
|
if (focusable && focusable.value === true) {
|
||||||
|
const zoomMargin = zone.properties?.find((property) => property.name === "zoom_margin");
|
||||||
|
this.cameraManager.enterFocusMode(
|
||||||
|
zone,
|
||||||
|
zoomMargin ? Math.max(0, Number(zoomMargin.value)) : undefined
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zones.forEach((zone) => {
|
||||||
|
iframeListener.sendEnterZoneEvent(zone.name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.gameMap.onLeaveZone((zones) => {
|
||||||
|
for (const zone of zones) {
|
||||||
|
const focusable = zone.properties?.find((property) => property.name === "focusable");
|
||||||
|
if (focusable && focusable.value === true) {
|
||||||
|
this.cameraManager.leaveFocusMode(this.CurrentPlayer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zones.forEach((zone) => {
|
||||||
|
iframeListener.sendLeaveZoneEvent(zone.name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// this.gameMap.onLeaveLayer((layers) => {
|
||||||
|
// layers.forEach((layer) => {
|
||||||
|
// iframeListener.sendLeaveLayerEvent(layer.name);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1388,6 +1428,7 @@ export class GameScene extends DirtyScene {
|
|||||||
this.userInputManager.destroy();
|
this.userInputManager.destroy();
|
||||||
this.pinchManager?.destroy();
|
this.pinchManager?.destroy();
|
||||||
this.emoteManager.destroy();
|
this.emoteManager.destroy();
|
||||||
|
this.cameraManager.destroy();
|
||||||
this.peerStoreUnsubscribe();
|
this.peerStoreUnsubscribe();
|
||||||
this.emoteUnsubscribe();
|
this.emoteUnsubscribe();
|
||||||
this.emoteMenuUnsubscribe();
|
this.emoteMenuUnsubscribe();
|
||||||
@@ -1419,7 +1460,7 @@ export class GameScene extends DirtyScene {
|
|||||||
|
|
||||||
this.MapPlayers.remove(player);
|
this.MapPlayers.remove(player);
|
||||||
});
|
});
|
||||||
this.MapPlayersByKey = new Map<number, RemotePlayer>();
|
this.MapPlayersByKey.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private getExitUrl(layer: ITiledMapLayer): string | undefined {
|
private getExitUrl(layer: ITiledMapLayer): string | undefined {
|
||||||
@@ -1477,13 +1518,6 @@ export class GameScene extends DirtyScene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo: in a dedicated class/function?
|
|
||||||
initCamera() {
|
|
||||||
this.cameras.main.setBounds(0, 0, this.Map.widthInPixels, this.Map.heightInPixels);
|
|
||||||
this.cameras.main.startFollow(this.CurrentPlayer, true);
|
|
||||||
biggestAvailableAreaStore.recompute();
|
|
||||||
}
|
|
||||||
|
|
||||||
createCollisionWithPlayer() {
|
createCollisionWithPlayer() {
|
||||||
//add collision layer
|
//add collision layer
|
||||||
for (const phaserLayer of this.gameMap.phaserLayers) {
|
for (const phaserLayer of this.gameMap.phaserLayers) {
|
||||||
@@ -1875,23 +1909,6 @@ export class GameScene extends DirtyScene {
|
|||||||
biggestAvailableAreaStore.recompute();
|
biggestAvailableAreaStore.recompute();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the offset of the character compared to the center of the screen according to the layout manager
|
|
||||||
* (tries to put the character in the center of the remaining space if there is a discussion going on.
|
|
||||||
*/
|
|
||||||
private updateCameraOffset(array: Box): void {
|
|
||||||
const xCenter = (array.xEnd - array.xStart) / 2 + array.xStart;
|
|
||||||
const yCenter = (array.yEnd - array.yStart) / 2 + array.yStart;
|
|
||||||
|
|
||||||
const game = HtmlUtils.querySelectorOrFail<HTMLCanvasElement>("#game canvas");
|
|
||||||
// Let's put this in Game coordinates by applying the zoom level:
|
|
||||||
|
|
||||||
this.cameras.main.setFollowOffset(
|
|
||||||
((xCenter - game.offsetWidth / 2) * window.devicePixelRatio) / this.scale.zoom,
|
|
||||||
((yCenter - game.offsetHeight / 2) * window.devicePixelRatio) / this.scale.zoom
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public startJitsi(roomName: string, jwt?: string): void {
|
public startJitsi(roomName: string, jwt?: string): void {
|
||||||
const allProps = this.gameMap.getCurrentProperties();
|
const allProps = this.gameMap.getCurrentProperties();
|
||||||
const jitsiConfig = this.safeParseJSONstring(
|
const jitsiConfig = this.safeParseJSONstring(
|
||||||
@@ -2001,6 +2018,9 @@ export class GameScene extends DirtyScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
zoomByFactor(zoomFactor: number) {
|
zoomByFactor(zoomFactor: number) {
|
||||||
|
if (this.cameraManager.isCameraLocked()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
waScaleManager.zoomModifier *= zoomFactor;
|
waScaleManager.zoomModifier *= zoomFactor;
|
||||||
biggestAvailableAreaStore.recompute();
|
biggestAvailableAreaStore.recompute();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { RoomConnection } from "../../Connexion/RoomConnection";
|
import type { RoomConnection } from "../../Connexion/RoomConnection";
|
||||||
import { iframeListener } from "../../Api/IframeListener";
|
import { iframeListener } from "../../Api/IframeListener";
|
||||||
import type { GameMap } from "./GameMap";
|
import type { GameMap } from "./GameMap";
|
||||||
import type { ITiledMapLayer, ITiledMapObject, ITiledMapObjectLayer } from "../Map/ITiledMap";
|
import type { ITiledMapLayer, ITiledMapObject } from "../Map/ITiledMap";
|
||||||
import { GameMapProperties } from "./GameMapProperties";
|
import { GameMapProperties } from "./GameMapProperties";
|
||||||
|
|
||||||
interface Variable {
|
interface Variable {
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
export class TexturesHelper {
|
||||||
|
public static async getSnapshot(
|
||||||
|
scene: Phaser.Scene,
|
||||||
|
...sprites: { sprite: Phaser.GameObjects.Sprite; frame?: string | number }[]
|
||||||
|
): Promise<string> {
|
||||||
|
const rt = scene.make.renderTexture({}, false);
|
||||||
|
try {
|
||||||
|
for (const { sprite, frame } of sprites) {
|
||||||
|
if (frame) {
|
||||||
|
sprite.setFrame(frame);
|
||||||
|
}
|
||||||
|
rt.draw(sprite, sprite.displayWidth * 0.5, sprite.displayHeight * 0.5);
|
||||||
|
}
|
||||||
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
rt.snapshot(
|
||||||
|
(url) => {
|
||||||
|
resolve((url as HTMLImageElement).src);
|
||||||
|
rt.destroy();
|
||||||
|
},
|
||||||
|
"image/png",
|
||||||
|
1
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
rt.destroy();
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
rt.destroy();
|
||||||
|
throw new Error("Could not get the snapshot");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import * as Phaser from "phaser";
|
import * as Phaser from "phaser";
|
||||||
import { Scene } from "phaser";
|
|
||||||
import Sprite = Phaser.GameObjects.Sprite;
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
import type { ITiledMapObject } from "../../Map/ITiledMap";
|
import type { ITiledMapObject } from "../../Map/ITiledMap";
|
||||||
import type { ItemFactoryInterface } from "../ItemFactoryInterface";
|
import type { ItemFactoryInterface } from "../ItemFactoryInterface";
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export class HdpiManager {
|
|||||||
/**
|
/**
|
||||||
* We only accept integer but we make an exception for 1.5
|
* We only accept integer but we make an exception for 1.5
|
||||||
*/
|
*/
|
||||||
private getOptimalZoomLevel(realPixelNumber: number): number {
|
public getOptimalZoomLevel(realPixelNumber: number): number {
|
||||||
const result = Math.sqrt(realPixelNumber / this.minRecommendedGamePixelsNumber);
|
const result = Math.sqrt(realPixelNumber / this.minRecommendedGamePixelsNumber);
|
||||||
if (1.5 <= result && result < 2) {
|
if (1.5 <= result && result < 2) {
|
||||||
return 1.5;
|
return 1.5;
|
||||||
|
|||||||
@@ -5,13 +5,15 @@ import type { Game } from "../Game/Game";
|
|||||||
import { ResizableScene } from "../Login/ResizableScene";
|
import { ResizableScene } from "../Login/ResizableScene";
|
||||||
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
||||||
|
|
||||||
class WaScaleManager {
|
export class WaScaleManager {
|
||||||
private hdpiManager: HdpiManager;
|
private hdpiManager: HdpiManager;
|
||||||
private scaleManager!: ScaleManager;
|
private scaleManager!: ScaleManager;
|
||||||
private game!: Game;
|
private game!: Game;
|
||||||
private actualZoom: number = 1;
|
private actualZoom: number = 1;
|
||||||
private _saveZoom: number = 1;
|
private _saveZoom: number = 1;
|
||||||
|
|
||||||
|
private focusTarget?: { x: number; y: number; width: number; height: number };
|
||||||
|
|
||||||
public constructor(private minGamePixelsNumber: number, private absoluteMinPixelNumber: number) {
|
public constructor(private minGamePixelsNumber: number, private absoluteMinPixelNumber: number) {
|
||||||
this.hdpiManager = new HdpiManager(minGamePixelsNumber, absoluteMinPixelNumber);
|
this.hdpiManager = new HdpiManager(minGamePixelsNumber, absoluteMinPixelNumber);
|
||||||
}
|
}
|
||||||
@@ -23,18 +25,14 @@ class WaScaleManager {
|
|||||||
|
|
||||||
public applyNewSize() {
|
public applyNewSize() {
|
||||||
const { width, height } = coWebsiteManager.getGameSize();
|
const { width, height } = coWebsiteManager.getGameSize();
|
||||||
|
const devicePixelRatio = window.devicePixelRatio ?? 1;
|
||||||
let devicePixelRatio = 1;
|
|
||||||
if (window.devicePixelRatio) {
|
|
||||||
devicePixelRatio = window.devicePixelRatio;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { game: gameSize, real: realSize } = this.hdpiManager.getOptimalGameSize({
|
const { game: gameSize, real: realSize } = this.hdpiManager.getOptimalGameSize({
|
||||||
width: width * devicePixelRatio,
|
width: width * devicePixelRatio,
|
||||||
height: height * devicePixelRatio,
|
height: height * devicePixelRatio,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.actualZoom = realSize.width / gameSize.width / devicePixelRatio;
|
this.actualZoom = realSize.width / gameSize.width / devicePixelRatio;
|
||||||
|
|
||||||
this.scaleManager.setZoom(realSize.width / gameSize.width / devicePixelRatio);
|
this.scaleManager.setZoom(realSize.width / gameSize.width / devicePixelRatio);
|
||||||
this.scaleManager.resize(gameSize.width, gameSize.height);
|
this.scaleManager.resize(gameSize.width, gameSize.height);
|
||||||
|
|
||||||
@@ -59,6 +57,34 @@ class WaScaleManager {
|
|||||||
this.game.markDirty();
|
this.game.markDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this in case of resizing while focusing on something
|
||||||
|
*/
|
||||||
|
public refreshFocusOnTarget(): void {
|
||||||
|
if (!this.focusTarget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.zoomModifier = this.getTargetZoomModifierFor(this.focusTarget.width, this.focusTarget.height);
|
||||||
|
this.game.events.emit("wa-scale-manager:refresh-focus-on-target", this.focusTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setFocusTarget(targetDimensions?: { x: number; y: number; width: number; height: number }): void {
|
||||||
|
this.focusTarget = targetDimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTargetZoomModifierFor(viewportWidth: number, viewportHeight: number) {
|
||||||
|
const { width: gameWidth, height: gameHeight } = coWebsiteManager.getGameSize();
|
||||||
|
const devicePixelRatio = window.devicePixelRatio ?? 1;
|
||||||
|
|
||||||
|
const { game: gameSize, real: realSize } = this.hdpiManager.getOptimalGameSize({
|
||||||
|
width: gameWidth * devicePixelRatio,
|
||||||
|
height: gameHeight * devicePixelRatio,
|
||||||
|
});
|
||||||
|
const desiredZoom = Math.min(realSize.width / viewportWidth, realSize.height / viewportHeight);
|
||||||
|
const realPixelNumber = gameWidth * devicePixelRatio * gameHeight * devicePixelRatio;
|
||||||
|
return desiredZoom / (this.hdpiManager.getOptimalZoomLevel(realPixelNumber) || 1);
|
||||||
|
}
|
||||||
|
|
||||||
public get zoomModifier(): number {
|
public get zoomModifier(): number {
|
||||||
return this.hdpiManager.zoomModifier;
|
return this.hdpiManager.zoomModifier;
|
||||||
}
|
}
|
||||||
@@ -72,6 +98,10 @@ class WaScaleManager {
|
|||||||
this._saveZoom = this.hdpiManager.zoomModifier;
|
this._saveZoom = this.hdpiManager.zoomModifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getSaveZoom(): number {
|
||||||
|
return this._saveZoom;
|
||||||
|
}
|
||||||
|
|
||||||
public restoreZoom(): void {
|
public restoreZoom(): void {
|
||||||
this.hdpiManager.zoomModifier = this._saveZoom;
|
this.hdpiManager.zoomModifier = this._saveZoom;
|
||||||
this.applyNewSize();
|
this.applyNewSize();
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import type { Readable } from "svelte/store";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A store that contains the player/companion avatar picture
|
||||||
|
*/
|
||||||
|
export type PictureStore = Readable<string | undefined>;
|
||||||
@@ -12,7 +12,7 @@ let idCount = 0;
|
|||||||
function createPlayersStore() {
|
function createPlayersStore() {
|
||||||
let players = new Map<number, PlayerInterface>();
|
let players = new Map<number, PlayerInterface>();
|
||||||
|
|
||||||
const { subscribe, set, update } = writable(players);
|
const { subscribe, set, update } = writable<Map<number, PlayerInterface>>(players);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe,
|
subscribe,
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
import type { Readable, Subscriber, Unsubscriber, Writable } from "svelte/store";
|
||||||
|
import { get, readable, writable } from "svelte/store";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is it a Map? Is it a Store? No! It's a MapStore!
|
||||||
|
*
|
||||||
|
* The MapStore behaves just like a regular JS Map, but... it is also a regular Svelte store.
|
||||||
|
*
|
||||||
|
* As a bonus, you can also get a store on any given key of the map.
|
||||||
|
*
|
||||||
|
* For instance:
|
||||||
|
*
|
||||||
|
* const mapStore = new MapStore<string, string>();
|
||||||
|
* mapStore.getStore('foo').subscribe((value) => {
|
||||||
|
* console.log('Foo key has been written to the store. New value: ', value);
|
||||||
|
* });
|
||||||
|
* mapStore.set('foo', 'bar');
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Even better, if the items stored in map contain stores, you can directly get the store to those values:
|
||||||
|
*
|
||||||
|
* const mapStore = new MapStore<string, {
|
||||||
|
* nestedStore: Readable<string>
|
||||||
|
* }>();
|
||||||
|
*
|
||||||
|
* mapStore.getNestedStore('foo', item => item.nestedStore).subscribe((value) => {
|
||||||
|
* console.log('Foo key has been written to the store or the nested store has been updated. New value: ', value);
|
||||||
|
* });
|
||||||
|
* mapStore.set('foo', {
|
||||||
|
* nestedStore: writable('bar')
|
||||||
|
* });
|
||||||
|
* // Whenever the nested store is updated OR the 'foo' key is overwritten, the store returned by mapStore.getNestedStore
|
||||||
|
* // will be triggered.
|
||||||
|
*/
|
||||||
|
export class MapStore<K, V> extends Map<K, V> implements Readable<Map<K, V>> {
|
||||||
|
private readonly store = writable(this);
|
||||||
|
private readonly storesByKey = new Map<K, Writable<V | undefined>>();
|
||||||
|
|
||||||
|
subscribe(run: Subscriber<Map<K, V>>, invalidate?: (value?: Map<K, V>) => void): Unsubscriber {
|
||||||
|
return this.store.subscribe(run, invalidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
super.clear();
|
||||||
|
this.store.set(this);
|
||||||
|
this.storesByKey.forEach((store) => {
|
||||||
|
store.set(undefined);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(key: K): boolean {
|
||||||
|
const result = super.delete(key);
|
||||||
|
if (result) {
|
||||||
|
this.store.set(this);
|
||||||
|
this.storesByKey.get(key)?.set(undefined);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key: K, value: V): this {
|
||||||
|
super.set(key, value);
|
||||||
|
this.store.set(this);
|
||||||
|
this.storesByKey.get(key)?.set(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStore(key: K): Readable<V | undefined> {
|
||||||
|
const store = writable(this.get(key), () => {
|
||||||
|
return () => {
|
||||||
|
// No more subscribers!
|
||||||
|
this.storesByKey.delete(key);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.storesByKey.set(key, store);
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an "inner" store inside a value stored in the map.
|
||||||
|
*/
|
||||||
|
getNestedStore<T>(key: K, accessor: (value: V) => Readable<T> | undefined): Readable<T | undefined> {
|
||||||
|
const initVal = this.get(key);
|
||||||
|
let initStore: Readable<T> | undefined;
|
||||||
|
let initStoreValue: T | undefined;
|
||||||
|
if (initVal) {
|
||||||
|
initStore = accessor(initVal);
|
||||||
|
if (initStore !== undefined) {
|
||||||
|
initStoreValue = get(initStore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return readable(initStoreValue, (set) => {
|
||||||
|
const storeByKey = this.getStore(key);
|
||||||
|
|
||||||
|
let unsubscribeDeepStore: Unsubscriber | undefined;
|
||||||
|
const unsubscribe = storeByKey.subscribe((newMapValue) => {
|
||||||
|
if (unsubscribeDeepStore) {
|
||||||
|
unsubscribeDeepStore();
|
||||||
|
}
|
||||||
|
if (newMapValue === undefined) {
|
||||||
|
set(undefined);
|
||||||
|
} else {
|
||||||
|
const deepValueStore = accessor(newMapValue);
|
||||||
|
if (deepValueStore !== undefined) {
|
||||||
|
set(get(deepValueStore));
|
||||||
|
|
||||||
|
unsubscribeDeepStore = deepValueStore.subscribe((value) => {
|
||||||
|
set(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsubscribe();
|
||||||
|
if (unsubscribeDeepStore) {
|
||||||
|
unsubscribeDeepStore();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
export class MathUtils {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param p Position to check.
|
||||||
|
* @param r Rectangle to check the overlap against.
|
||||||
|
* @returns true is overlapping
|
||||||
|
*/
|
||||||
|
public static isOverlappingWithRectangle(
|
||||||
|
p: { x: number; y: number },
|
||||||
|
r: { x: number; y: number; width: number; height: number }
|
||||||
|
): boolean {
|
||||||
|
return this.isBetween(p.x, r.x, r.x + r.width) && this.isBetween(p.y, r.y, r.y + r.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param value Value to check
|
||||||
|
* @param min inclusive min value
|
||||||
|
* @param max inclusive max value
|
||||||
|
* @returns true if value is in <min, max>
|
||||||
|
*/
|
||||||
|
public static isBetween(value: number, min: number, max: number): boolean {
|
||||||
|
return value >= min && value <= max;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -651,6 +651,7 @@ class CoWebsiteManager {
|
|||||||
private fire(): void {
|
private fire(): void {
|
||||||
this._onResize.next();
|
this._onResize.next();
|
||||||
waScaleManager.applyNewSize();
|
waScaleManager.applyNewSize();
|
||||||
|
waScaleManager.refreshFocusOnTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
private fullscreen(): void {
|
private fullscreen(): void {
|
||||||
|
|||||||
@@ -144,10 +144,12 @@ window.addEventListener("resize", function (event) {
|
|||||||
coWebsiteManager.resetStyleMain();
|
coWebsiteManager.resetStyleMain();
|
||||||
|
|
||||||
waScaleManager.applyNewSize();
|
waScaleManager.applyNewSize();
|
||||||
|
waScaleManager.refreshFocusOnTarget();
|
||||||
});
|
});
|
||||||
|
|
||||||
coWebsiteManager.onResize.subscribe(() => {
|
coWebsiteManager.onResize.subscribe(() => {
|
||||||
waScaleManager.applyNewSize();
|
waScaleManager.applyNewSize();
|
||||||
|
waScaleManager.refreshFocusOnTarget();
|
||||||
});
|
});
|
||||||
|
|
||||||
iframeListener.init();
|
iframeListener.init();
|
||||||
|
|||||||
@@ -21,3 +21,34 @@ export interface IVirtualJoystick extends Phaser.GameObjects.GameObject {
|
|||||||
visible: boolean;
|
visible: boolean;
|
||||||
createCursorKeys: () => CursorKeys;
|
createCursorKeys: () => CursorKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum Easing {
|
||||||
|
Linear = "Linear",
|
||||||
|
QuadEaseIn = "Quad.easeIn",
|
||||||
|
CubicEaseIn = "Cubic.easeIn",
|
||||||
|
QuartEaseIn = "Quart.easeIn",
|
||||||
|
QuintEaseIn = "Quint.easeIn",
|
||||||
|
SineEaseIn = "Sine.easeIn",
|
||||||
|
ExpoEaseIn = "Expo.easeIn",
|
||||||
|
CircEaseIn = "Circ.easeIn",
|
||||||
|
BackEaseIn = "Back.easeIn",
|
||||||
|
BounceEaseIn = "Bounce.easeIn",
|
||||||
|
QuadEaseOut = "Quad.easeOut",
|
||||||
|
CubicEaseOut = "Cubic.easeOut",
|
||||||
|
QuartEaseOut = "Quart.easeOut",
|
||||||
|
QuintEaseOut = "Quint.easeOut",
|
||||||
|
SineEaseOut = "Sine.easeOut",
|
||||||
|
ExpoEaseOut = "Expo.easeOut",
|
||||||
|
CircEaseOut = "Circ.easeOut",
|
||||||
|
BackEaseOut = "Back.easeOut",
|
||||||
|
BounceEaseOut = "Bounce.easeOut",
|
||||||
|
QuadEaseInOut = "Quad.easeInOut",
|
||||||
|
CubicEaseInOut = "Cubic.easeInOut",
|
||||||
|
QuartEaseInOut = "Quart.easeInOut",
|
||||||
|
QuintEaseInOut = "Quint.easeInOut",
|
||||||
|
SineEaseInOut = "Sine.easeInOut",
|
||||||
|
ExpoEaseInOut = "Expo.easeInOut",
|
||||||
|
CircEaseInOut = "Circ.easeInOut",
|
||||||
|
BackEaseInOut = "Back.easeInOut",
|
||||||
|
BounceEaseInOut = "Bounce.easeInOut",
|
||||||
|
}
|
||||||
|
|||||||
@@ -54,8 +54,7 @@ body .message-info.warning{
|
|||||||
left: calc(50% - 50px);
|
left: calc(50% - 50px);
|
||||||
top: calc(50% - 50px);
|
top: calc(50% - 50px);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding-top: 32px;
|
font-size: 14px;
|
||||||
font-size: 28px;
|
|
||||||
color: white;
|
color: white;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import "jasmine";
|
||||||
|
import {MapStore} from "../../../src/Stores/Utils/MapStore";
|
||||||
|
import type {Readable, Writable} from "svelte/store";
|
||||||
|
import {get, writable} from "svelte/store";
|
||||||
|
|
||||||
|
describe("Main store", () => {
|
||||||
|
it("Set / delete / clear triggers main store updates", () => {
|
||||||
|
const mapStore = new MapStore<string, string>();
|
||||||
|
|
||||||
|
let triggered = false;
|
||||||
|
|
||||||
|
mapStore.subscribe((map) => {
|
||||||
|
triggered = true;
|
||||||
|
expect(map).toBe(mapStore);
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(triggered).toBeTrue();
|
||||||
|
triggered = false;
|
||||||
|
mapStore.set('foo', 'bar');
|
||||||
|
expect(triggered).toBeTrue();
|
||||||
|
|
||||||
|
triggered = false;
|
||||||
|
mapStore.delete('baz');
|
||||||
|
expect(triggered).toBe(false);
|
||||||
|
mapStore.delete('foo');
|
||||||
|
expect(triggered).toBe(true);
|
||||||
|
|
||||||
|
triggered = false;
|
||||||
|
mapStore.clear();
|
||||||
|
expect(triggered).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("generates stores for keys with getStore", () => {
|
||||||
|
|
||||||
|
const mapStore = new MapStore<string, string>();
|
||||||
|
|
||||||
|
let valueReceivedInStoreForFoo: string|undefined;
|
||||||
|
let valueReceivedInStoreForBar: string|undefined;
|
||||||
|
|
||||||
|
mapStore.set('foo', 'someValue');
|
||||||
|
|
||||||
|
mapStore.getStore('foo').subscribe((value) => {
|
||||||
|
valueReceivedInStoreForFoo = value;
|
||||||
|
});
|
||||||
|
const unsubscribeBar = mapStore.getStore('bar').subscribe((value) => {
|
||||||
|
valueReceivedInStoreForBar = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(valueReceivedInStoreForFoo).toBe('someValue');
|
||||||
|
expect(valueReceivedInStoreForBar).toBe(undefined);
|
||||||
|
mapStore.set('foo', 'someOtherValue');
|
||||||
|
expect(valueReceivedInStoreForFoo).toBe('someOtherValue');
|
||||||
|
mapStore.delete('foo');
|
||||||
|
expect(valueReceivedInStoreForFoo).toBe(undefined);
|
||||||
|
mapStore.set('bar', 'baz');
|
||||||
|
expect(valueReceivedInStoreForBar).toBe('baz');
|
||||||
|
mapStore.clear();
|
||||||
|
expect(valueReceivedInStoreForBar).toBe(undefined);
|
||||||
|
unsubscribeBar();
|
||||||
|
mapStore.set('bar', 'fiz');
|
||||||
|
expect(valueReceivedInStoreForBar).toBe(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("generates stores with getStoreByAccessor", () => {
|
||||||
|
const mapStore = new MapStore<string, {
|
||||||
|
foo: string,
|
||||||
|
store: Writable<string>
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const fooStore = mapStore.getNestedStore('foo', (value) => {
|
||||||
|
return value.store;
|
||||||
|
});
|
||||||
|
|
||||||
|
mapStore.set('foo', {
|
||||||
|
foo: 'bar',
|
||||||
|
store: writable('init')
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(get(fooStore)).toBe('init');
|
||||||
|
|
||||||
|
mapStore.get('foo')?.store.set('newVal');
|
||||||
|
|
||||||
|
expect(get(fooStore)).toBe('newVal');
|
||||||
|
|
||||||
|
mapStore.set('foo', {
|
||||||
|
foo: 'bar',
|
||||||
|
store: writable('anotherVal')
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(get(fooStore)).toBe('anotherVal');
|
||||||
|
|
||||||
|
mapStore.delete('foo');
|
||||||
|
|
||||||
|
expect(get(fooStore)).toBeUndefined();
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,410 @@
|
|||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":17,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 444, 444, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 444, 444, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 444, 444, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":17,
|
||||||
|
"id":6,
|
||||||
|
"name":"start",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":31,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 0, 0, 0, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 0, 0, 443, 443, 443, 0, 0, 443, 443, 0, 0, 0, 0, 0, 0, 443, 0, 0, 0, 443, 443, 0, 0, 0, 0, 443, 443, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 0, 0, 0, 0, 443, 0, 0, 0, 443, 443, 0, 0, 0, 0, 443, 443, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 0, 0, 0, 0, 443, 0, 0, 0, 443, 443, 0, 0, 0, 0, 443, 443, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 0, 0, 443, 443, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 443, 443, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 443, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 443, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 443, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 443, 443, 443, 443, 443, 0, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 0, 443, 443, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 443, 443, 443, 443, 443, 0, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 0, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 443, 443, 0, 0, 0, 0, 0, 0, 0, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":17,
|
||||||
|
"id":7,
|
||||||
|
"name":"collisions",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":31,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":17,
|
||||||
|
"id":29,
|
||||||
|
"name":"jitsiMeetingRoom",
|
||||||
|
"opacity":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"jitsiRoom",
|
||||||
|
"type":"string",
|
||||||
|
"value":"MeetingRoom"
|
||||||
|
}],
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":31,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 454, 454, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":17,
|
||||||
|
"id":38,
|
||||||
|
"name":"jitsiChillzone",
|
||||||
|
"opacity":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"jitsiRoom",
|
||||||
|
"type":"string",
|
||||||
|
"value":"ChillZone"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"jitsiTrigger",
|
||||||
|
"type":"string",
|
||||||
|
"value":"onaction"
|
||||||
|
}],
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":31,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 446, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":17,
|
||||||
|
"id":23,
|
||||||
|
"name":"clockZone",
|
||||||
|
"opacity":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"zone",
|
||||||
|
"type":"string",
|
||||||
|
"value":"clock"
|
||||||
|
}],
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":31,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 223, 223, 223, 223, 223, 223, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 223, 223, 223, 223, 223, 223, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 223, 223, 223, 223, 223, 223, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 223, 223, 223, 223, 223, 223, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201],
|
||||||
|
"height":17,
|
||||||
|
"id":4,
|
||||||
|
"name":"floor",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":31,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[49, 58, 58, 58, 58, 58, 58, 42, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 42, 57, 57, 57, 57, 57, 57, 57, 50, 45, 63, 63, 63, 63, 63, 63, 45, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 45, 63, 63, 63, 63, 63, 63, 63, 45, 45, 73, 73, 73, 73, 73, 73, 45, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 45, 73, 73, 73, 73, 73, 73, 73, 45, 45, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 45, 59, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 32, 58, 58, 58, 58, 58, 58, 58, 60, 83, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 84, 93, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 94],
|
||||||
|
"height":17,
|
||||||
|
"id":9,
|
||||||
|
"name":"walls",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":31,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 293, 0, 0, 0, 0, 293, 0, 107, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 107, 0, 0, 128, 1, 2, 3, 0, 0, 0, 0, 304, 296, 297, 296, 297, 304, 0, 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 0, 0, 0, 11, 12, 13, 0, 0, 0, 0, 315, 307, 308, 307, 308, 315, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 22, 23, 0, 0, 0, 0, 243, 0, 0, 0, 0, 2147483943, 0, 0, 0, 325, 340, 340, 326, 0, 0, 325, 340, 340, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 0, 283, 283, 0, 2147483954, 0, 0, 0, 0, 340, 340, 0, 0, 0, 0, 340, 340, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 294, 294, 0, 0, 0, 0, 0, 325, 340, 340, 326, 0, 0, 325, 340, 340, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 351, 351, 0, 0, 0, 0, 351, 351, 0, 0, 0, 0, 0, 0, 325, 273, 275, 326, 0, 0, 0, 394, 395, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 2147483923, 275, 326, 0, 0, 0, 405, 406, 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, 334, 333, 334, 333, 334, 0, 0, 0, 0, 0, 0, 0, 325, 2147483923, 275, 326, 0, 0, 0, 416, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 344, 345, 344, 345, 344, 345, 0, 0, 0, 0, 0, 0, 0, 325, 2147483923, 275, 326, 0, 0, 0, 427, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217, 220, 220, 220, 220, 218, 0, 0, 0, 0, 0, 0, 0, 0, 284, 286, 0, 0, 0, 0, 438, 439, 0, 0, 0, 0, 0, 0, 0, 0, 0, 335, 336, 335, 336, 335, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 282, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 346, 347, 346, 347, 346, 347, 0, 2147483811, 2147483810, 2147483809, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":17,
|
||||||
|
"id":1,
|
||||||
|
"name":"furniture",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":31,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2147483909, 261, 0, 0, 0, 0, 2147483909, 261, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2147483909, 261, 0, 0, 0, 0, 2147483909, 261, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 0, 0, 0, 0, 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 231, 231, 231, 231, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 282, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":17,
|
||||||
|
"id":33,
|
||||||
|
"name":"aboveFurniture",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":31,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"draworder":"topdown",
|
||||||
|
"id":2,
|
||||||
|
"name":"floorLayer",
|
||||||
|
"objects":[
|
||||||
|
{
|
||||||
|
"height":64,
|
||||||
|
"id":4,
|
||||||
|
"name":"clockPopup",
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":128,
|
||||||
|
"x":512,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":146.081567555252,
|
||||||
|
"id":9,
|
||||||
|
"name":"chillZone",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"focusable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"zoom_margin",
|
||||||
|
"type":"float",
|
||||||
|
"value":3
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"zone",
|
||||||
|
"visible":true,
|
||||||
|
"width":192,
|
||||||
|
"x":32,
|
||||||
|
"y":77.9184324447482
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":416,
|
||||||
|
"id":11,
|
||||||
|
"name":"meetingZone",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"display_name",
|
||||||
|
"type":"string",
|
||||||
|
"value":"Brainstorm Zone!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"focusable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"zoom_margin",
|
||||||
|
"type":"float",
|
||||||
|
"value":0.35
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"zone",
|
||||||
|
"visible":true,
|
||||||
|
"width":224,
|
||||||
|
"x":736,
|
||||||
|
"y":32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":66.6667,
|
||||||
|
"id":13,
|
||||||
|
"name":"",
|
||||||
|
"rotation":0,
|
||||||
|
"text":
|
||||||
|
{
|
||||||
|
"fontfamily":"Sans Serif",
|
||||||
|
"halign":"center",
|
||||||
|
"pixelsize":11,
|
||||||
|
"text":"Camera should show the whole zone. Zoom in before entering",
|
||||||
|
"valign":"center",
|
||||||
|
"wrap":true
|
||||||
|
},
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":155.104,
|
||||||
|
"x":770.473518341308,
|
||||||
|
"y":126.688522863978
|
||||||
|
}],
|
||||||
|
"opacity":1,
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 329, 329, 0, 0, 0, 0, 329, 329, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 206, 209, 209, 209, 209, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 428, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2147483801, 2147483800, 2147483799, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":17,
|
||||||
|
"id":3,
|
||||||
|
"name":"abovePlayer1",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":31,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 399, 400, 399, 400, 399, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 410, 411, 410, 411, 410, 411, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":17,
|
||||||
|
"id":27,
|
||||||
|
"name":"abovePlayer2",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":31,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 401, 402, 401, 402, 401, 402, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 412, 413, 412, 413, 412, 413, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":17,
|
||||||
|
"id":28,
|
||||||
|
"name":"abovePlayer3",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":31,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":39,
|
||||||
|
"nextobjectid":18,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"mapCopyright",
|
||||||
|
"type":"string",
|
||||||
|
"value":"Credits: Valdo Romao https:\/\/www.linkedin.com\/in\/valdo-romao\/ \nLicense: CC-BY-SA 3.0 (http:\/\/creativecommons.org\/licenses\/by-sa\/3.0\/)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"mapDescription",
|
||||||
|
"type":"string",
|
||||||
|
"value":"A perfect virtual office to get started with WorkAdventure!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"mapImage",
|
||||||
|
"type":"string",
|
||||||
|
"value":"map.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"mapLink",
|
||||||
|
"type":"string",
|
||||||
|
"value":"https:\/\/thecodingmachine.github.io\/workadventure-map-starter-kit\/map.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"mapName",
|
||||||
|
"type":"string",
|
||||||
|
"value":"Starter kit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"script",
|
||||||
|
"type":"string",
|
||||||
|
"value":"..\/dist\/script.js"
|
||||||
|
}],
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.7.2",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[
|
||||||
|
{
|
||||||
|
"columns":10,
|
||||||
|
"firstgid":1,
|
||||||
|
"image":"..\/assets\/tileset5_export.png",
|
||||||
|
"imageheight":320,
|
||||||
|
"imagewidth":320,
|
||||||
|
"margin":0,
|
||||||
|
"name":"tileset5_export",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"tilesetCopyright",
|
||||||
|
"type":"string",
|
||||||
|
"value":"\u00a9 2021 WorkAdventure <https:\/\/workadventu.re\/> \nLicence: WORKADVENTURE SPECIFIC RESOURCES LICENSE (see LICENSE.assets file)"
|
||||||
|
}],
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":100,
|
||||||
|
"tileheight":32,
|
||||||
|
"tilewidth":32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns":10,
|
||||||
|
"firstgid":101,
|
||||||
|
"image":"..\/assets\/tileset6_export.png",
|
||||||
|
"imageheight":320,
|
||||||
|
"imagewidth":320,
|
||||||
|
"margin":0,
|
||||||
|
"name":"tileset6_export",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"tilesetCopyright",
|
||||||
|
"type":"string",
|
||||||
|
"value":"\u00a9 2021 WorkAdventure <https:\/\/workadventu.re\/> \nLicence: WORKADVENTURE SPECIFIC RESOURCES LICENSE (see LICENSE.assets file)"
|
||||||
|
}],
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":100,
|
||||||
|
"tileheight":32,
|
||||||
|
"tilewidth":32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns":11,
|
||||||
|
"firstgid":201,
|
||||||
|
"image":"..\/assets\/tileset1.png",
|
||||||
|
"imageheight":352,
|
||||||
|
"imagewidth":352,
|
||||||
|
"margin":0,
|
||||||
|
"name":"tileset1",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"tilesetCopyright",
|
||||||
|
"type":"string",
|
||||||
|
"value":"\u00a9 2021 WorkAdventure <https:\/\/workadventu.re\/> \nLicence: WORKADVENTURE SPECIFIC RESOURCES LICENSE (see LICENSE.assets file)"
|
||||||
|
}],
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":121,
|
||||||
|
"tileheight":32,
|
||||||
|
"tilewidth":32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns":11,
|
||||||
|
"firstgid":322,
|
||||||
|
"image":"..\/assets\/tileset1-repositioning.png",
|
||||||
|
"imageheight":352,
|
||||||
|
"imagewidth":352,
|
||||||
|
"margin":0,
|
||||||
|
"name":"tileset1-repositioning",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"tilesetCopyright",
|
||||||
|
"type":"string",
|
||||||
|
"value":"\u00a9 2021 WorkAdventure <https:\/\/workadventu.re\/> \nLicence: WORKADVENTURE SPECIFIC RESOURCES LICENSE (see LICENSE.assets file)"
|
||||||
|
}],
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":121,
|
||||||
|
"tileheight":32,
|
||||||
|
"tilewidth":32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns":6,
|
||||||
|
"firstgid":443,
|
||||||
|
"image":"..\/assets\/Special_Zones.png",
|
||||||
|
"imageheight":64,
|
||||||
|
"imagewidth":192,
|
||||||
|
"margin":0,
|
||||||
|
"name":"Special_Zones",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"tilesetCopyright",
|
||||||
|
"type":"string",
|
||||||
|
"value":"\u00a9 2021 WorkAdventure <https:\/\/workadventu.re\/> \nLicence: WORKADVENTURE SPECIFIC RESOURCES LICENSE (see LICENSE.assets file)"
|
||||||
|
}],
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":12,
|
||||||
|
"tileheight":32,
|
||||||
|
"tiles":[
|
||||||
|
{
|
||||||
|
"id":0,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"tilewidth":32
|
||||||
|
}],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":"1.6",
|
||||||
|
"width":31
|
||||||
|
}
|
||||||
@@ -104,6 +104,14 @@
|
|||||||
<a href="#" class="testLink" data-testmap="emoji.json" target="_blank">Testing Emoji</a>
|
<a href="#" class="testLink" data-testmap="emoji.json" target="_blank">Testing Emoji</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="radio" name="test-emoji"> Success <input type="radio" name="test-emoji"> Failure <input type="radio" name="test-emoji" checked> Pending
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="#" class="testLink" data-testmap="focusable_zone_map.json" target="_blank">Focusable Zones</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<h2>Iframe API</h2>
|
<h2>Iframe API</h2>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
|
|||||||