Merge branch 'develop' into feature-options-menu

This commit is contained in:
Hanusiak Piotr 2022-02-02 09:31:10 +01:00
commit 0eaeaf7cfb
20 changed files with 104 additions and 26 deletions

View file

@ -47,6 +47,7 @@
"@types/simple-peer": "^9.11.1", "@types/simple-peer": "^9.11.1",
"@types/socket.io-client": "^1.4.32", "@types/socket.io-client": "^1.4.32",
"axios": "^0.21.2", "axios": "^0.21.2",
"cancelable-promise": "^4.2.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"deep-copy-ts": "^0.5.0", "deep-copy-ts": "^0.5.0",
"easystarjs": "^0.4.4", "easystarjs": "^0.4.4",

View file

@ -1,5 +1,4 @@
<script lang="typescript"> <script lang="typescript">
import { fly } from "svelte/transition";
import { actionsMenuStore } from '../../Stores/ActionsMenuStore'; import { actionsMenuStore } from '../../Stores/ActionsMenuStore';
import { onDestroy } from "svelte"; import { onDestroy } from "svelte";

View file

@ -20,7 +20,7 @@
onMount(() => { onMount(() => {
icon.src = `${ICON_URL}/icon?url=${urlObject.hostname}&size=64..96..256&fallback_icon_color=14304c`; icon.src = `${ICON_URL}/icon?url=${urlObject.hostname}&size=64..96..256&fallback_icon_color=14304c`;
icon.alt = urlObject.hostname; icon.alt = coWebsite.altMessage ?? urlObject.hostname;
icon.onload = () => { icon.onload = () => {
iconLoaded = true; iconLoaded = true;
}; };
@ -204,6 +204,10 @@
border-image-outset: 1; border-image-outset: 1;
} }
&:not(.vertical) {
animation: bounce 0.35s ease 6 alternate;
}
&.vertical { &.vertical {
margin: 7px; margin: 7px;
@ -216,6 +220,8 @@
width: 40px; width: 40px;
height: 40px; height: 40px;
} }
animation: shake 0.35s ease-in-out;
} }
&.displayed { &.displayed {
@ -259,6 +265,41 @@
} }
} }
@keyframes bounce {
from {
transform: translateY(0);
}
to {
transform: translateY(-15px);
}
}
@keyframes shake {
0% {
transform: translateX(0);
}
20% {
transform: translateX(-10px);
}
40% {
transform: translateX(10px);
}
60% {
transform: translateX(-10px);
}
80% {
transform: translateX(10px);
}
100% {
transform: translateX(0);
}
}
.cowebsite-icon { .cowebsite-icon {
width: 50px; width: 50px;
height: 50px; height: 50px;

View file

@ -15,7 +15,7 @@
if (stream) { if (stream) {
embedScreen = { embedScreen = {
type: "streamable", type: "streamable",
embed: stream as unknown as Streamable, embed: peer as unknown as Streamable,
}; };
} }
</script> </script>

View file

@ -44,7 +44,7 @@
pointer-events: auto; pointer-events: auto;
padding: 0; padding: 0;
max-height: 85%; max-height: 200px;
max-width: 85%; max-width: 85%;
&:hover { &:hover {

View file

@ -31,10 +31,10 @@
<div class="rtc-error" /> <div class="rtc-error" />
{/if} {/if}
{#if $streamStore !== null} {#if $streamStore !== null}
<!-- svelte-ignore a11y-media-has-caption -->
<i class="container"> <i class="container">
<span style="background-color: {getColorByString(name)};">{name}</span> <span style="background-color: {getColorByString(name)};">{name}</span>
</i> </i>
<!-- svelte-ignore a11y-media-has-caption -->
<video <video
use:srcObject={$streamStore} use:srcObject={$streamStore}
autoplay autoplay

View file

@ -50,7 +50,12 @@
}); });
</script> </script>
<div class="video-container" class:no-clikable={!clickable} bind:this={videoContainer}> <div
class="video-container"
class:no-clikable={!clickable}
bind:this={videoContainer}
on:click={() => (clickable ? highlightedEmbedScreen.toggleHighlight(embedScreen) : null)}
>
{#if $statusStore === "connecting"} {#if $statusStore === "connecting"}
<div class="connecting-spinner" /> <div class="connecting-spinner" />
{/if} {/if}

View file

@ -16,6 +16,7 @@ import type { PictureStore } from "../../Stores/PictureStore";
import { Unsubscriber, Writable, writable } from "svelte/store"; import { Unsubscriber, Writable, writable } from "svelte/store";
import { createColorStore } from "../../Stores/OutlineColorStore"; import { createColorStore } from "../../Stores/OutlineColorStore";
import type { OutlineableInterface } from '../Game/OutlineableInterface'; import type { OutlineableInterface } from '../Game/OutlineableInterface';
import type CancelablePromise from "cancelable-promise";
const playerNameY = -25; const playerNameY = -25;
@ -45,12 +46,13 @@ export abstract class Character extends Container implements OutlineableInterfac
private readonly _pictureStore: Writable<string | undefined>; private readonly _pictureStore: Writable<string | undefined>;
private readonly outlineColorStore = createColorStore(); private readonly outlineColorStore = createColorStore();
private readonly outlineColorStoreUnsubscribe: Unsubscriber; private readonly outlineColorStoreUnsubscribe: Unsubscriber;
private texturePromise: CancelablePromise<string[] | void> | undefined;
constructor( constructor(
scene: GameScene, scene: GameScene,
x: number, x: number,
y: number, y: number,
texturesPromise: Promise<string[]>, texturesPromise: CancelablePromise<string[]>,
name: string, name: string,
direction: PlayerAnimationDirections, direction: PlayerAnimationDirections,
moving: boolean, moving: boolean,
@ -69,7 +71,7 @@ export abstract class Character extends Container implements OutlineableInterfac
this._pictureStore = writable(undefined); 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 this.texturePromise = texturesPromise
.then((textures) => { .then((textures) => {
this.addTextures(textures, frame); this.addTextures(textures, frame);
this.invisible = false; this.invisible = false;
@ -84,6 +86,9 @@ export abstract class Character extends Container implements OutlineableInterfac
this.invisible = false; this.invisible = false;
this.playAnimation(direction, moving); this.playAnimation(direction, moving);
}); });
})
.finally(() => {
this.texturePromise = undefined;
}); });
this.playerNameText = new Text(scene, 0, playerNameY, name, { this.playerNameText = new Text(scene, 0, playerNameY, name, {
@ -344,6 +349,7 @@ export abstract class Character extends Container implements OutlineableInterfac
this.scene.sys.updateList.remove(sprite); this.scene.sys.updateList.remove(sprite);
} }
} }
this.texturePromise?.cancel();
this.list.forEach((objectContaining) => objectContaining.destroy()); this.list.forEach((objectContaining) => objectContaining.destroy());
this.outlineColorStoreUnsubscribe(); this.outlineColorStoreUnsubscribe();
super.destroy(); super.destroy();

