Merge branch 'develop' of ssh://git.bstly.de:222/_Bastler/partey_workadventure

This commit is contained in:
_Bastler
2022-02-20 13:28:29 +01:00
52 changed files with 848 additions and 221 deletions
+28 -29
View File
@@ -1,4 +1,4 @@
import type { IAnalyserNode, IAudioContext, IMediaStreamAudioSourceNode } from "standardized-audio-context";
import { AudioContext, IAnalyserNode, IAudioContext, IMediaStreamAudioSourceNode } from "standardized-audio-context";
/**
* Class to measure the sound volume of a media stream
@@ -6,41 +6,15 @@ import type { IAnalyserNode, IAudioContext, IMediaStreamAudioSourceNode } from "
export class SoundMeter {
private instant: number;
private clip: number;
//private script: ScriptProcessorNode;
private analyser: IAnalyserNode<IAudioContext> | undefined;
private dataArray: Uint8Array | undefined;
private context: IAudioContext | undefined;
private source: IMediaStreamAudioSourceNode<IAudioContext> | undefined;
constructor() {
constructor(mediaStream: MediaStream) {
this.instant = 0.0;
this.clip = 0.0;
//this.script = context.createScriptProcessor(2048, 1, 1);
}
private init(context: IAudioContext) {
this.context = context;
this.analyser = this.context.createAnalyser();
this.analyser.fftSize = 2048;
const bufferLength = this.analyser.fftSize;
this.dataArray = new Uint8Array(bufferLength);
}
public connectToSource(stream: MediaStream, context: IAudioContext): void {
if (this.source !== undefined) {
this.stop();
}
this.init(context);
this.source = this.context?.createMediaStreamSource(stream);
if (this.analyser !== undefined) {
this.source?.connect(this.analyser);
}
//analyser.connect(distortion);
//distortion.connect(this.context.destination);
//this.analyser.connect(this.context.destination);
this.connectToSource(mediaStream, new AudioContext());
}
public getVolume(): number {
@@ -78,4 +52,29 @@ export class SoundMeter {
this.dataArray = undefined;
this.source = undefined;
}
private init(context: IAudioContext) {
this.context = context;
this.analyser = this.context.createAnalyser();
this.analyser.fftSize = 2048;
const bufferLength = this.analyser.fftSize;
this.dataArray = new Uint8Array(bufferLength);
}
private connectToSource(stream: MediaStream, context: IAudioContext): void {
if (this.source !== undefined) {
this.stop();
}
this.init(context);
this.source = this.context?.createMediaStreamSource(stream);
if (this.analyser !== undefined) {
this.source?.connect(this.analyser);
}
//analyser.connect(distortion);
//distortion.connect(this.context.destination);
//this.analyser.connect(this.context.destination);
}
}
+58
View File
@@ -0,0 +1,58 @@
import { Easing } from "../../types";
export class TalkIcon extends Phaser.GameObjects.Image {
private shown: boolean;
private showAnimationTween?: Phaser.Tweens.Tween;
private defaultPosition: { x: number; y: number };
private defaultScale: number;
constructor(scene: Phaser.Scene, x: number, y: number) {
super(scene, x, y, "iconTalk");
this.defaultPosition = { x, y };
this.defaultScale = 0.3;
this.shown = false;
this.setAlpha(0);
this.setScale(this.defaultScale);
this.scene.add.existing(this);
}
public show(show: boolean = true, forceClose: boolean = false): void {
if (this.shown === show && !forceClose) {
return;
}
this.showAnimation(show, forceClose);
}
private showAnimation(show: boolean = true, forceClose: boolean = false) {
if (forceClose && !show) {
this.showAnimationTween?.stop();
} else if (this.showAnimationTween?.isPlaying()) {
return;
}
this.shown = show;
if (show) {
this.y += 50;
this.scale = 0.05;
this.alpha = 0;
}
this.showAnimationTween = this.scene.tweens.add({
targets: [this],
duration: 350,
alpha: show ? 1 : 0,
y: this.defaultPosition.y,
scale: this.defaultScale,
ease: Easing.BackEaseOut,
onComplete: () => {
this.showAnimationTween = undefined;
},
});
}
public isShown(): boolean {
return this.shown;
}
}
+17
View File
@@ -17,6 +17,7 @@ import { Unsubscriber, Writable, writable } from "svelte/store";
import { createColorStore } from "../../Stores/OutlineColorStore";
import type { OutlineableInterface } from "../Game/OutlineableInterface";
import type CancelablePromise from "cancelable-promise";
import { TalkIcon } from "../Components/TalkIcon";
const playerNameY = -25;
@@ -33,6 +34,7 @@ const interactiveRadius = 35;
export abstract class Character extends Container implements OutlineableInterface {
private bubble: SpeechBubble | null = null;
private readonly playerNameText: Text;
private readonly talkIcon: TalkIcon;
public playerName: string;
public sprites: Map<string, Sprite>;
protected lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down;
@@ -102,6 +104,17 @@ export abstract class Character extends Container implements OutlineableInterfac
fontSize: 35,
},
});
this.talkIcon = new TalkIcon(scene, 0, -45);
this.add(this.talkIcon);
if (isClickable) {
this.setInteractive({
hitArea: new Phaser.Geom.Circle(0, 0, interactiveRadius),
hitAreaCallback: Phaser.Geom.Circle.Contains, //eslint-disable-line @typescript-eslint/unbound-method
useHandCursor: true,
});
}
this.playerNameText.setOrigin(0.5).setDepth(DEPTH_INGAME_TEXT_INDEX);
this.add(this.playerNameText);
@@ -200,6 +213,10 @@ export abstract class Character extends Container implements OutlineableInterfac
});
}
public showTalkIcon(show: boolean = true, forceClose: boolean = false): void {
this.talkIcon.show(show, forceClose);
}
public addCompanion(name: string, texturePromise?: CancelablePromise<string>): void {
if (typeof texturePromise !== "undefined") {
this.companion = new Companion(this.scene, this.x, this.y, name, texturePromise);
+2 -1
View File
@@ -7,6 +7,7 @@ import type { PlayerAnimationDirections } from "../Player/Animation";
import type { Unsubscriber } from "svelte/store";
import type { ActivatableInterface } from "../Game/ActivatableInterface";
import type CancelablePromise from "cancelable-promise";
import LL from "../../i18n/i18n-svelte";
/**
* Class representing the sprite of a remote player (a player that plays on another computer)
@@ -107,7 +108,7 @@ export class RemotePlayer extends Character implements ActivatableInterface {
private registerDefaultActionsMenuActions(): void {
if (this.visitCardUrl) {
this.registeredActions.push({
actionName: "Visiting Card",
actionName: LL.woka.menu.businessCard(),
callback: () => {
requestVisitCardsStore.set(this.visitCardUrl);
actionsMenuStore.clear();
+38
View File
@@ -133,6 +133,18 @@ export class GameMap {
return grid;
}
public getWalkingCostGrid(): number[][] {
const grid: number[][] = [];
for (let y = 0; y < this.map.height; y += 1) {
const row: number[] = [];
for (let x = 0; x < this.map.width; x += 1) {
row.push(this.getWalkingCostAt(x, y));
}
grid.push(row);
}
return grid;
}
public getTileDimensions(): { width: number; height: number } {
return { width: this.map.tilewidth, height: this.map.tileheight };
}
@@ -356,6 +368,32 @@ export class GameMap {
return false;
}
private getWalkingCostAt(x: number, y: number): number {
const bigCost = 100;
for (const layer of this.phaserLayers) {
if (!layer.visible) {
continue;
}
const tile = layer.getTileAt(x, y);
if (!tile) {
continue;
}
if (
tile &&
(tile.properties[GameMapProperties.EXIT_URL] || tile.properties[GameMapProperties.EXIT_SCENE_URL])
) {
return bigCost;
}
for (const property of layer.layer.properties) {
//@ts-ignore
if (property.name && property.name === "exitUrl") {
return bigCost;
}
}
}
return 0;
}
private triggerAllProperties(): void {
const newProps = this.getProperties(this.key ?? 0);
const oldProps = this.lastProperties;
@@ -24,6 +24,7 @@ export enum GameMapProperties {
OPEN_WEBSITE_POSITION = "openWebsitePosition",
OPEN_WEBSITE_TRIGGER = "openWebsiteTrigger",
OPEN_WEBSITE_TRIGGER_MESSAGE = "openWebsiteTriggerMessage",
OPEN_WEBSITE_HINT = "openWebsiteHint",
PLAY_AUDIO = "playAudio",
PLAY_AUDIO_LOOP = "playAudioLoop",
READABLE_BY = "readableBy",
@@ -194,6 +194,7 @@ export class GameMapPropertiesListener {
let websitePositionProperty: number | undefined;
let websiteTriggerProperty: string | undefined;
let websiteTriggerMessageProperty: string | undefined;
let websiteHintProperty: string | undefined;
layer.properties.forEach((property) => {
switch (property.name) {
@@ -218,6 +219,9 @@ export class GameMapPropertiesListener {
case GameMapProperties.OPEN_WEBSITE_TRIGGER_MESSAGE:
websiteTriggerMessageProperty = property.value as string | undefined;
break;
case GameMapProperties.OPEN_WEBSITE_HINT:
websiteHintProperty = property.value as string | undefined;
break;
}
});
@@ -251,7 +255,8 @@ export class GameMapPropertiesListener {
allowApiProperty,
websitePolicyProperty,
websiteWidthProperty,
false
false,
websiteHintProperty
);
coWebsiteOpen.coWebsite = coWebsite;
@@ -284,7 +289,8 @@ export class GameMapPropertiesListener {
allowApiProperty,
websitePolicyProperty,
websiteWidthProperty,
false
false,
websiteHintProperty
);
coWebsiteOpen.coWebsite = coWebsite;
+41 -9
View File
@@ -92,6 +92,7 @@ import { followUsersColorStore } from "../../Stores/FollowStore";
import { GameSceneUserInputHandler } from "../UserInput/GameSceneUserInputHandler";
import { locale } from "../../i18n/i18n-svelte";
import { i18nJson } from "../../i18n/locales";
import { localVolumeStore } from "../../Stores/MediaStore";
import { StringUtils } from "../../Utils/StringUtils";
import { startLayerNamesStore } from "../../Stores/StartLayerNamesStore";
import { JitsiCoWebsite } from "../../WebRtc/CoWebsite/JitsiCoWebsite";
@@ -173,6 +174,9 @@ export class GameScene extends DirtyScene {
private peerStoreUnsubscribe!: Unsubscriber;
private emoteUnsubscribe!: Unsubscriber;
private emoteMenuUnsubscribe!: Unsubscriber;
private volumeStoreUnsubscribers: Map<number, Unsubscriber> = new Map<number, Unsubscriber>();
private localVolumeStoreUnsubscriber: Unsubscriber | undefined;
private followUsersColorStoreUnsubscribe!: Unsubscriber;
private biggestAvailableAreaStoreUnsubscribe!: () => void;
@@ -250,6 +254,7 @@ export class GameScene extends DirtyScene {
loadCustomTexture(this.load, texture).catch((e) => console.error(e));
}
}
this.load.image("iconTalk", "/resources/icons/icon_talking.png");
if (touchScreenManager.supportTouchScreen) {
this.load.image(joystickBaseKey, joystickBaseImg);
@@ -585,6 +590,7 @@ export class GameScene extends DirtyScene {
this.pathfindingManager = new PathfindingManager(
this,
this.gameMap.getCollisionGrid(),
this.gameMap.getWalkingCostGrid(),
this.gameMap.getTileDimensions()
);
@@ -600,12 +606,6 @@ export class GameScene extends DirtyScene {
waScaleManager
);
this.pathfindingManager = new PathfindingManager(
this,
this.gameMap.getCollisionGrid(),
this.gameMap.getTileDimensions()
);
this.activatablesManager = new ActivatablesManager(this.CurrentPlayer);
biggestAvailableAreaStore.recompute();
@@ -659,14 +659,45 @@ export class GameScene extends DirtyScene {
this.connect();
}
const talkIconVolumeTreshold = 10;
let oldPeerNumber = 0;
this.peerStoreUnsubscribe = peerStore.subscribe((peers) => {
this.volumeStoreUnsubscribers.forEach((unsubscribe) => unsubscribe());
this.volumeStoreUnsubscribers.clear();
for (const [key, videoStream] of peers) {
this.volumeStoreUnsubscribers.set(
key,
videoStream.volumeStore.subscribe((volume) => {
if (volume) {
this.MapPlayersByKey.get(key)?.showTalkIcon(volume > talkIconVolumeTreshold);
}
})
);
}
const newPeerNumber = peers.size;
if (newPeerNumber > oldPeerNumber) {
this.playSound("audio-webrtc-in");
} else if (newPeerNumber < oldPeerNumber) {
this.playSound("audio-webrtc-out");
}
if (newPeerNumber > 0) {
if (!this.localVolumeStoreUnsubscriber) {
this.localVolumeStoreUnsubscriber = localVolumeStore.subscribe((volume) => {
if (volume) {
this.CurrentPlayer.showTalkIcon(volume > talkIconVolumeTreshold);
}
});
}
} else {
this.CurrentPlayer.showTalkIcon(false, true);
this.MapPlayersByKey.forEach((remotePlayer) => remotePlayer.showTalkIcon(false, true));
if (this.localVolumeStoreUnsubscriber) {
this.localVolumeStoreUnsubscriber();
this.localVolumeStoreUnsubscriber = undefined;
}
}
oldPeerNumber = newPeerNumber;
});
@@ -1218,7 +1249,8 @@ export class GameScene extends DirtyScene {
openCoWebsite.allowApi,
openCoWebsite.allowPolicy,
openCoWebsite.widthPercent,
openCoWebsite.closable ?? true
openCoWebsite.closable ?? true,
openCoWebsite.hint
);
if (openCoWebsite.lazy === undefined || !openCoWebsite.lazy) {
@@ -1450,7 +1482,7 @@ export class GameScene extends DirtyScene {
phaserLayer.setCollisionByProperty({ collides: true }, visible);
} else {
const phaserLayers = this.gameMap.findPhaserLayers(layerName + "/");
if (phaserLayers === []) {
if (phaserLayers.length === 0) {
console.warn(
'Could not find layer with name that contains "' +
layerName +
@@ -1463,7 +1495,7 @@ export class GameScene extends DirtyScene {
phaserLayers[i].setCollisionByProperty({ collides: true }, visible);
}
}
this.pathfindingManager.setCollisionGrid(this.gameMap.getCollisionGrid());
this.pathfindingManager.setCollisionGrid(this.gameMap.getCollisionGrid(), this.gameMap.getWalkingCostGrid());
this.markDirty();
}