Merge branch 'develop' of github.com:thecodingmachine/workadventure into codeAPI
This commit is contained in:
@@ -7,4 +7,4 @@ export const isChangeZoneEvent = z.object({
|
||||
/**
|
||||
* A message sent from the game to the iFrame when a user enters or leaves a zone.
|
||||
*/
|
||||
export type ChangeZoneEvent = z.infer<typeof isChangeZoneEvent>;
|
||||
export type ChangeAreaEvent = z.infer<typeof isChangeZoneEvent>;
|
||||
@@ -29,7 +29,7 @@ import { isMenuRegisterEvent, isUnregisterMenuEvent } from "./ui/MenuRegisterEve
|
||||
import type { ChangeLayerEvent } from "./ChangeLayerEvent";
|
||||
import { isPlayerPosition } from "./PlayerPosition";
|
||||
import type { WasCameraUpdatedEvent } from "./WasCameraUpdatedEvent";
|
||||
import type { ChangeZoneEvent } from "./ChangeZoneEvent";
|
||||
import type { ChangeAreaEvent } from "./ChangeAreaEvent";
|
||||
import { isCameraSetEvent } from "./CameraSetEvent";
|
||||
import { isCameraFollowPlayerEvent } from "./CameraFollowPlayerEvent";
|
||||
import { isColorEvent } from "./ColorEvent";
|
||||
@@ -162,8 +162,8 @@ export interface IframeResponseEventMap {
|
||||
leaveEvent: EnterLeaveEvent;
|
||||
enterLayerEvent: ChangeLayerEvent;
|
||||
leaveLayerEvent: ChangeLayerEvent;
|
||||
enterZoneEvent: ChangeZoneEvent;
|
||||
leaveZoneEvent: ChangeZoneEvent;
|
||||
enterAreaEvent: ChangeAreaEvent;
|
||||
leaveAreaEvent: ChangeAreaEvent;
|
||||
buttonClickedEvent: ButtonClickedEvent;
|
||||
remotePlayerClickedEvent: RemotePlayerClickedEvent;
|
||||
actionsMenuActionClickedEvent: ActionsMenuActionClickedEvent;
|
||||
|
||||
@@ -28,7 +28,7 @@ import { ModifyEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent";
|
||||
import { handleMenuRegistrationEvent, handleMenuUnregisterEvent } from "../Stores/MenuStore";
|
||||
import type { ChangeLayerEvent } from "./Events/ChangeLayerEvent";
|
||||
import type { WasCameraUpdatedEvent } from "./Events/WasCameraUpdatedEvent";
|
||||
import type { ChangeZoneEvent } from "./Events/ChangeZoneEvent";
|
||||
import type { ChangeAreaEvent } from "./Events/ChangeAreaEvent";
|
||||
import { CameraSetEvent } from "./Events/CameraSetEvent";
|
||||
import { CameraFollowPlayerEvent } from "./Events/CameraFollowPlayerEvent";
|
||||
import type { RemotePlayerClickedEvent } from "./Events/RemotePlayerClickedEvent";
|
||||
@@ -445,21 +445,21 @@ class IframeListener {
|
||||
});
|
||||
}
|
||||
|
||||
sendEnterZoneEvent(zoneName: string) {
|
||||
sendEnterAreaEvent(areaName: string) {
|
||||
this.postMessage({
|
||||
type: "enterZoneEvent",
|
||||
type: "enterAreaEvent",
|
||||
data: {
|
||||
name: zoneName,
|
||||
} as ChangeZoneEvent,
|
||||
name: areaName,
|
||||
} as ChangeAreaEvent,
|
||||
});
|
||||
}
|
||||
|
||||
sendLeaveZoneEvent(zoneName: string) {
|
||||
sendLeaveAreaEvent(areaName: string) {
|
||||
this.postMessage({
|
||||
type: "leaveZoneEvent",
|
||||
type: "leaveAreaEvent",
|
||||
data: {
|
||||
name: zoneName,
|
||||
} as ChangeZoneEvent,
|
||||
name: areaName,
|
||||
} as ChangeAreaEvent,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,13 @@
|
||||
{#if !canShare}
|
||||
<section class="share-url not-mobile">
|
||||
<h3>{$LL.menu.invite.description()}</h3>
|
||||
<input type="text" readonly id="input-share-link" class="link-url" value={location.toString()} />
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
id="input-share-link"
|
||||
class="link-url nes-input is-dark"
|
||||
value={location.toString()}
|
||||
/>
|
||||
<button type="button" class="nes-btn is-primary" on:click={copyLink}>{$LL.menu.invite.copy()}</button>
|
||||
</section>
|
||||
{:else}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import SettingsSubMenu from "./SettingsSubMenu.svelte";
|
||||
import ProfileSubMenu from "./ProfileSubMenu.svelte";
|
||||
import AboutRoomSubMenu from "./AboutRoomSubMenu.svelte";
|
||||
import GlobalMessageSubMenu from "./GlobalMessagesSubMenu.svelte";
|
||||
import ContactSubMenu from "./ContactSubMenu.svelte";
|
||||
import CustomSubMenu from "./CustomSubMenu.svelte";
|
||||
import GuestSubMenu from "./GuestSubMenu.svelte";
|
||||
@@ -25,16 +24,16 @@
|
||||
let props: { url: string; allowApi: boolean };
|
||||
let unsubscriberSubMenuStore: Unsubscriber;
|
||||
|
||||
onMount(() => {
|
||||
onMount(async () => {
|
||||
unsubscriberSubMenuStore = subMenusStore.subscribe(() => {
|
||||
if (!$subMenusStore.includes(activeSubMenu)) {
|
||||
switchMenu($subMenusStore[0]);
|
||||
void switchMenu($subMenusStore[0]);
|
||||
}
|
||||
});
|
||||
|
||||
checkSubMenuToShow();
|
||||
|
||||
switchMenu($subMenusStore[0]);
|
||||
await switchMenu($subMenusStore[0]);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
@@ -43,7 +42,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
function switchMenu(menu: MenuItem) {
|
||||
async function switchMenu(menu: MenuItem) {
|
||||
if (menu.type === "translated") {
|
||||
activeSubMenu = menu;
|
||||
switch (menu.key) {
|
||||
@@ -60,7 +59,7 @@
|
||||
activeComponent = AboutRoomSubMenu;
|
||||
break;
|
||||
case SubMenusInterface.globalMessages:
|
||||
activeComponent = GlobalMessageSubMenu;
|
||||
activeComponent = (await import("./GlobalMessagesSubMenu.svelte")).default;
|
||||
break;
|
||||
case SubMenusInterface.contact:
|
||||
activeComponent = ContactSubMenu;
|
||||
@@ -111,7 +110,7 @@
|
||||
<button
|
||||
type="button"
|
||||
class="nes-btn {activeSubMenu === submenu ? 'is-disabled' : ''}"
|
||||
on:click|preventDefault={() => switchMenu(submenu)}
|
||||
on:click|preventDefault={() => void switchMenu(submenu)}
|
||||
>
|
||||
{translateMenuName(submenu)}
|
||||
</button>
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
|
||||
import { audioManagerVolumeStore } from "../../Stores/AudioManagerStore";
|
||||
|
||||
import infoImg from "../images/info.svg";
|
||||
|
||||
let fullscreen: boolean = localUserStore.getFullscreen();
|
||||
let notification: boolean = localUserStore.getNotification() === "granted";
|
||||
let forceCowebsiteTrigger: boolean = localUserStore.getForceCowebsiteTrigger();
|
||||
@@ -179,8 +181,15 @@
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>{$LL.menu.settings.privacySettings.title()}</h3>
|
||||
<p>{$LL.menu.settings.privacySettings.explanation()}</p>
|
||||
<div class="tooltip">
|
||||
<h3>
|
||||
<span class="dotted-bottom">{$LL.menu.settings.privacySettings.title()}</span>
|
||||
<img src={infoImg} alt="info icon" width="23px" height="23px" />
|
||||
</h3>
|
||||
<div class="nes-balloon away-tooltip-container from-left flex">
|
||||
<p class="away-tooltip-text">{$LL.menu.settings.privacySettings.explanation()}</p>
|
||||
</div>
|
||||
</div>
|
||||
<label>
|
||||
<input type="checkbox" class="nes-checkbox is-dark" bind:checked={valueCameraPrivacySettings} />
|
||||
<span>{$LL.menu.settings.privacySettings.cameraToggle()}</span>
|
||||
@@ -286,6 +295,35 @@
|
||||
.languages-switcher option {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
//Tooltip
|
||||
.tooltip {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.away-tooltip-container {
|
||||
background-color: #fff;
|
||||
position: absolute;
|
||||
bottom: 45%;
|
||||
left: 55%;
|
||||
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.away-tooltip-text {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
.dotted-bottom {
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
|
||||
.tooltip:hover {
|
||||
.away-tooltip-container {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { gameManager } from "../../Phaser/Game/GameManager";
|
||||
import { AdminMessageEventTypes } from "../../Connexion/AdminMessagesService";
|
||||
import type { Quill } from "quill";
|
||||
import Quill from "quill";
|
||||
import type { PlayGlobalMessageInterface } from "../../Connexion/ConnexionModels";
|
||||
import LL from "../../i18n/i18n-svelte";
|
||||
|
||||
@@ -54,10 +54,7 @@
|
||||
};
|
||||
|
||||
//Quill
|
||||
onMount(async () => {
|
||||
// Import quill
|
||||
const { default: Quill } = await import("quill"); // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
|
||||
onMount(() => {
|
||||
quill = new Quill(QUILL_EDITOR, {
|
||||
placeholder: $LL.menu.globalMessage.enter(),
|
||||
theme: "snow",
|
||||
@@ -76,3 +73,49 @@
|
||||
<section class="section-input-send-text">
|
||||
<div class="input-send-text" bind:this={QUILL_EDITOR} />
|
||||
</section>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../style/breakpoints.scss";
|
||||
@import "quill/dist/quill.snow.css";
|
||||
|
||||
section.section-input-send-text {
|
||||
--height-toolbar: 20%;
|
||||
height: 100%;
|
||||
|
||||
:global(.ql-toolbar) {
|
||||
max-height: var(--height-toolbar);
|
||||
background: whitesmoke;
|
||||
}
|
||||
|
||||
div.input-send-text {
|
||||
height: calc(100% - var(--height-toolbar));
|
||||
overflow: auto;
|
||||
|
||||
color: whitesmoke;
|
||||
font-size: 1rem;
|
||||
|
||||
:global(.ql-editor.ql-blank::before) {
|
||||
color: whitesmoke;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
:global(.ql-tooltip) {
|
||||
top: 40% !important;
|
||||
left: 20% !important;
|
||||
|
||||
color: whitesmoke;
|
||||
background-color: #333333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
section.section-input-send-text {
|
||||
--height-toolbar: 30%;
|
||||
|
||||
:global(.ql-toolbar) {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M40 80C62.0914 80 80 62.0914 80 40C80 17.9086 62.0914 0 40 0C17.9086 0 0 17.9086 0 40C0 62.0914 17.9086 80 40 80ZM40 73C58.2254 73 73 58.2254 73 40C73 21.7746 58.2254 7 40 7C21.7746 7 7 21.7746 7 40C7 58.2254 21.7746 73 40 73Z" fill="white"/>
|
||||
<path d="M43.7812 32.6406V58H37.1953V32.6406H43.7812ZM36.8906 26.0781C36.8906 25.0625 37.2109 24.2266 37.8516 23.5703C38.5078 22.8984 39.3828 22.5625 40.4766 22.5625C41.5859 22.5625 42.4609 22.8984 43.1016 23.5703C43.7422 24.2266 44.0625 25.0625 44.0625 26.0781C44.0625 27.0781 43.7344 27.9141 43.0781 28.5859C42.4375 29.2422 41.5781 29.5703 40.5 29.5703C39.3906 29.5703 38.5078 29.2422 37.8516 28.5859C37.2109 27.9141 36.8906 27.0781 36.8906 26.0781Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 863 B |
@@ -15,14 +15,9 @@ export interface RoomRedirect {
|
||||
|
||||
export class Room {
|
||||
public readonly id: string;
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
private readonly isPublic: boolean;
|
||||
private _authenticationMandatory: boolean = DISABLE_ANONYMOUS;
|
||||
private _iframeAuthentication?: string = OPID_LOGIN_SCREEN_PROVIDER;
|
||||
private _mapUrl: string | undefined;
|
||||
private instance: string | undefined;
|
||||
private readonly _search: URLSearchParams;
|
||||
private _contactPage: string | undefined;
|
||||
private _group: string | null = null;
|
||||
@@ -37,13 +32,6 @@ export class Room {
|
||||
if (this.id.startsWith("/")) {
|
||||
this.id = this.id.substr(1);
|
||||
}
|
||||
if (this.id.startsWith("_/") || this.id.startsWith("*/")) {
|
||||
this.isPublic = true;
|
||||
} else if (this.id.startsWith("@/")) {
|
||||
this.isPublic = false;
|
||||
} else {
|
||||
throw new Error("Invalid room ID");
|
||||
}
|
||||
|
||||
this._search = new URLSearchParams(roomUrl.search);
|
||||
}
|
||||
@@ -84,8 +72,10 @@ export class Room {
|
||||
|
||||
const currentRoom = new Room(baseUrl);
|
||||
let instance: string = "global";
|
||||
if (currentRoom.isPublic) {
|
||||
instance = currentRoom.getInstance();
|
||||
if (currentRoom.id.startsWith("_/") || currentRoom.id.startsWith("*/")) {
|
||||
const match = /[_*]\/([^/]+)\/.+/.exec(currentRoom.id);
|
||||
if (!match) throw new Error('Could not extract instance from "' + currentRoom.id + '"');
|
||||
instance = match[1];
|
||||
}
|
||||
|
||||
baseUrl.pathname = "/_/" + instance + "/" + absoluteExitSceneUrl.host + absoluteExitSceneUrl.pathname;
|
||||
@@ -151,31 +141,6 @@ export class Room {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instance name is:
|
||||
* - In a public URL: the second part of the URL ( _/[instance]/map.json)
|
||||
* - In a private URL: [organizationId/worldId]
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public getInstance(): string {
|
||||
if (this.instance !== undefined) {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
if (this.isPublic) {
|
||||
const match = /[_*]\/([^/]+)\/.+/.exec(this.id);
|
||||
if (!match) throw new Error('Could not extract instance from "' + this.id + '"');
|
||||
this.instance = match[1];
|
||||
return this.instance;
|
||||
} else {
|
||||
const match = /@\/([^/]+)\/([^/]+)\/.+/.exec(this.id);
|
||||
if (!match) throw new Error('Could not extract instance from "' + this.id + '"');
|
||||
this.instance = match[1] + "/" + match[2];
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
|
||||
public isDisconnected(): boolean {
|
||||
const alone = this._search.get("alone");
|
||||
if (alone && alone !== "0" && alone.toLowerCase() !== "false") {
|
||||
|
||||
@@ -22,9 +22,9 @@ export type layerChangeCallback = (
|
||||
allLayersOnNewPosition: Array<ITiledMapLayer>
|
||||
) => void;
|
||||
|
||||
export type zoneChangeCallback = (
|
||||
zonesChangedByAction: Array<ITiledMapObject>,
|
||||
allZonesOnNewPosition: Array<ITiledMapObject>
|
||||
export type areaChangeCallback = (
|
||||
areasChangedByAction: Array<ITiledMapObject>,
|
||||
allAreasOnNewPosition: Array<ITiledMapObject>
|
||||
) => void;
|
||||
|
||||
/**
|
||||
@@ -54,8 +54,8 @@ export class GameMap {
|
||||
|
||||
private enterLayerCallbacks = Array<layerChangeCallback>();
|
||||
private leaveLayerCallbacks = Array<layerChangeCallback>();
|
||||
private enterZoneCallbacks = Array<zoneChangeCallback>();
|
||||
private leaveZoneCallbacks = Array<zoneChangeCallback>();
|
||||
private enterAreaCallbacks = Array<areaChangeCallback>();
|
||||
private leaveAreaCallbacks = Array<areaChangeCallback>();
|
||||
|
||||
private tileNameMap = new Map<string, number>();
|
||||
|
||||
@@ -63,7 +63,9 @@ export class GameMap {
|
||||
public readonly flatLayers: ITiledMapLayer[];
|
||||
public readonly tiledObjects: ITiledMapObject[];
|
||||
public readonly phaserLayers: TilemapLayer[] = [];
|
||||
public readonly zones: ITiledMapObject[] = [];
|
||||
public readonly areas: ITiledMapObject[] = [];
|
||||
|
||||
private readonly areasPositionOffsetY: number = 16;
|
||||
|
||||
public exitUrls: Array<string> = [];
|
||||
|
||||
@@ -76,7 +78,8 @@ export class GameMap {
|
||||
) {
|
||||
this.flatLayers = flattenGroupLayersMap(map);
|
||||
this.tiledObjects = this.getObjectsFromLayers(this.flatLayers);
|
||||
this.zones = this.tiledObjects.filter((object) => object.type === "zone");
|
||||
// NOTE: We leave "zone" for legacy reasons
|
||||
this.areas = this.tiledObjects.filter((object) => ["zone", "area"].includes(object.type));
|
||||
|
||||
let depth = -2;
|
||||
for (const layer of this.flatLayers) {
|
||||
@@ -148,7 +151,10 @@ export class GameMap {
|
||||
public setPosition(x: number, y: number) {
|
||||
this.oldPosition = this.position;
|
||||
this.position = { x, y };
|
||||
this.triggerZonesChange();
|
||||
const areasChanged = this.triggerAreasChange();
|
||||
if (areasChanged) {
|
||||
this.triggerAllProperties();
|
||||
}
|
||||
|
||||
this.oldKey = this.key;
|
||||
|
||||
@@ -201,21 +207,17 @@ export class GameMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback called when the user moves inside another zone.
|
||||
*
|
||||
* @deprecated
|
||||
* Registers a callback called when the user moves inside another area.
|
||||
*/
|
||||
public onEnterZone(callback: zoneChangeCallback) {
|
||||
this.enterZoneCallbacks.push(callback);
|
||||
public onEnterArea(callback: areaChangeCallback) {
|
||||
this.enterAreaCallbacks.push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback called when the user moves outside another zone.
|
||||
*
|
||||
* @deprecated
|
||||
* Registers a callback called when the user moves outside another area.
|
||||
*/
|
||||
public onLeaveZone(callback: zoneChangeCallback) {
|
||||
this.leaveZoneCallbacks.push(callback);
|
||||
public onLeaveArea(callback: areaChangeCallback) {
|
||||
this.leaveAreaCallbacks.push(callback);
|
||||
}
|
||||
|
||||
public findLayer(layerName: string): ITiledMapLayer | undefined {
|
||||
@@ -424,55 +426,56 @@ export class GameMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* We use Tiled Objects with type "zone" as zones with defined x, y, width and height for easier event triggering.
|
||||
* We use Tiled Objects with type "area" as areas with defined x, y, width and height for easier event triggering.
|
||||
* @returns If there were any areas changes
|
||||
*/
|
||||
private triggerZonesChange(): void {
|
||||
const zonesByOldPosition = this.oldPosition
|
||||
? this.zones.filter((zone) => {
|
||||
if (!this.oldPosition) {
|
||||
return false;
|
||||
}
|
||||
return MathUtils.isOverlappingWithRectangle(this.oldPosition, zone);
|
||||
})
|
||||
: [];
|
||||
private triggerAreasChange(): boolean {
|
||||
const areasByOldPosition = this.getAreasOnPosition(this.oldPosition, this.areasPositionOffsetY);
|
||||
const areasByNewPosition = this.getAreasOnPosition(this.position, this.areasPositionOffsetY);
|
||||
|
||||
const zonesByNewPosition = this.position
|
||||
? this.zones.filter((zone) => {
|
||||
if (!this.position) {
|
||||
return false;
|
||||
}
|
||||
return MathUtils.isOverlappingWithRectangle(this.position, zone);
|
||||
})
|
||||
: [];
|
||||
const enterAreas = new Set(areasByNewPosition);
|
||||
const leaveAreas = new Set(areasByOldPosition);
|
||||
|
||||
const enterZones = new Set(zonesByNewPosition);
|
||||
const leaveZones = new Set(zonesByOldPosition);
|
||||
|
||||
enterZones.forEach((zone) => {
|
||||
if (leaveZones.has(zone)) {
|
||||
leaveZones.delete(zone);
|
||||
enterZones.delete(zone);
|
||||
enterAreas.forEach((area) => {
|
||||
if (leaveAreas.has(area)) {
|
||||
leaveAreas.delete(area);
|
||||
enterAreas.delete(area);
|
||||
}
|
||||
});
|
||||
|
||||
if (enterZones.size > 0) {
|
||||
const zonesArray = Array.from(enterZones);
|
||||
for (const callback of this.enterZoneCallbacks) {
|
||||
callback(zonesArray, zonesByNewPosition);
|
||||
let areasChange = false;
|
||||
if (enterAreas.size > 0) {
|
||||
const areasArray = Array.from(enterAreas);
|
||||
for (const callback of this.enterAreaCallbacks) {
|
||||
callback(areasArray, areasByNewPosition);
|
||||
}
|
||||
areasChange = true;
|
||||
}
|
||||
|
||||
if (leaveZones.size > 0) {
|
||||
const zonesArray = Array.from(leaveZones);
|
||||
for (const callback of this.leaveZoneCallbacks) {
|
||||
callback(zonesArray, zonesByNewPosition);
|
||||
if (leaveAreas.size > 0) {
|
||||
const areasArray = Array.from(leaveAreas);
|
||||
for (const callback of this.leaveAreaCallbacks) {
|
||||
callback(areasArray, areasByNewPosition);
|
||||
}
|
||||
areasChange = true;
|
||||
}
|
||||
return areasChange;
|
||||
}
|
||||
|
||||
private getProperties(key: number): Map<string, string | boolean | number> {
|
||||
const properties = new Map<string, string | boolean | number>();
|
||||
|
||||
for (const area of this.getAreasOnPosition(this.position, this.areasPositionOffsetY)) {
|
||||
if (area.properties !== undefined) {
|
||||
for (const property of area.properties) {
|
||||
if (property.value === undefined) {
|
||||
continue;
|
||||
}
|
||||
properties.set(property.name, property.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const layer of this.flatLayers) {
|
||||
if (layer.type !== "tilelayer") {
|
||||
continue;
|
||||
@@ -511,6 +514,17 @@ export class GameMap {
|
||||
return properties;
|
||||
}
|
||||
|
||||
private getAreasOnPosition(position?: { x: number; y: number }, offsetY: number = 0): ITiledMapObject[] {
|
||||
return position
|
||||
? this.areas.filter((area) => {
|
||||
if (!position) {
|
||||
return false;
|
||||
}
|
||||
return MathUtils.isOverlappingWithRectangle({ x: position.x, y: position.y + offsetY }, area);
|
||||
})
|
||||
: [];
|
||||
}
|
||||
|
||||
private getTileProperty(index: number): Array<ITiledMapProperty> {
|
||||
if (this.tileSetPropertyMap[index]) {
|
||||
return this.tileSetPropertyMap[index];
|
||||
|
||||
@@ -7,6 +7,7 @@ export enum GameMapProperties {
|
||||
EXIT_URL = "exitUrl",
|
||||
EXIT_SCENE_URL = "exitSceneUrl",
|
||||
FONT_FAMILY = "font-family",
|
||||
FOCUSABLE = "focusable",
|
||||
JITSI_ADMIN_ROOM_TAG = "jitsiRoomAdminTag",
|
||||
JITSI_CONFIG = "jitsiConfig",
|
||||
JITSI_INTERFACE_CONFIG = "jitsiInterfaceConfig",
|
||||
@@ -15,6 +16,7 @@ export enum GameMapProperties {
|
||||
JITSI_TRIGGER_MESSAGE = "jitsiTriggerMessage",
|
||||
JITSI_URL = "jitsiUrl",
|
||||
JITSI_WIDTH = "jitsiWidth",
|
||||
JITSI_NO_PREFIX = "jitsiNoPrefix",
|
||||
NAME = "name",
|
||||
OPEN_TAB = "openTab",
|
||||
OPEN_WEBSITE = "openWebsite",
|
||||
@@ -35,4 +37,5 @@ export enum GameMapProperties {
|
||||
URL = "url",
|
||||
WRITABLE_BY = "writableBy",
|
||||
ZONE = "zone",
|
||||
ZOOM_MARGIN = "zoom_margin",
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { get } from "svelte/store";
|
||||
import { ON_ACTION_TRIGGER_BUTTON, ON_ICON_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager";
|
||||
import type { ITiledMapLayer } from "../Map/ITiledMap";
|
||||
import type { ITiledMapProperty } from "../Map/ITiledMap";
|
||||
import { GameMapProperties } from "./GameMapProperties";
|
||||
import type { CoWebsite } from "../../WebRtc/CoWebsite/CoWesbite";
|
||||
import { SimpleCoWebsite } from "../../WebRtc/CoWebsite/SimpleCoWebsite";
|
||||
@@ -23,9 +23,21 @@ interface OpenCoWebsite {
|
||||
coWebsite?: CoWebsite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Either Layer or Object within Objects Layer in Tiled
|
||||
*/
|
||||
export interface ITiledPlace {
|
||||
name: string;
|
||||
properties?: ITiledMapProperty[];
|
||||
x?: number;
|
||||
y?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export class GameMapPropertiesListener {
|
||||
private coWebsitesOpenByLayer = new Map<ITiledMapLayer, OpenCoWebsite>();
|
||||
private coWebsitesActionTriggerByLayer = new Map<ITiledMapLayer, string>();
|
||||
private coWebsitesOpenByPlace = new Map<ITiledPlace, OpenCoWebsite>();
|
||||
private coWebsitesActionTriggerByPlace = new Map<ITiledPlace, string>();
|
||||
|
||||
constructor(private scene: GameScene, private gameMap: GameMap) {}
|
||||
|
||||
@@ -67,7 +79,11 @@ export class GameMapPropertiesListener {
|
||||
});
|
||||
} else {
|
||||
const openJitsiRoomFunction = () => {
|
||||
const roomName = jitsiFactory.getRoomName(newValue.toString(), this.scene.instance);
|
||||
let addPrefix = true;
|
||||
if (allProps.get(GameMapProperties.JITSI_NO_PREFIX)) {
|
||||
addPrefix = false;
|
||||
}
|
||||
const roomName = jitsiFactory.getRoomName(newValue.toString(), this.scene.roomUrl, addPrefix);
|
||||
const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined;
|
||||
|
||||
if (JITSI_PRIVATE_MODE && !jitsiUrl) {
|
||||
@@ -179,193 +195,241 @@ export class GameMapPropertiesListener {
|
||||
}
|
||||
});
|
||||
|
||||
// Open a new co-website by the property.
|
||||
this.gameMap.onEnterLayer((newLayers) => {
|
||||
const handler = () => {
|
||||
newLayers.forEach((layer) => {
|
||||
if (!layer.properties) {
|
||||
return;
|
||||
}
|
||||
|
||||
let openWebsiteProperty: string | undefined;
|
||||
let allowApiProperty: boolean | undefined;
|
||||
let websitePolicyProperty: string | undefined;
|
||||
let websiteWidthProperty: number | undefined;
|
||||
let websitePositionProperty: number | undefined;
|
||||
let websiteTriggerProperty: string | undefined;
|
||||
let websiteTriggerMessageProperty: string | undefined;
|
||||
|
||||
layer.properties.forEach((property) => {
|
||||
switch (property.name) {
|
||||
case GameMapProperties.OPEN_WEBSITE:
|
||||
openWebsiteProperty = property.value as string | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_ALLOW_API:
|
||||
allowApiProperty = property.value as boolean | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_POLICY:
|
||||
websitePolicyProperty = property.value as string | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_WIDTH:
|
||||
websiteWidthProperty = property.value as number | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_POSITION:
|
||||
websitePositionProperty = property.value as number | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_TRIGGER:
|
||||
websiteTriggerProperty = property.value as string | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_TRIGGER_MESSAGE:
|
||||
websiteTriggerMessageProperty = property.value as string | undefined;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (!openWebsiteProperty) {
|
||||
return;
|
||||
}
|
||||
|
||||
const actionId = "openWebsite-" + (Math.random() + 1).toString(36).substring(7);
|
||||
|
||||
if (this.coWebsitesOpenByLayer.has(layer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const coWebsiteOpen: OpenCoWebsite = {
|
||||
actionId: actionId,
|
||||
};
|
||||
|
||||
this.coWebsitesOpenByLayer.set(layer, coWebsiteOpen);
|
||||
|
||||
const loadCoWebsiteFunction = (coWebsite: CoWebsite) => {
|
||||
coWebsiteManager.loadCoWebsite(coWebsite).catch(() => {
|
||||
console.error("Error during loading a co-website: " + coWebsite.getUrl());
|
||||
});
|
||||
|
||||
layoutManagerActionStore.removeAction(actionId);
|
||||
};
|
||||
|
||||
const openCoWebsiteFunction = () => {
|
||||
const coWebsite = new SimpleCoWebsite(
|
||||
new URL(openWebsiteProperty ?? "", this.scene.MapUrlFile),
|
||||
allowApiProperty,
|
||||
websitePolicyProperty,
|
||||
websiteWidthProperty,
|
||||
false
|
||||
);
|
||||
|
||||
coWebsiteOpen.coWebsite = coWebsite;
|
||||
|
||||
coWebsiteManager.addCoWebsiteToStore(coWebsite, websitePositionProperty);
|
||||
|
||||
loadCoWebsiteFunction(coWebsite);
|
||||
};
|
||||
|
||||
if (
|
||||
localUserStore.getForceCowebsiteTrigger() ||
|
||||
websiteTriggerProperty === ON_ACTION_TRIGGER_BUTTON
|
||||
) {
|
||||
if (!websiteTriggerMessageProperty) {
|
||||
websiteTriggerMessageProperty = get(LL).trigger.cowebsite();
|
||||
}
|
||||
|
||||
this.coWebsitesActionTriggerByLayer.set(layer, actionId);
|
||||
|
||||
layoutManagerActionStore.addAction({
|
||||
uuid: actionId,
|
||||
type: "message",
|
||||
message: websiteTriggerMessageProperty,
|
||||
callback: () => openCoWebsiteFunction(),
|
||||
userInputManager: this.scene.userInputManager,
|
||||
});
|
||||
} else if (websiteTriggerProperty === ON_ICON_TRIGGER_BUTTON) {
|
||||
const coWebsite = new SimpleCoWebsite(
|
||||
new URL(openWebsiteProperty ?? "", this.scene.MapUrlFile),
|
||||
allowApiProperty,
|
||||
websitePolicyProperty,
|
||||
websiteWidthProperty,
|
||||
false
|
||||
);
|
||||
|
||||
coWebsiteOpen.coWebsite = coWebsite;
|
||||
|
||||
coWebsiteManager.addCoWebsiteToStore(coWebsite, websitePositionProperty);
|
||||
}
|
||||
|
||||
if (!websiteTriggerProperty) {
|
||||
openCoWebsiteFunction();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handler();
|
||||
this.onEnterPlaceHandler(newLayers);
|
||||
});
|
||||
|
||||
// Close opened co-websites on leave the layer who contain the property.
|
||||
this.gameMap.onLeaveLayer((oldLayers) => {
|
||||
const handler = () => {
|
||||
oldLayers.forEach((layer) => {
|
||||
if (!layer.properties) {
|
||||
return;
|
||||
}
|
||||
this.onLeavePlaceHandler(oldLayers);
|
||||
});
|
||||
|
||||
let openWebsiteProperty: string | undefined;
|
||||
let websiteTriggerProperty: string | undefined;
|
||||
this.gameMap.onEnterArea((newAreas) => {
|
||||
this.onEnterPlaceHandler(newAreas);
|
||||
});
|
||||
|
||||
layer.properties.forEach((property) => {
|
||||
switch (property.name) {
|
||||
case GameMapProperties.OPEN_WEBSITE:
|
||||
openWebsiteProperty = property.value as string | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_TRIGGER:
|
||||
websiteTriggerProperty = property.value as string | undefined;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (!openWebsiteProperty) {
|
||||
return;
|
||||
}
|
||||
|
||||
const coWebsiteOpen = this.coWebsitesOpenByLayer.get(layer);
|
||||
|
||||
if (!coWebsiteOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
const coWebsite = coWebsiteOpen.coWebsite;
|
||||
|
||||
if (coWebsite) {
|
||||
coWebsiteManager.closeCoWebsite(coWebsite);
|
||||
}
|
||||
|
||||
this.coWebsitesOpenByLayer.delete(layer);
|
||||
|
||||
if (!websiteTriggerProperty) {
|
||||
return;
|
||||
}
|
||||
|
||||
const actionStore = get(layoutManagerActionStore);
|
||||
const actionTriggerUuid = this.coWebsitesActionTriggerByLayer.get(layer);
|
||||
|
||||
if (!actionTriggerUuid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const action =
|
||||
actionStore && actionStore.length > 0
|
||||
? actionStore.find((action) => action.uuid === actionTriggerUuid)
|
||||
: undefined;
|
||||
|
||||
if (action) {
|
||||
layoutManagerActionStore.removeAction(actionTriggerUuid);
|
||||
}
|
||||
|
||||
this.coWebsitesActionTriggerByLayer.delete(layer);
|
||||
});
|
||||
};
|
||||
|
||||
handler();
|
||||
this.gameMap.onLeaveArea((oldAreas) => {
|
||||
this.onLeavePlaceHandler(oldAreas);
|
||||
});
|
||||
}
|
||||
|
||||
private onEnterPlaceHandler(places: ITiledPlace[]): void {
|
||||
places.forEach((place) => {
|
||||
this.handleOpenWebsitePropertiesOnEnter(place);
|
||||
this.handleFocusablePropertiesOnEnter(place);
|
||||
});
|
||||
}
|
||||
|
||||
private onLeavePlaceHandler(places: ITiledPlace[]): void {
|
||||
places.forEach((place) => {
|
||||
if (!place.properties) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.handleOpenWebsitePropertiesOnLeave(place);
|
||||
this.handleFocusablePropertiesOnLeave(place);
|
||||
});
|
||||
}
|
||||
|
||||
private handleOpenWebsitePropertiesOnEnter(place: ITiledPlace): void {
|
||||
if (!place.properties) {
|
||||
return;
|
||||
}
|
||||
let openWebsiteProperty: string | undefined;
|
||||
let allowApiProperty: boolean | undefined;
|
||||
let websitePolicyProperty: string | undefined;
|
||||
let websiteWidthProperty: number | undefined;
|
||||
let websitePositionProperty: number | undefined;
|
||||
let websiteTriggerProperty: string | undefined;
|
||||
let websiteTriggerMessageProperty: string | undefined;
|
||||
|
||||
place.properties.forEach((property) => {
|
||||
switch (property.name) {
|
||||
case GameMapProperties.OPEN_WEBSITE:
|
||||
openWebsiteProperty = property.value as string | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_ALLOW_API:
|
||||
allowApiProperty = property.value as boolean | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_POLICY:
|
||||
websitePolicyProperty = property.value as string | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_WIDTH:
|
||||
websiteWidthProperty = property.value as number | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_POSITION:
|
||||
websitePositionProperty = property.value as number | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_TRIGGER:
|
||||
websiteTriggerProperty = property.value as string | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_TRIGGER_MESSAGE:
|
||||
websiteTriggerMessageProperty = property.value as string | undefined;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (!openWebsiteProperty) {
|
||||
return;
|
||||
}
|
||||
|
||||
const actionId = "openWebsite-" + (Math.random() + 1).toString(36).substring(7);
|
||||
|
||||
if (this.coWebsitesOpenByPlace.has(place)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const coWebsiteOpen: OpenCoWebsite = {
|
||||
actionId: actionId,
|
||||
};
|
||||
|
||||
this.coWebsitesOpenByPlace.set(place, coWebsiteOpen);
|
||||
|
||||
const loadCoWebsiteFunction = (coWebsite: CoWebsite) => {
|
||||
coWebsiteManager.loadCoWebsite(coWebsite).catch(() => {
|
||||
console.error("Error during loading a co-website: " + coWebsite.getUrl());
|
||||
});
|
||||
|
||||
layoutManagerActionStore.removeAction(actionId);
|
||||
};
|
||||
|
||||
const openCoWebsiteFunction = () => {
|
||||
const coWebsite = new SimpleCoWebsite(
|
||||
new URL(openWebsiteProperty ?? "", this.scene.MapUrlFile),
|
||||
allowApiProperty,
|
||||
websitePolicyProperty,
|
||||
websiteWidthProperty,
|
||||
false
|
||||
);
|
||||
|
||||
coWebsiteOpen.coWebsite = coWebsite;
|
||||
|
||||
coWebsiteManager.addCoWebsiteToStore(coWebsite, websitePositionProperty);
|
||||
|
||||
loadCoWebsiteFunction(coWebsite);
|
||||
};
|
||||
|
||||
if (localUserStore.getForceCowebsiteTrigger() || websiteTriggerProperty === ON_ACTION_TRIGGER_BUTTON) {
|
||||
if (!websiteTriggerMessageProperty) {
|
||||
websiteTriggerMessageProperty = get(LL).trigger.cowebsite();
|
||||
}
|
||||
|
||||
this.coWebsitesActionTriggerByPlace.set(place, actionId);
|
||||
|
||||
layoutManagerActionStore.addAction({
|
||||
uuid: actionId,
|
||||
type: "message",
|
||||
message: websiteTriggerMessageProperty,
|
||||
callback: () => openCoWebsiteFunction(),
|
||||
userInputManager: this.scene.userInputManager,
|
||||
});
|
||||
} else if (websiteTriggerProperty === ON_ICON_TRIGGER_BUTTON) {
|
||||
const coWebsite = new SimpleCoWebsite(
|
||||
new URL(openWebsiteProperty ?? "", this.scene.MapUrlFile),
|
||||
allowApiProperty,
|
||||
websitePolicyProperty,
|
||||
websiteWidthProperty,
|
||||
false
|
||||
);
|
||||
|
||||
coWebsiteOpen.coWebsite = coWebsite;
|
||||
|
||||
coWebsiteManager.addCoWebsiteToStore(coWebsite, websitePositionProperty);
|
||||
}
|
||||
|
||||
if (!websiteTriggerProperty) {
|
||||
openCoWebsiteFunction();
|
||||
}
|
||||
}
|
||||
|
||||
private handleFocusablePropertiesOnEnter(place: ITiledPlace): void {
|
||||
if (!place.properties) {
|
||||
return;
|
||||
}
|
||||
if (place.x === undefined || place.y === undefined || !place.height || !place.width) {
|
||||
return;
|
||||
}
|
||||
const focusable = place.properties.find((property) => property.name === GameMapProperties.FOCUSABLE);
|
||||
if (focusable && focusable.value === true) {
|
||||
const zoomMargin = place.properties.find((property) => property.name === GameMapProperties.ZOOM_MARGIN);
|
||||
this.scene.getCameraManager().enterFocusMode(
|
||||
{
|
||||
x: place.x + place.width * 0.5,
|
||||
y: place.y + place.height * 0.5,
|
||||
width: place.width,
|
||||
height: place.height,
|
||||
},
|
||||
zoomMargin ? Math.max(0, Number(zoomMargin.value)) : undefined
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private handleOpenWebsitePropertiesOnLeave(place: ITiledPlace): void {
|
||||
if (!place.properties) {
|
||||
return;
|
||||
}
|
||||
|
||||
let openWebsiteProperty: string | undefined;
|
||||
let websiteTriggerProperty: string | undefined;
|
||||
|
||||
place.properties.forEach((property) => {
|
||||
switch (property.name) {
|
||||
case GameMapProperties.OPEN_WEBSITE:
|
||||
openWebsiteProperty = property.value as string | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_TRIGGER:
|
||||
websiteTriggerProperty = property.value as string | undefined;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (!openWebsiteProperty) {
|
||||
return;
|
||||
}
|
||||
|
||||
const coWebsiteOpen = this.coWebsitesOpenByPlace.get(place);
|
||||
|
||||
if (!coWebsiteOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
const coWebsite = coWebsiteOpen.coWebsite;
|
||||
|
||||
if (coWebsite) {
|
||||
coWebsiteManager.closeCoWebsite(coWebsite);
|
||||
}
|
||||
|
||||
this.coWebsitesOpenByPlace.delete(place);
|
||||
|
||||
if (!websiteTriggerProperty) {
|
||||
return;
|
||||
}
|
||||
|
||||
const actionStore = get(layoutManagerActionStore);
|
||||
const actionTriggerUuid = this.coWebsitesActionTriggerByPlace.get(place);
|
||||
|
||||
if (!actionTriggerUuid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const action =
|
||||
actionStore && actionStore.length > 0
|
||||
? actionStore.find((action) => action.uuid === actionTriggerUuid)
|
||||
: undefined;
|
||||
|
||||
if (action) {
|
||||
layoutManagerActionStore.removeAction(actionTriggerUuid);
|
||||
}
|
||||
|
||||
this.coWebsitesActionTriggerByPlace.delete(place);
|
||||
}
|
||||
|
||||
private handleFocusablePropertiesOnLeave(place: ITiledPlace): void {
|
||||
if (!place.properties) {
|
||||
return;
|
||||
}
|
||||
const focusable = place.properties.find((property) => property.name === GameMapProperties.FOCUSABLE);
|
||||
if (focusable && focusable.value === true) {
|
||||
this.scene.getCameraManager().leaveFocusMode(this.scene.CurrentPlayer, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +185,6 @@ export class GameScene extends DirtyScene {
|
||||
private biggestAvailableAreaStoreUnsubscribe!: () => void;
|
||||
MapUrlFile: string;
|
||||
roomUrl: string;
|
||||
instance: string;
|
||||
|
||||
currentTick!: number;
|
||||
lastSentTick!: number; // The last tick at which a position was sent.
|
||||
@@ -234,7 +233,6 @@ export class GameScene extends DirtyScene {
|
||||
});
|
||||
this.Terrains = [];
|
||||
this.groups = new Map<number, Sprite>();
|
||||
this.instance = room.getInstance();
|
||||
|
||||
this.MapUrlFile = MapUrlFile;
|
||||
this.roomUrl = room.key;
|
||||
@@ -909,38 +907,15 @@ export class GameScene extends DirtyScene {
|
||||
});
|
||||
});
|
||||
|
||||
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(
|
||||
{
|
||||
x: zone.x + zone.width * 0.5,
|
||||
y: zone.y + zone.height * 0.5,
|
||||
width: zone.width,
|
||||
height: zone.height,
|
||||
},
|
||||
zoomMargin ? Math.max(0, Number(zoomMargin.value)) : undefined
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
zones.forEach((zone) => {
|
||||
iframeListener.sendEnterZoneEvent(zone.name);
|
||||
this.gameMap.onEnterArea((areas) => {
|
||||
areas.forEach((area) => {
|
||||
iframeListener.sendEnterAreaEvent(area.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, 1000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
zones.forEach((zone) => {
|
||||
iframeListener.sendLeaveZoneEvent(zone.name);
|
||||
this.gameMap.onLeaveArea((areas) => {
|
||||
areas.forEach((area) => {
|
||||
iframeListener.sendLeaveAreaEvent(area.name);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -9,4 +9,21 @@ export class StringUtils {
|
||||
}
|
||||
return { x: values[0], y: values[1] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a "short URL" hash of the string passed in parameter.
|
||||
*/
|
||||
public static shortHash = function (s: string): string {
|
||||
let hash = 0;
|
||||
const strLength = s.length;
|
||||
if (strLength === 0) {
|
||||
return "";
|
||||
}
|
||||
for (let i = 0; i < strLength; i++) {
|
||||
const c = s.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + c;
|
||||
hash = hash & hash; // Convert to 32bit integer
|
||||
}
|
||||
return Math.abs(hash).toString(36);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { get } from "svelte/store";
|
||||
import CancelablePromise from "cancelable-promise";
|
||||
import { gameManager } from "../Phaser/Game/GameManager";
|
||||
import { jitsiParticipantsCountStore, userIsJitsiDominantSpeakerStore } from "../Stores/GameStore";
|
||||
import { StringUtils } from "../Utils/StringUtils";
|
||||
|
||||
interface jitsiConfigInterface {
|
||||
startWithAudioMuted: boolean;
|
||||
@@ -120,7 +121,7 @@ const slugify = (...args: (string | number)[]): string => {
|
||||
.replace(/[\u0300-\u036f]/g, "") // remove all previously split accents
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/[^a-z0-9 ]/g, "") // remove all chars not letters, numbers and spaces (to be replaced)
|
||||
.replace(/[^a-z0-9-_ ]/g, "") // remove all chars not letters, numbers, dash, underscores and spaces (to be replaced)
|
||||
.replace(/\s+/g, "-"); // separator
|
||||
};
|
||||
|
||||
@@ -135,8 +136,8 @@ class JitsiFactory {
|
||||
/**
|
||||
* Slugifies the room name and prepends the room name with the instance
|
||||
*/
|
||||
public getRoomName(roomName: string, instance: string): string {
|
||||
return slugify(instance.replace("/", "-") + "-" + roomName);
|
||||
public getRoomName(roomName: string, roomId: string, addPrefix: boolean): string {
|
||||
return slugify((addPrefix ? StringUtils.shortHash(roomId) + "-" : "") + roomName);
|
||||
}
|
||||
|
||||
public start(
|
||||
|
||||
@@ -58,11 +58,11 @@ const menu: NonNullable<Translation["menu"]> = {
|
||||
title: "Sprache",
|
||||
},
|
||||
privacySettings: {
|
||||
title: "Einstellungen Abwesenheitsmodus",
|
||||
title: "Away mode", //TODO: translate
|
||||
explanation:
|
||||
"Falls der WorkAdventure Tab nicht aktiv ist wird in den Abwesenheitsmodus umgeschaltet. Für diesen Modus kann eingestellt werden, ob die Kamera und/oder das Mikrofon deaktiviert sind solange der Tab nicht sichtbar ist.",
|
||||
cameraToggle: "Kamera",
|
||||
microphoneToggle: "Mikrofon",
|
||||
'While the WorkAdventure tab in your browser is not visible. WorkAdventure switches in "away mode.', //TODO: translate
|
||||
cameraToggle: "Keep camera active in away mode", //TODO: translate
|
||||
microphoneToggle: "Keep camera active in away mode", //TODO: translate
|
||||
},
|
||||
save: {
|
||||
warning: "(Das Spiel wird nach dem Speichern neugestartet)",
|
||||
|
||||
@@ -3,17 +3,18 @@ import type { Translation } from "../i18n-types";
|
||||
const report: NonNullable<Translation["report"]> = {
|
||||
block: {
|
||||
title: "Blockieren",
|
||||
content: "Blockiere jede Kommunikation von und zu {userName}. Kann jederzeit rückgängig gemacht werden.",
|
||||
unblock: "Blockierung für diesen User aufheben",
|
||||
block: "Blockiere diese User",
|
||||
content: "Blockiere jegliche Kommunikation mit {userName}. Kann jederzeit rückgängig gemacht werden.",
|
||||
unblock: "Blockierung für diesen Nutzer aufheben",
|
||||
block: "Blockiere diesen Nutzer",
|
||||
},
|
||||
title: "Melden",
|
||||
content: "Verfasse eine Meldung an die Administratoren dieses Raums. Diese können den User anschließend bannen.",
|
||||
content:
|
||||
"Verfasse eine Beschwerde an die Administratoren dieses Raums. Diese können den Nutzer anschließend bannen.",
|
||||
message: {
|
||||
title: "Deine Nachricht: ",
|
||||
empty: "Bitte einen Text angeben.",
|
||||
empty: "Bitte Text eingeben.",
|
||||
},
|
||||
submit: "Diesen User melden",
|
||||
submit: "Diesen Nutzer melden",
|
||||
moderate: {
|
||||
title: "{userName} moderieren",
|
||||
block: "Blockieren",
|
||||
|
||||
@@ -58,11 +58,11 @@ const menu: BaseTranslation = {
|
||||
title: "Language",
|
||||
},
|
||||
privacySettings: {
|
||||
title: "Away mode settings",
|
||||
title: "Away mode",
|
||||
explanation:
|
||||
'When the WorkAdventure tab is not visible, it switches to "away mode". In this mode, you can decide to automatically disable your webcam and/or microphone for as long as the tab stays hidden.',
|
||||
cameraToggle: "Camera",
|
||||
microphoneToggle: "Microphone",
|
||||
'While the WorkAdventure tab in your browser is not visible. WorkAdventure switches to "away mode"',
|
||||
cameraToggle: 'Keep camera active in "away mode"',
|
||||
microphoneToggle: 'Keep microphone active in "away mode"',
|
||||
},
|
||||
save: {
|
||||
warning: "(Saving these settings will restart the game)",
|
||||
|
||||
@@ -58,11 +58,11 @@ const menu: NonNullable<Translation["menu"]> = {
|
||||
title: "Langage",
|
||||
},
|
||||
privacySettings: {
|
||||
title: "Paramètres du mode absent",
|
||||
title: "Mode absent",
|
||||
explanation:
|
||||
"Quand l'onglet WorkAdventure n'est pas visible, vous passez en \"mode absent\". Lorsque ce mode est actif, vous pouvez décider de garder vos webcam et/ou micro désactivés tant que vous ne revenez pas sur l'onglet",
|
||||
cameraToggle: "Camera",
|
||||
microphoneToggle: "Microphone",
|
||||
"Quand l'onglet WorkAdventure de votre navigateur n'est pas visible, WorkAdventure passe en \"mode absent\"",
|
||||
cameraToggle: 'Garder la caméra activée en "mode absent"',
|
||||
microphoneToggle: 'Garder le microphone activé en "mode absent"',
|
||||
},
|
||||
save: {
|
||||
warning: "(La sauvegarde de ces paramètres redémarre le jeu)",
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
//TextGlobalMessage
|
||||
|
||||
// TODO: load on demand with TextGlobalMessage component
|
||||
@import "quill/dist/quill.snow.css";
|
||||
|
||||
section.section-input-send-text {
|
||||
--height-toolbar: 20%;
|
||||
height: 100%;
|
||||
|
||||
.ql-toolbar{
|
||||
max-height: var(--height-toolbar);
|
||||
background: whitesmoke;
|
||||
}
|
||||
|
||||
div.input-send-text{
|
||||
height: calc(100% - var(--height-toolbar));
|
||||
overflow: auto;
|
||||
|
||||
color: whitesmoke;
|
||||
font-size: 1rem;
|
||||
|
||||
.ql-editor.ql-blank::before {
|
||||
color: whitesmoke;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.ql-tooltip {
|
||||
top: 40% !important;
|
||||
left: 20% !important;
|
||||
|
||||
color: whitesmoke;
|
||||
background-color: #333333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
section.section-input-send-text {
|
||||
--height-toolbar: 30%;
|
||||
|
||||
.ql-toolbar {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,3 @@
|
||||
@import "cowebsite.scss";
|
||||
@import "fonts.scss";
|
||||
@import "style";
|
||||
@import "TextGlobalMessageSvelte-Style";
|
||||
|
||||
Reference in New Issue
Block a user