View file

@ -1,6 +1,7 @@
import LoaderPlugin = Phaser.Loader.LoaderPlugin; import LoaderPlugin = Phaser.Loader.LoaderPlugin;
import type { CharacterTexture } from "../../Connexion/LocalUser"; import type { CharacterTexture } from "../../Connexion/LocalUser";
import { BodyResourceDescriptionInterface, LAYERS, PLAYER_RESOURCES } from "./PlayerTextures"; import { BodyResourceDescriptionInterface, LAYERS, PLAYER_RESOURCES } from "./PlayerTextures";
import CancelablePromise from "cancelable-promise";
export interface FrameConfig { export interface FrameConfig {
frameWidth: number; frameWidth: number;
@ -30,7 +31,7 @@ export const loadAllDefaultModels = (load: LoaderPlugin): BodyResourceDescriptio
export const loadCustomTexture = ( export const loadCustomTexture = (
loaderPlugin: LoaderPlugin, loaderPlugin: LoaderPlugin,
texture: CharacterTexture texture: CharacterTexture
): Promise<BodyResourceDescriptionInterface> => { ): CancelablePromise<BodyResourceDescriptionInterface> => {
const name = "customCharacterTexture" + texture.id; const name = "customCharacterTexture" + texture.id;
const playerResourceDescriptor: BodyResourceDescriptionInterface = { name, img: texture.url, level: texture.level }; const playerResourceDescriptor: BodyResourceDescriptionInterface = { name, img: texture.url, level: texture.level };
return createLoadingPromise(loaderPlugin, playerResourceDescriptor, { return createLoadingPromise(loaderPlugin, playerResourceDescriptor, {
@ -42,8 +43,8 @@ export const loadCustomTexture = (
export const lazyLoadPlayerCharacterTextures = ( export const lazyLoadPlayerCharacterTextures = (
loadPlugin: LoaderPlugin, loadPlugin: LoaderPlugin,
texturekeys: Array<string | BodyResourceDescriptionInterface> texturekeys: Array<string | BodyResourceDescriptionInterface>
): Promise<string[]> => { ): CancelablePromise<string[]> => {
const promisesList: Promise<unknown>[] = []; const promisesList: CancelablePromise<unknown>[] = [];
texturekeys.forEach((textureKey: string | BodyResourceDescriptionInterface) => { texturekeys.forEach((textureKey: string | BodyResourceDescriptionInterface) => {
try { try {
//TODO refactor //TODO refactor
@ -60,12 +61,12 @@ export const lazyLoadPlayerCharacterTextures = (
console.error(err); console.error(err);
} }
}); });
let returnPromise: Promise<Array<string | BodyResourceDescriptionInterface>>; let returnPromise: CancelablePromise<Array<string | BodyResourceDescriptionInterface>>;
if (promisesList.length > 0) { if (promisesList.length > 0) {
loadPlugin.start(); loadPlugin.start();
returnPromise = Promise.all(promisesList).then(() => texturekeys); returnPromise = CancelablePromise.all(promisesList).then(() => texturekeys);
} else { } else {
returnPromise = Promise.resolve(texturekeys); returnPromise = CancelablePromise.resolve(texturekeys);
} }
//If the loading fail, we render the default model instead. //If the loading fail, we render the default model instead.
@ -98,10 +99,17 @@ export const createLoadingPromise = (
playerResourceDescriptor: BodyResourceDescriptionInterface, playerResourceDescriptor: BodyResourceDescriptionInterface,
frameConfig: FrameConfig frameConfig: FrameConfig
) => { ) => {
return new Promise<BodyResourceDescriptionInterface>((res, rej) => { return new CancelablePromise<BodyResourceDescriptionInterface>((res, rej, cancel) => {
if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) { if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) {
return res(playerResourceDescriptor); return res(playerResourceDescriptor);
} }
cancel(() => {
loadPlugin.off("loaderror");
loadPlugin.off("filecomplete-spritesheet-" + playerResourceDescriptor.name);
return;
});
loadPlugin.spritesheet(playerResourceDescriptor.name, playerResourceDescriptor.img, frameConfig); loadPlugin.spritesheet(playerResourceDescriptor.name, playerResourceDescriptor.img, frameConfig);
const errorCallback = (file: { src: string }) => { const errorCallback = (file: { src: string }) => {
if (file.src !== playerResourceDescriptor.img) return; if (file.src !== playerResourceDescriptor.img) return;

View file

@ -7,6 +7,7 @@ import type { PointInterface } from "../../Connexion/ConnexionModels";
import type { PlayerAnimationDirections } from "../Player/Animation"; import type { PlayerAnimationDirections } from "../Player/Animation";
import type { Unsubscriber } from 'svelte/store'; import type { Unsubscriber } from 'svelte/store';
import type { ActivatableInterface } from '../Game/ActivatableInterface'; import type { ActivatableInterface } from '../Game/ActivatableInterface';
import type CancelablePromise from "cancelable-promise";
/** /**
* Class representing the sprite of a remote player (a player that plays on another computer) * Class representing the sprite of a remote player (a player that plays on another computer)
@ -27,7 +28,7 @@ export class RemotePlayer extends Character implements ActivatableInterface {
x: number, x: number,
y: number, y: number,
name: string, name: string,
texturesPromise: Promise<string[]>, texturesPromise: CancelablePromise<string[]>,
direction: PlayerAnimationDirections, direction: PlayerAnimationDirections,
moving: boolean, moving: boolean,
visitCardUrl: string | null, visitCardUrl: string | null,

View file

@ -66,6 +66,7 @@ export class GameMapPropertiesListener {
let websitePolicyProperty: string | undefined; let websitePolicyProperty: string | undefined;
let websitePositionProperty: number | undefined; let websitePositionProperty: number | undefined;
let websiteTriggerProperty: string | undefined; let websiteTriggerProperty: string | undefined;
let websiteTriggerMessageProperty: string | undefined;
layer.properties.forEach((property) => { layer.properties.forEach((property) => {
switch (property.name) { switch (property.name) {
@ -84,6 +85,9 @@ export class GameMapPropertiesListener {
case GameMapProperties.OPEN_WEBSITE_TRIGGER: case GameMapProperties.OPEN_WEBSITE_TRIGGER:
websiteTriggerProperty = property.value as string | undefined; websiteTriggerProperty = property.value as string | undefined;
break; break;
case GameMapProperties.OPEN_WEBSITE_TRIGGER_MESSAGE:
websiteTriggerMessageProperty = property.value as string | undefined;
break;
} }
}); });

View file

@ -1270,7 +1270,7 @@ ${escapedMessage}
openCoWebsite.closable ?? true openCoWebsite.closable ?? true
); );
if (openCoWebsite.lazy !== undefined && !openCoWebsite.lazy) { if (openCoWebsite.lazy === undefined || !openCoWebsite.lazy) {
await coWebsiteManager.loadCoWebsite(coWebsite); await coWebsiteManager.loadCoWebsite(coWebsite);
} }

View file

@ -3,11 +3,12 @@ import { localUserStore } from "../../Connexion/LocalUserStore";
import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures"; import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures";
import { loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager"; import { loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager";
import type { CharacterTexture } from "../../Connexion/LocalUser"; import type { CharacterTexture } from "../../Connexion/LocalUser";
import type CancelablePromise from "cancelable-promise";
export abstract class AbstractCharacterScene extends ResizableScene { export abstract class AbstractCharacterScene extends ResizableScene {
loadCustomSceneSelectCharacters(): Promise<BodyResourceDescriptionInterface[]> { loadCustomSceneSelectCharacters(): Promise<BodyResourceDescriptionInterface[]> {
const textures = this.getTextures(); const textures = this.getTextures();
const promises: Promise<BodyResourceDescriptionInterface>[] = []; const promises: CancelablePromise<BodyResourceDescriptionInterface>[] = [];
if (textures) { if (textures) {
for (const texture of textures) { for (const texture of textures) {
if (texture.level === -1) { if (texture.level === -1) {
@ -21,7 +22,7 @@ export abstract class AbstractCharacterScene extends ResizableScene {
loadSelectSceneCharacters(): Promise<BodyResourceDescriptionInterface[]> { loadSelectSceneCharacters(): Promise<BodyResourceDescriptionInterface[]> {
const textures = this.getTextures(); const textures = this.getTextures();
const promises: Promise<BodyResourceDescriptionInterface>[] = []; const promises: CancelablePromise<BodyResourceDescriptionInterface>[] = [];
if (textures) { if (textures) {
for (const texture of textures) { for (const texture of textures) {
if (texture.level !== -1) { if (texture.level !== -1) {

View file

@ -289,7 +289,6 @@ export class CustomizeScene extends AbstractCharacterScene {
gameManager.setCharacterLayers(layers); gameManager.setCharacterLayers(layers);
this.scene.sleep(CustomizeSceneName); this.scene.sleep(CustomizeSceneName);
waScaleManager.restoreZoom(); waScaleManager.restoreZoom();
this.events.removeListener("wake");
gameManager.tryResumingGame(EnableCameraSceneName); gameManager.tryResumingGame(EnableCameraSceneName);
customCharacterSceneVisibleStore.set(false); customCharacterSceneVisibleStore.set(false);
} }

View file

@ -6,6 +6,7 @@ import { Character } from "../Entity/Character";
import { get } from "svelte/store"; import { get } from "svelte/store";
import { userMovingStore } from "../../Stores/GameStore"; import { userMovingStore } from "../../Stores/GameStore";
import { followStateStore, followRoleStore, followUsersStore } from "../../Stores/FollowStore"; import { followStateStore, followRoleStore, followUsersStore } from "../../Stores/FollowStore";
import type CancelablePromise from "cancelable-promise";
export const hasMovedEventName = "hasMoved"; export const hasMovedEventName = "hasMoved";
export const requestEmoteEventName = "requestEmote"; export const requestEmoteEventName = "requestEmote";
@ -20,7 +21,7 @@ export class Player extends Character {
x: number, x: number,
y: number, y: number,
name: string, name: string,
texturesPromise: Promise<string[]>, texturesPromise: CancelablePromise<string[]>,
direction: PlayerAnimationDirections, direction: PlayerAnimationDirections,
moving: boolean, moving: boolean,
companion: string | null, companion: string | null,

View file

@ -41,8 +41,8 @@ export class WaScaleManager {
this.actualZoom = realSize.width / gameSize.width / devicePixelRatio; this.actualZoom = realSize.width / gameSize.width / devicePixelRatio;
} }
this.scaleManager.setZoom(this.actualZoom);
this.scaleManager.resize(gameSize.width, gameSize.height); this.scaleManager.resize(gameSize.width, gameSize.height);
this.scaleManager.setZoom(this.actualZoom);
// Override bug in canvas resizing in Phaser. Let's resize the canvas ourselves // Override bug in canvas resizing in Phaser. Let's resize the canvas ourselves
const style = this.scaleManager.canvas.style; const style = this.scaleManager.canvas.style;

View file

@ -2,6 +2,7 @@ import { derived, get, Readable } from "svelte/store";
import { ScreenSharingLocalMedia, screenSharingLocalMedia } from "./ScreenSharingStore"; import { ScreenSharingLocalMedia, screenSharingLocalMedia } from "./ScreenSharingStore";
import { peerStore, screenSharingStreamStore } from "./PeerStore"; import { peerStore, screenSharingStreamStore } from "./PeerStore";
import type { RemotePeer } from "../WebRtc/SimplePeer"; import type { RemotePeer } from "../WebRtc/SimplePeer";
import { highlightedEmbedScreen } from "./EmbedScreensStore";
export type Streamable = RemotePeer | ScreenSharingLocalMedia; export type Streamable = RemotePeer | ScreenSharingLocalMedia;
@ -25,6 +26,12 @@ function createStreamableCollectionStore(): Readable<Map<string, Streamable>> {
addPeer($screenSharingLocalMedia); addPeer($screenSharingLocalMedia);
} }
const $highlightedEmbedScreen = get(highlightedEmbedScreen);
if ($highlightedEmbedScreen?.type === "streamable" && !peers.has($highlightedEmbedScreen.embed.uniqueId)) {
highlightedEmbedScreen.removeHighlight();
}
set(peers); set(peers);
} }
); );

View file

@ -44,6 +44,7 @@ export type CoWebsite = {
allowPolicy: string | undefined; allowPolicy: string | undefined;
allowApi: boolean | undefined; allowApi: boolean | undefined;
jitsi?: boolean; jitsi?: boolean;
altMessage?: string;
}; };
class CoWebsiteManager { class CoWebsiteManager {
@ -533,7 +534,8 @@ class CoWebsiteManager {
allowApi?: boolean, allowApi?: boolean,
allowPolicy?: string, allowPolicy?: string,
position?: number, position?: number,
closable?: boolean closable?: boolean,
altMessage?: string
): CoWebsite { ): CoWebsite {
const iframe = document.createElement("iframe"); const iframe = document.createElement("iframe");
const fullUrl = new URL(url, base); const fullUrl = new URL(url, base);
@ -547,6 +549,7 @@ class CoWebsiteManager {
closable: closable ?? false, closable: closable ?? false,
allowPolicy, allowPolicy,
allowApi, allowApi,
altMessage,
}; };
this.initialiseCowebsite(newCoWebsite, position); this.initialiseCowebsite(newCoWebsite, position);

View file

@ -38,8 +38,6 @@ body .message-info.warning{
.video-container { .video-container {
display: flex; display: flex;
transition: all 0.2s ease; transition: all 0.2s ease;
background-color: #00000099;
border-radius: 15px;
cursor: url('./images/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
width: 100%; width: 100%;
@ -55,7 +53,6 @@ body .message-info.warning{
max-width: 100%; max-width: 100%;
width: 100%; width: 100%;
cursor: url('./images/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
border-radius: 15px;
&.mobile{ &.mobile{
width: 100%; width: 100%;

View file

@ -1352,6 +1352,11 @@ camelcase@^6.2.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
cancelable-promise@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/cancelable-promise/-/cancelable-promise-4.2.1.tgz#b02f79c5dde2704acfff1bc1ac2b4090f55541fe"
integrity sha512-PJZ/000ocWhPZQBAuNewAOMA2WEkJ8RhXI6AxeGLiGdW8EYDmumzo9wKyNgjDgxc1q/HbXuTdlcI+wXrOe/jMw==
caniuse-api@^3.0.0: caniuse-api@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"