Availability indicator (#2044)
* status indicators changing properly * away status wip * updating away status on connection * remove obsolete logs * fix typecheck * minor adjustments * darker outline * Trying darker outline * Apply suggestions from code review * Update pusher/src/Model/Zone.ts * Making the dot smaller * Marking onleavezone as deprecated Co-authored-by: Piotr 'pwh' Hanusiak <p.hanusiak@workadventu.re> Co-authored-by: David Négrier <d.negrier@thecodingmachine.com>
This commit is contained in:
@@ -14,6 +14,7 @@ export interface MessageUserPositionInterface {
|
||||
name: string;
|
||||
characterLayers: BodyResourceDescriptionInterface[];
|
||||
position: PointInterface;
|
||||
away: boolean;
|
||||
visitCardUrl: string | null;
|
||||
companion: string | null;
|
||||
userUuid: string;
|
||||
@@ -29,6 +30,7 @@ export interface MessageUserJoined {
|
||||
name: string;
|
||||
characterLayers: BodyResourceDescriptionInterface[];
|
||||
position: PointInterface;
|
||||
away: boolean;
|
||||
visitCardUrl: string | null;
|
||||
companion: string | null;
|
||||
userUuid: string;
|
||||
|
||||
@@ -518,6 +518,20 @@ export class RoomConnection implements RoomConnection {
|
||||
this.socket.send(bytes);
|
||||
}
|
||||
|
||||
public emitPlayerAway(away: boolean): void {
|
||||
const message = SetPlayerDetailsMessageTsProto.fromPartial({
|
||||
away,
|
||||
});
|
||||
const bytes = ClientToServerMessageTsProto.encode({
|
||||
message: {
|
||||
$case: "setPlayerDetailsMessage",
|
||||
setPlayerDetailsMessage: message,
|
||||
},
|
||||
}).finish();
|
||||
|
||||
this.socket.send(bytes);
|
||||
}
|
||||
|
||||
public emitPlayerOutlineColor(color: number | null) {
|
||||
let message: SetPlayerDetailsMessageTsProto;
|
||||
if (color === null) {
|
||||
@@ -654,6 +668,7 @@ export class RoomConnection implements RoomConnection {
|
||||
characterLayers,
|
||||
visitCardUrl: message.visitCardUrl,
|
||||
position: ProtobufClientUtils.toPointInterface(position),
|
||||
away: message.away,
|
||||
companion: companion ? companion.name : null,
|
||||
userUuid: message.userUuid,
|
||||
outlineColor: message.hasOutline ? message.outlineColor : undefined,
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Easing } from "../../types";
|
||||
|
||||
export class PlayerStatusDot extends Phaser.GameObjects.Container {
|
||||
private graphics: Phaser.GameObjects.Graphics;
|
||||
|
||||
private away: boolean;
|
||||
|
||||
private readonly COLORS = {
|
||||
// online: 0x00ff00,
|
||||
// away: 0xffff00,
|
||||
online: 0x8cc43f,
|
||||
onlineOutline: 0x427a25,
|
||||
away: 0xf5931e,
|
||||
awayOutline: 0x875d13,
|
||||
};
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number) {
|
||||
super(scene, x, y);
|
||||
|
||||
this.away = false;
|
||||
|
||||
this.graphics = this.scene.add.graphics();
|
||||
this.add(this.graphics);
|
||||
this.redraw();
|
||||
|
||||
this.scene.add.existing(this);
|
||||
}
|
||||
|
||||
public setAway(away: boolean = true, instant: boolean = false): void {
|
||||
if (this.away === away) {
|
||||
return;
|
||||
}
|
||||
this.away = away;
|
||||
if (instant) {
|
||||
this.redraw();
|
||||
} else {
|
||||
this.playStatusChangeAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
private playStatusChangeAnimation(): void {
|
||||
this.scale = 1;
|
||||
this.scene.tweens.add({
|
||||
targets: [this],
|
||||
duration: 200,
|
||||
yoyo: true,
|
||||
ease: Easing.BackEaseIn,
|
||||
scale: 0,
|
||||
onYoyo: () => {
|
||||
this.redraw();
|
||||
},
|
||||
onComplete: () => {
|
||||
this.scale = 1;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private redraw(): void {
|
||||
this.graphics.clear();
|
||||
this.graphics.fillStyle(this.away ? this.COLORS.away : this.COLORS.online);
|
||||
this.graphics.lineStyle(1, this.away ? this.COLORS.awayOutline : this.COLORS.onlineOutline);
|
||||
this.graphics.fillCircle(0, 0, 3);
|
||||
this.graphics.strokeCircle(0, 0, 3);
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import type { OutlineableInterface } from "../Game/OutlineableInterface";
|
||||
import type CancelablePromise from "cancelable-promise";
|
||||
import { TalkIcon } from "../Components/TalkIcon";
|
||||
import { Deferred } from "ts-deferred";
|
||||
import { PlayerStatusDot } from "../Components/PlayerStatusDot";
|
||||
|
||||
const playerNameY = -25;
|
||||
const interactiveRadius = 35;
|
||||
@@ -32,6 +33,7 @@ export abstract class Character extends Container implements OutlineableInterfac
|
||||
private bubble: SpeechBubble | null = null;
|
||||
private readonly playerNameText: Text;
|
||||
private readonly talkIcon: TalkIcon;
|
||||
protected readonly statusDot: PlayerStatusDot;
|
||||
public playerName: string;
|
||||
public sprites: Map<string, Sprite>;
|
||||
protected lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down;
|
||||
@@ -137,7 +139,8 @@ export abstract class Character extends Container implements OutlineableInterfac
|
||||
});
|
||||
}
|
||||
this.playerNameText.setOrigin(0.5).setDepth(DEPTH_INGAME_TEXT_INDEX);
|
||||
this.add(this.playerNameText);
|
||||
this.statusDot = new PlayerStatusDot(scene, this.playerNameText.getLeftCenter().x - 6, playerNameY - 1);
|
||||
this.add([this.playerNameText, this.statusDot]);
|
||||
|
||||
this.setClickable(isClickable);
|
||||
|
||||
@@ -238,6 +241,10 @@ export abstract class Character extends Container implements OutlineableInterfac
|
||||
this.talkIcon.show(show, forceClose);
|
||||
}
|
||||
|
||||
public setAwayStatus(away: boolean = true, instant: boolean = false): void {
|
||||
this.statusDot.setAway(away, instant);
|
||||
}
|
||||
|
||||
public addCompanion(name: string, texturePromise?: CancelablePromise<string>): void {
|
||||
if (typeof texturePromise !== "undefined") {
|
||||
this.companion = new Companion(this.scene, this.x, this.y, name, texturePromise);
|
||||
|
||||
@@ -202,6 +202,8 @@ export class GameMap {
|
||||
|
||||
/**
|
||||
* Registers a callback called when the user moves inside another zone.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public onEnterZone(callback: zoneChangeCallback) {
|
||||
this.enterZoneCallbacks.push(callback);
|
||||
@@ -209,6 +211,8 @@ export class GameMap {
|
||||
|
||||
/**
|
||||
* Registers a callback called when the user moves outside another zone.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public onLeaveZone(callback: zoneChangeCallback) {
|
||||
this.leaveZoneCallbacks.push(callback);
|
||||
|
||||
@@ -102,6 +102,7 @@ import CancelablePromise from "cancelable-promise";
|
||||
import { Deferred } from "ts-deferred";
|
||||
import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin";
|
||||
import { PlayerDetailsUpdatedMessage } from "../../Messages/ts-proto-generated/protos/messages";
|
||||
import { privacyShutdownStore } from "../../Stores/PrivacyShutdownStore";
|
||||
export interface GameSceneInitInterface {
|
||||
initPosition: PointInterface | null;
|
||||
reconnecting: boolean;
|
||||
@@ -177,6 +178,7 @@ export class GameScene extends DirtyScene {
|
||||
|
||||
private localVolumeStoreUnsubscriber: Unsubscriber | undefined;
|
||||
private followUsersColorStoreUnsubscribe!: Unsubscriber;
|
||||
private privacyShutdownStoreUnsubscribe!: Unsubscriber;
|
||||
private userIsJitsiDominantSpeakerStoreUnsubscriber!: Unsubscriber;
|
||||
private jitsiParticipantsCountStoreUnsubscriber!: Unsubscriber;
|
||||
|
||||
@@ -705,6 +707,10 @@ export class GameScene extends DirtyScene {
|
||||
}
|
||||
});
|
||||
|
||||
this.privacyShutdownStoreUnsubscribe = privacyShutdownStore.subscribe((away) => {
|
||||
this.connection?.emitPlayerAway(away);
|
||||
});
|
||||
|
||||
Promise.all([
|
||||
this.connectionAnswerPromiseDeferred.promise as Promise<unknown>,
|
||||
...scriptPromises,
|
||||
@@ -763,6 +769,7 @@ export class GameScene extends DirtyScene {
|
||||
characterLayers: message.characterLayers,
|
||||
name: message.name,
|
||||
position: message.position,
|
||||
away: message.away,
|
||||
visitCardUrl: message.visitCardUrl,
|
||||
companion: message.companion,
|
||||
userUuid: message.userUuid,
|
||||
@@ -1568,6 +1575,7 @@ ${escapedMessage}
|
||||
this.emoteUnsubscribe();
|
||||
this.emoteMenuUnsubscribe();
|
||||
this.followUsersColorStoreUnsubscribe();
|
||||
this.privacyShutdownStoreUnsubscribe();
|
||||
this.biggestAvailableAreaStoreUnsubscribe();
|
||||
this.userIsJitsiDominantSpeakerStoreUnsubscriber();
|
||||
this.jitsiParticipantsCountStoreUnsubscriber();
|
||||
@@ -1940,6 +1948,9 @@ ${escapedMessage}
|
||||
if (addPlayerData.outlineColor !== undefined) {
|
||||
player.setApiOutlineColor(addPlayerData.outlineColor);
|
||||
}
|
||||
if (addPlayerData.away !== undefined) {
|
||||
player.setAwayStatus(addPlayerData.away, true);
|
||||
}
|
||||
this.MapPlayers.add(player);
|
||||
this.MapPlayersByKey.set(player.userId, player);
|
||||
player.updatePosition(addPlayerData.position);
|
||||
@@ -2091,6 +2102,9 @@ ${escapedMessage}
|
||||
if (message.details?.showVoiceIndicator !== undefined) {
|
||||
character.showTalkIcon(message.details?.showVoiceIndicator);
|
||||
}
|
||||
if (message.details?.away !== undefined) {
|
||||
character.setAwayStatus(message.details?.away);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@ export interface PlayerInterface {
|
||||
visitCardUrl: string | null;
|
||||
companion: string | null;
|
||||
userUuid: string;
|
||||
away: boolean;
|
||||
color?: string;
|
||||
outlineColor?: number;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export class Player extends Character {
|
||||
companionTexturePromise?: CancelablePromise<string>
|
||||
) {
|
||||
super(Scene, x, y, texturesPromise, name, direction, moving, 1, true, companion, companionTexturePromise);
|
||||
|
||||
this.statusDot.setVisible(false);
|
||||
//the current player model should be push away by other players to prevent conflict
|
||||
this.getBody().setImmovable(false);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ function createPlayersStore() {
|
||||
visitCardUrl: message.visitCardUrl,
|
||||
companion: message.companion,
|
||||
userUuid: message.userUuid,
|
||||
away: message.away,
|
||||
color: getRandomColor(),
|
||||
});
|
||||
return users;
|
||||
@@ -59,6 +60,7 @@ function createPlayersStore() {
|
||||
characterLayers: [],
|
||||
visitCardUrl: null,
|
||||
companion: null,
|
||||
away: false,
|
||||
userUuid: "dummy",
|
||||
color: getRandomColor(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user