diff --git a/front/src/Components/Companion/Companion.svelte b/front/src/Components/Companion/Companion.svelte
new file mode 100644
index 00000000..984e8bba
--- /dev/null
+++ b/front/src/Components/Companion/Companion.svelte
@@ -0,0 +1,39 @@
+
+
+
+
+
diff --git a/front/src/Components/Menu/MenuIcon.svelte b/front/src/Components/Menu/MenuIcon.svelte
index bf34658f..bb5a2df2 100644
--- a/front/src/Components/Menu/MenuIcon.svelte
+++ b/front/src/Components/Menu/MenuIcon.svelte
@@ -1,6 +1,6 @@
+
+
+
+
diff --git a/front/src/Phaser/Companion/Companion.ts b/front/src/Phaser/Companion/Companion.ts
index 75eb844f..f7f010ac 100644
--- a/front/src/Phaser/Companion/Companion.ts
+++ b/front/src/Phaser/Companion/Companion.ts
@@ -1,6 +1,7 @@
import Sprite = Phaser.GameObjects.Sprite;
import Container = Phaser.GameObjects.Container;
import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation";
+import { TexturesHelper } from "../Helpers/TexturesHelper";
export interface CompanionStatus {
x: number;
@@ -39,6 +40,7 @@ export class Companion extends Container {
texturePromise.then((resource) => {
this.addResource(resource);
this.invisible = false;
+ this.emit("texture-loaded");
});
this.scene.physics.world.enableBody(this);
@@ -123,6 +125,22 @@ export class Companion extends Container {
};
}
+ public async getSnapshot(): Promise {
+ 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 {
if (this.invisible) return;
diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts
index 1211a52d..6a8e0752 100644
--- a/front/src/Phaser/Entity/Character.ts
+++ b/front/src/Phaser/Entity/Character.ts
@@ -8,10 +8,10 @@ import { TextureError } from "../../Exception/TextureError";
import { Companion } from "../Companion/Companion";
import type { GameScene } from "../Game/GameScene";
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 { isSilentStore } from "../../Stores/MediaStore";
-import { lazyLoadPlayerCharacterTextures } from "./PlayerTexturesLoadingManager";
+import { lazyLoadPlayerCharacterTextures, loadAllDefaultModels } from "./PlayerTexturesLoadingManager";
+import { TexturesHelper } from "../Helpers/TexturesHelper";
const playerNameY = -25;
@@ -64,6 +64,7 @@ export abstract class Character extends Container {
this.addTextures(textures, frame);
this.invisible = false;
this.playAnimation(direction, moving);
+ this.emit("woka-textures-loaded");
})
.catch(() => {
return lazyLoadPlayerCharacterTextures(scene.load, ["color_22", "eyes_23"]).then((textures) => {
@@ -117,13 +118,28 @@ export abstract class Character extends Container {
}
}
- private getOutlinePlugin(): OutlinePipelinePlugin | undefined {
- return this.scene.plugins.get("rexOutlinePipeline") as unknown as OutlinePipelinePlugin | undefined;
+ public async getSnapshot(): Promise {
+ 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): void {
if (typeof texturePromise !== "undefined") {
this.companion = new Companion(this.scene, this.x, this.y, name, texturePromise);
+ this.companion.once("texture-loaded", () => {
+ this.emit("companion-texture-loaded", this.companion?.getSnapshot());
+ });
}
}
@@ -154,6 +170,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[] {
return [
{
diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts
index 5d0c06ab..558b4d21 100644
--- a/front/src/Phaser/Game/GameScene.ts
+++ b/front/src/Phaser/Game/GameScene.ts
@@ -1,7 +1,54 @@
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 { iframeListener } from "../../Api/IframeListener";
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 {
GroupCreatedUpdatedMessageInterface,
MessageUserJoined,
@@ -12,85 +59,36 @@ import type {
PositionInterface,
RoomJoinedMessageInterface,
} 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 { ProtobufClientUtils } from "../../Network/ProtobufClientUtils";
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 { ItemFactoryInterface } from "../Items/ItemFactoryInterface";
-import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene";
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 { gameManager } from "./GameManager";
-import { GameMap } from "./GameMap";
-import { PlayerMovement } from "./PlayerMovement";
-import { PlayersPositionInterpolator } from "./PlayersPositionInterpolator";
+import { CameraManager } from "./CameraManager";
+import type { HasPlayerMovedEvent } from "../../Api/Events/HasPlayerMovedEvent";
+import type { Character } from "../Entity/Character";
+
+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 { UserWokaPictureStore } from "../../Stores/UserWokaPictureStore";
+import { UserCompanionPictureStore } from "../../Stores/UserCompanionPictureStore";
+
+import EVENT_TYPE = Phaser.Scenes.Events;
import Texture = Phaser.Textures.Texture;
import Sprite = Phaser.GameObjects.Sprite;
import CanvasTexture = Phaser.Textures.CanvasTexture;
import GameObject = Phaser.GameObjects.GameObject;
-import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
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 { CameraManager } from "./CameraManager";
-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 { 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 FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
export interface GameSceneInitInterface {
initPosition: PointInterface | null;
reconnecting: boolean;
@@ -206,6 +204,11 @@ export class GameScene extends DirtyScene {
private objectsByType = new Map();
private embeddedWebsiteManager!: EmbeddedWebsiteManager;
private loader: Loader;
+ private userWokaPictureStores: Map = new Map();
+ private userCompanionPictureStores: Map = new Map<
+ number,
+ UserCompanionPictureStore
+ >();
constructor(private room: Room, MapUrlFile: string, customKey?: string | undefined) {
super({
@@ -339,6 +342,24 @@ export class GameScene extends DirtyScene {
this.loader.addLoader();
}
+ public getUserWokaPictureStore(userId: number) {
+ let store = this.userWokaPictureStores.get(userId);
+ if (!store) {
+ store = new UserWokaPictureStore();
+ this.userWokaPictureStores.set(userId, store);
+ }
+ return store;
+ }
+
+ public getUserCompanionPictureStore(userId: number) {
+ let store = this.userCompanionPictureStores.get(userId);
+ if (!store) {
+ store = new UserCompanionPictureStore();
+ this.userCompanionPictureStores.set(userId, store);
+ }
+ return store;
+ }
+
// FIXME: we need to put a "unknown" instead of a "any" and validate the structure of the JSON we are receiving.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private async onMapLoad(data: any): Promise {
@@ -674,7 +695,6 @@ export class GameScene extends DirtyScene {
this.connection = onConnect.connection;
playersStore.connectToRoomConnection(this.connection);
-
userIsAdminStore.set(this.connection.hasTag("admin"));
this.connection.onUserJoins((message: MessageUserJoined) => {
@@ -1539,6 +1559,14 @@ ${escapedMessage}
this.companion,
this.companion !== null ? lazyLoadCompanionResource(this.load, this.companion) : undefined
);
+ this.CurrentPlayer.once("woka-textures-loaded", () => {
+ this.savePlayerWokaPicture(this.CurrentPlayer, -1);
+ });
+ this.CurrentPlayer.once("companion-texture-loaded", (snapshotPromise: Promise) => {
+ snapshotPromise.then((snapshot: string) => {
+ this.savePlayerCompanionPicture(-1, snapshot);
+ });
+ });
this.CurrentPlayer.on("pointerdown", (pointer: Phaser.Input.Pointer) => {
if (pointer.wasTouch && (pointer.event as TouchEvent).touches.length > 1) {
return; //we don't want the menu to open when pinching on a touch screen.
@@ -1566,6 +1594,15 @@ ${escapedMessage}
this.createCollisionWithPlayer();
}
+ private async savePlayerWokaPicture(character: Character, userId: number): Promise {
+ const htmlImageElementSrc = await character.getSnapshot();
+ this.getUserWokaPictureStore(userId).picture.set(htmlImageElementSrc);
+ }
+
+ private savePlayerCompanionPicture(userId: number, snapshot: string): void {
+ this.getUserCompanionPictureStore(userId).picture.set(snapshot);
+ }
+
pushPlayerPosition(event: HasPlayerMovedEvent) {
if (this.lastMoveEventSent === event) {
return;
@@ -1753,6 +1790,9 @@ ${escapedMessage}
addPlayerData.companion,
addPlayerData.companion !== null ? lazyLoadCompanionResource(this.load, addPlayerData.companion) : undefined
);
+ player.once("woka-textures-loaded", () => {
+ this.savePlayerWokaPicture(player, addPlayerData.userId);
+ });
this.MapPlayers.add(player);
this.MapPlayersByKey.set(player.userId, player);
player.updatePosition(addPlayerData.position);
diff --git a/front/src/Phaser/Helpers/TexturesHelper.ts b/front/src/Phaser/Helpers/TexturesHelper.ts
new file mode 100644
index 00000000..348e957a
--- /dev/null
+++ b/front/src/Phaser/Helpers/TexturesHelper.ts
@@ -0,0 +1,34 @@
+export class TexturesHelper {
+ public static async getSnapshot(
+ scene: Phaser.Scene,
+ ...sprites: { sprite: Phaser.GameObjects.Sprite; frame?: string | number }[]
+ ): Promise {
+ 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((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");
+ }
+ }
+}
diff --git a/front/src/Stores/UserCompanionPictureStore.ts b/front/src/Stores/UserCompanionPictureStore.ts
new file mode 100644
index 00000000..5483ca91
--- /dev/null
+++ b/front/src/Stores/UserCompanionPictureStore.ts
@@ -0,0 +1,8 @@
+import { writable, Writable } from "svelte/store";
+
+/**
+ * A store that contains the player companion picture
+ */
+export class UserCompanionPictureStore {
+ constructor(public picture: Writable = writable(undefined)) {}
+}
diff --git a/front/src/Stores/UserWokaPictureStore.ts b/front/src/Stores/UserWokaPictureStore.ts
new file mode 100644
index 00000000..8422ae50
--- /dev/null
+++ b/front/src/Stores/UserWokaPictureStore.ts
@@ -0,0 +1,8 @@
+import { writable, Writable } from "svelte/store";
+
+/**
+ * A store that contains the player avatar picture
+ */
+export class UserWokaPictureStore {
+ constructor(public picture: Writable = writable(undefined)) {}
+}
diff --git a/front/style/style.scss b/front/style/style.scss
index 1654156d..89437a99 100644
--- a/front/style/style.scss
+++ b/front/style/style.scss
@@ -62,8 +62,7 @@ body .message-info.warning{
background-color: black;
border-radius: 50%;
text-align: center;
- padding-top: 32px;
- font-size: 28px;
+ font-size: 14px;
color: white;
overflow: hidden;
}