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:
Piotr Hanusiak
2022-04-07 14:23:53 +02:00
committed by GitHub
parent 106ee755a8
commit 02f06a913b
15 changed files with 143 additions and 2 deletions
+2
View File
@@ -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;
+15
View File
@@ -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);
}
}
+8 -1
View File
@@ -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);
+4
View File
@@ -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);
+14
View File
@@ -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);
}
}
/**
+1
View File
@@ -7,6 +7,7 @@ export interface PlayerInterface {
visitCardUrl: string | null;
companion: string | null;
userUuid: string;
away: boolean;
color?: string;
outlineColor?: number;
}
+1 -1
View File
@@ -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);
}
+2
View File
@@ -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(),
});