Actions menu api (#1862)
* wip * wip * random action on click * removing actions * register single key per command use * change removeActionsMenu action name * fixed actions menu not hiding content properly: * actions menu fix * added mock Block Player action * ActionsMenu buttons styling * added displaying priority for menu actions * moved utils actionMenu features to the UI * import as a type: * more object oriented style for API * removed registered actions from RemotePlayer instance * readme update * Fixing typos / Improving wording * added instructions on AlterActionsMenu test map Co-authored-by: Hanusiak Piotr <piotr@ltmp.co> Co-authored-by: David Négrier <d.negrier@thecodingmachine.com>
This commit is contained in:
parent
55db6a9b12
commit
d4dcd0d5ce
@ -162,3 +162,34 @@ class ActionMessage {
|
|||||||
remove() {};
|
remove() {};
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Adding custom ActionsMenu Action
|
||||||
|
|
||||||
|
When clicking on other player's WOKA, the contextual menu (we call it ActionsMenu) is displayed with some default Actions. It is possible to add custom actions right when player is clicked:
|
||||||
|
|
||||||
|
<div class="col">
|
||||||
|
<img src="images/actions-menu-1.png" class="figure-img img-fluid rounded" alt="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
To do that, we need to listen for the `onRemotePlayerClicked` stream and make use of the `remotePlayer` object that is passed by as a payload.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
WA.ui.onRemotePlayerClicked.subscribe((remotePlayer) => {
|
||||||
|
remotePlayer.addAction('Ask to tell a joke', () => {
|
||||||
|
console.log('I am NOT telling you a joke!');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`remotePlayer.addAction(actionName, callback)` returns an Action object, which can remove itself from ActionsMenu:
|
||||||
|
```javascript
|
||||||
|
const action = remotePlayer.addAction('This will disappear!', () => {
|
||||||
|
console.log('You managed to click me!');
|
||||||
|
});
|
||||||
|
setTimeout(
|
||||||
|
() => {
|
||||||
|
action.remove();
|
||||||
|
},
|
||||||
|
1000,
|
||||||
|
);
|
||||||
|
```
|
||||||
|
BIN
docs/maps/images/actions-menu-1.png
Normal file
BIN
docs/maps/images/actions-menu-1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
12
front/src/Api/Events/ActionsMenuActionClickedEvent.ts
Normal file
12
front/src/Api/Events/ActionsMenuActionClickedEvent.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import * as tg from "generic-type-guard";
|
||||||
|
|
||||||
|
export const isActionsMenuActionClickedEvent = new tg.IsInterface()
|
||||||
|
.withProperties({
|
||||||
|
id: tg.isNumber,
|
||||||
|
actionName: tg.isString,
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
|
||||||
|
export type ActionsMenuActionClickedEvent = tg.GuardedType<typeof isActionsMenuActionClickedEvent>;
|
||||||
|
|
||||||
|
export type ActionsMenuActionClickedEventCallback = (event: ActionsMenuActionClickedEvent) => void;
|
12
front/src/Api/Events/AddActionsMenuKeyToRemotePlayerEvent.ts
Normal file
12
front/src/Api/Events/AddActionsMenuKeyToRemotePlayerEvent.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import * as tg from "generic-type-guard";
|
||||||
|
|
||||||
|
export const isAddActionsMenuKeyToRemotePlayerEvent = new tg.IsInterface()
|
||||||
|
.withProperties({
|
||||||
|
id: tg.isNumber,
|
||||||
|
actionKey: tg.isString,
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
|
||||||
|
export type AddActionsMenuKeyToRemotePlayerEvent = tg.GuardedType<typeof isAddActionsMenuKeyToRemotePlayerEvent>;
|
||||||
|
|
||||||
|
export type AddActionsMenuKeyToRemotePlayerEventCallback = (event: AddActionsMenuKeyToRemotePlayerEvent) => void;
|
@ -36,6 +36,10 @@ import type { CameraFollowPlayerEvent } from "./CameraFollowPlayerEvent";
|
|||||||
import { isColorEvent } from "./ColorEvent";
|
import { isColorEvent } from "./ColorEvent";
|
||||||
import { isMovePlayerToEventConfig } from "./MovePlayerToEvent";
|
import { isMovePlayerToEventConfig } from "./MovePlayerToEvent";
|
||||||
import { isMovePlayerToEventAnswer } from "./MovePlayerToEventAnswer";
|
import { isMovePlayerToEventAnswer } from "./MovePlayerToEventAnswer";
|
||||||
|
import type { RemotePlayerClickedEvent } from "./RemotePlayerClickedEvent";
|
||||||
|
import type { AddActionsMenuKeyToRemotePlayerEvent } from "./AddActionsMenuKeyToRemotePlayerEvent";
|
||||||
|
import type { ActionsMenuActionClickedEvent } from "./ActionsMenuActionClickedEvent";
|
||||||
|
import type { RemoveActionsMenuKeyFromRemotePlayerEvent } from "./RemoveActionsMenuKeyFromRemotePlayerEvent";
|
||||||
|
|
||||||
export interface TypedMessageEvent<T> extends MessageEvent {
|
export interface TypedMessageEvent<T> extends MessageEvent {
|
||||||
data: T;
|
data: T;
|
||||||
@ -45,6 +49,8 @@ export interface TypedMessageEvent<T> extends MessageEvent {
|
|||||||
* List event types sent from an iFrame to WorkAdventure
|
* List event types sent from an iFrame to WorkAdventure
|
||||||
*/
|
*/
|
||||||
export type IframeEventMap = {
|
export type IframeEventMap = {
|
||||||
|
addActionsMenuKeyToRemotePlayer: AddActionsMenuKeyToRemotePlayerEvent;
|
||||||
|
removeActionsMenuKeyFromRemotePlayer: RemoveActionsMenuKeyFromRemotePlayerEvent;
|
||||||
loadPage: LoadPageEvent;
|
loadPage: LoadPageEvent;
|
||||||
chat: ChatEvent;
|
chat: ChatEvent;
|
||||||
cameraFollowPlayer: CameraFollowPlayerEvent;
|
cameraFollowPlayer: CameraFollowPlayerEvent;
|
||||||
@ -58,6 +64,7 @@ export type IframeEventMap = {
|
|||||||
displayBubble: null;
|
displayBubble: null;
|
||||||
removeBubble: null;
|
removeBubble: null;
|
||||||
onPlayerMove: undefined;
|
onPlayerMove: undefined;
|
||||||
|
onOpenActionMenu: undefined;
|
||||||
onCameraUpdate: undefined;
|
onCameraUpdate: undefined;
|
||||||
showLayer: LayerEvent;
|
showLayer: LayerEvent;
|
||||||
hideLayer: LayerEvent;
|
hideLayer: LayerEvent;
|
||||||
@ -90,6 +97,8 @@ export interface IframeResponseEventMap {
|
|||||||
enterZoneEvent: ChangeZoneEvent;
|
enterZoneEvent: ChangeZoneEvent;
|
||||||
leaveZoneEvent: ChangeZoneEvent;
|
leaveZoneEvent: ChangeZoneEvent;
|
||||||
buttonClickedEvent: ButtonClickedEvent;
|
buttonClickedEvent: ButtonClickedEvent;
|
||||||
|
remotePlayerClickedEvent: RemotePlayerClickedEvent;
|
||||||
|
actionsMenuActionClickedEvent: ActionsMenuActionClickedEvent;
|
||||||
hasPlayerMoved: HasPlayerMovedEvent;
|
hasPlayerMoved: HasPlayerMovedEvent;
|
||||||
wasCameraUpdated: WasCameraUpdatedEvent;
|
wasCameraUpdated: WasCameraUpdatedEvent;
|
||||||
menuItemClicked: MenuItemClickedEvent;
|
menuItemClicked: MenuItemClickedEvent;
|
||||||
|
15
front/src/Api/Events/RemotePlayerClickedEvent.ts
Normal file
15
front/src/Api/Events/RemotePlayerClickedEvent.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import * as tg from "generic-type-guard";
|
||||||
|
|
||||||
|
// TODO: Change for player Clicked, add all neccessary data
|
||||||
|
export const isRemotePlayerClickedEvent = new tg.IsInterface()
|
||||||
|
.withProperties({
|
||||||
|
id: tg.isNumber,
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A message sent from the game to the iFrame when RemotePlayer is clicked.
|
||||||
|
*/
|
||||||
|
export type RemotePlayerClickedEvent = tg.GuardedType<typeof isRemotePlayerClickedEvent>;
|
||||||
|
|
||||||
|
export type RemotePlayerClickedEventCallback = (event: RemotePlayerClickedEvent) => void;
|
@ -0,0 +1,16 @@
|
|||||||
|
import * as tg from "generic-type-guard";
|
||||||
|
|
||||||
|
export const isRemoveActionsMenuKeyFromRemotePlayerEvent = new tg.IsInterface()
|
||||||
|
.withProperties({
|
||||||
|
id: tg.isNumber,
|
||||||
|
actionKey: tg.isString,
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
|
||||||
|
export type RemoveActionsMenuKeyFromRemotePlayerEvent = tg.GuardedType<
|
||||||
|
typeof isRemoveActionsMenuKeyFromRemotePlayerEvent
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type RemoveActionsMenuKeyFromRemotePlayerEventCallback = (
|
||||||
|
event: RemoveActionsMenuKeyFromRemotePlayerEvent
|
||||||
|
) => void;
|
@ -34,6 +34,16 @@ import type { WasCameraUpdatedEvent } from "./Events/WasCameraUpdatedEvent";
|
|||||||
import type { ChangeZoneEvent } from "./Events/ChangeZoneEvent";
|
import type { ChangeZoneEvent } from "./Events/ChangeZoneEvent";
|
||||||
import { CameraSetEvent, isCameraSetEvent } from "./Events/CameraSetEvent";
|
import { CameraSetEvent, isCameraSetEvent } from "./Events/CameraSetEvent";
|
||||||
import { CameraFollowPlayerEvent, isCameraFollowPlayerEvent } from "./Events/CameraFollowPlayerEvent";
|
import { CameraFollowPlayerEvent, isCameraFollowPlayerEvent } from "./Events/CameraFollowPlayerEvent";
|
||||||
|
import type { RemotePlayerClickedEvent } from "./Events/RemotePlayerClickedEvent";
|
||||||
|
import {
|
||||||
|
AddActionsMenuKeyToRemotePlayerEvent,
|
||||||
|
isAddActionsMenuKeyToRemotePlayerEvent,
|
||||||
|
} from "./Events/AddActionsMenuKeyToRemotePlayerEvent";
|
||||||
|
import type { ActionsMenuActionClickedEvent } from "./Events/ActionsMenuActionClickedEvent";
|
||||||
|
import {
|
||||||
|
isRemoveActionsMenuKeyFromRemotePlayerEvent,
|
||||||
|
RemoveActionsMenuKeyFromRemotePlayerEvent,
|
||||||
|
} from "./Events/RemoveActionsMenuKeyFromRemotePlayerEvent";
|
||||||
|
|
||||||
type AnswererCallback<T extends keyof IframeQueryMap> = (
|
type AnswererCallback<T extends keyof IframeQueryMap> = (
|
||||||
query: IframeQueryMap[T]["query"],
|
query: IframeQueryMap[T]["query"],
|
||||||
@ -63,6 +73,15 @@ class IframeListener {
|
|||||||
private readonly _cameraFollowPlayerStream: Subject<CameraFollowPlayerEvent> = new Subject();
|
private readonly _cameraFollowPlayerStream: Subject<CameraFollowPlayerEvent> = new Subject();
|
||||||
public readonly cameraFollowPlayerStream = this._cameraFollowPlayerStream.asObservable();
|
public readonly cameraFollowPlayerStream = this._cameraFollowPlayerStream.asObservable();
|
||||||
|
|
||||||
|
private readonly _addActionsMenuKeyToRemotePlayerStream: Subject<AddActionsMenuKeyToRemotePlayerEvent> =
|
||||||
|
new Subject();
|
||||||
|
public readonly addActionsMenuKeyToRemotePlayerStream = this._addActionsMenuKeyToRemotePlayerStream.asObservable();
|
||||||
|
|
||||||
|
private readonly _removeActionsMenuKeyFromRemotePlayerEvent: Subject<RemoveActionsMenuKeyFromRemotePlayerEvent> =
|
||||||
|
new Subject();
|
||||||
|
public readonly removeActionsMenuKeyFromRemotePlayerEvent =
|
||||||
|
this._removeActionsMenuKeyFromRemotePlayerEvent.asObservable();
|
||||||
|
|
||||||
private readonly _enablePlayerControlStream: Subject<void> = new Subject();
|
private readonly _enablePlayerControlStream: Subject<void> = new Subject();
|
||||||
public readonly enablePlayerControlStream = this._enablePlayerControlStream.asObservable();
|
public readonly enablePlayerControlStream = this._enablePlayerControlStream.asObservable();
|
||||||
|
|
||||||
@ -241,6 +260,16 @@ class IframeListener {
|
|||||||
this._removeBubbleStream.next();
|
this._removeBubbleStream.next();
|
||||||
} else if (payload.type == "onPlayerMove") {
|
} else if (payload.type == "onPlayerMove") {
|
||||||
this.sendPlayerMove = true;
|
this.sendPlayerMove = true;
|
||||||
|
} else if (
|
||||||
|
payload.type == "addActionsMenuKeyToRemotePlayer" &&
|
||||||
|
isAddActionsMenuKeyToRemotePlayerEvent(payload.data)
|
||||||
|
) {
|
||||||
|
this._addActionsMenuKeyToRemotePlayerStream.next(payload.data);
|
||||||
|
} else if (
|
||||||
|
payload.type == "removeActionsMenuKeyFromRemotePlayer" &&
|
||||||
|
isRemoveActionsMenuKeyFromRemotePlayerEvent(payload.data)
|
||||||
|
) {
|
||||||
|
this._removeActionsMenuKeyFromRemotePlayerEvent.next(payload.data);
|
||||||
} else if (payload.type == "onCameraUpdate") {
|
} else if (payload.type == "onCameraUpdate") {
|
||||||
this._trackCameraUpdateStream.next();
|
this._trackCameraUpdateStream.next();
|
||||||
} else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) {
|
} else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) {
|
||||||
@ -439,6 +468,20 @@ class IframeListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendRemotePlayerClickedEvent(event: RemotePlayerClickedEvent) {
|
||||||
|
this.postMessage({
|
||||||
|
type: "remotePlayerClickedEvent",
|
||||||
|
data: event,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sendActionsMenuActionClickedEvent(event: ActionsMenuActionClickedEvent) {
|
||||||
|
this.postMessage({
|
||||||
|
type: "actionsMenuActionClickedEvent",
|
||||||
|
data: event,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
sendCameraUpdated(event: WasCameraUpdatedEvent) {
|
sendCameraUpdated(event: WasCameraUpdatedEvent) {
|
||||||
this.postMessage({
|
this.postMessage({
|
||||||
type: "wasCameraUpdated",
|
type: "wasCameraUpdated",
|
||||||
|
@ -8,6 +8,12 @@ import { ActionMessage } from "./Ui/ActionMessage";
|
|||||||
import { isMessageReferenceEvent } from "../Events/ui/TriggerActionMessageEvent";
|
import { isMessageReferenceEvent } from "../Events/ui/TriggerActionMessageEvent";
|
||||||
import { Menu } from "./Ui/Menu";
|
import { Menu } from "./Ui/Menu";
|
||||||
import type { RequireOnlyOne } from "../types";
|
import type { RequireOnlyOne } from "../types";
|
||||||
|
import { isRemotePlayerClickedEvent, RemotePlayerClickedEvent } from "../Events/RemotePlayerClickedEvent";
|
||||||
|
import {
|
||||||
|
ActionsMenuActionClickedEvent,
|
||||||
|
isActionsMenuActionClickedEvent,
|
||||||
|
} from "../Events/ActionsMenuActionClickedEvent";
|
||||||
|
import { Observable, Subject } from "rxjs";
|
||||||
|
|
||||||
let popupId = 0;
|
let popupId = 0;
|
||||||
const popups: Map<number, Popup> = new Map<number, Popup>();
|
const popups: Map<number, Popup> = new Map<number, Popup>();
|
||||||
@ -42,7 +48,77 @@ export interface ActionMessageOptions {
|
|||||||
callback: () => void;
|
callback: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RemotePlayerInterface {
|
||||||
|
addAction(key: string, callback: Function): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RemotePlayer implements RemotePlayerInterface {
|
||||||
|
private id: number;
|
||||||
|
|
||||||
|
private actions: Map<string, ActionsMenuAction> = new Map<string, ActionsMenuAction>();
|
||||||
|
|
||||||
|
constructor(id: number) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addAction(key: string, callback: Function): ActionsMenuAction {
|
||||||
|
const newAction = new ActionsMenuAction(this, key, callback);
|
||||||
|
this.actions.set(key, newAction);
|
||||||
|
sendToWorkadventure({
|
||||||
|
type: "addActionsMenuKeyToRemotePlayer",
|
||||||
|
data: { id: this.id, actionKey: key },
|
||||||
|
});
|
||||||
|
return newAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public callAction(key: string): void {
|
||||||
|
const action = this.actions.get(key);
|
||||||
|
if (action) {
|
||||||
|
action.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeAction(key: string): void {
|
||||||
|
this.actions.delete(key);
|
||||||
|
sendToWorkadventure({
|
||||||
|
type: "removeActionsMenuKeyFromRemotePlayer",
|
||||||
|
data: { id: this.id, actionKey: key },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ActionsMenuAction {
|
||||||
|
private remotePlayer: RemotePlayer;
|
||||||
|
private key: string;
|
||||||
|
private callback: Function;
|
||||||
|
|
||||||
|
constructor(remotePlayer: RemotePlayer, key: string, callback: Function) {
|
||||||
|
this.remotePlayer = remotePlayer;
|
||||||
|
this.key = key;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public call(): void {
|
||||||
|
this.callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
public remove(): void {
|
||||||
|
this.remotePlayer.removeAction(this.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventureUiCommands> {
|
export class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventureUiCommands> {
|
||||||
|
public readonly _onRemotePlayerClicked: Subject<RemotePlayerInterface>;
|
||||||
|
public readonly onRemotePlayerClicked: Observable<RemotePlayerInterface>;
|
||||||
|
|
||||||
|
private currentlyClickedRemotePlayer?: RemotePlayer;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this._onRemotePlayerClicked = new Subject<RemotePlayerInterface>();
|
||||||
|
this.onRemotePlayerClicked = this._onRemotePlayerClicked.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
callbacks = [
|
callbacks = [
|
||||||
apiCallback({
|
apiCallback({
|
||||||
type: "buttonClickedEvent",
|
type: "buttonClickedEvent",
|
||||||
@ -82,9 +158,38 @@ export class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventure
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
apiCallback({
|
||||||
|
type: "remotePlayerClickedEvent",
|
||||||
|
typeChecker: isRemotePlayerClickedEvent,
|
||||||
|
callback: (payloadData: RemotePlayerClickedEvent) => {
|
||||||
|
this.currentlyClickedRemotePlayer = new RemotePlayer(payloadData.id);
|
||||||
|
this._onRemotePlayerClicked.next(this.currentlyClickedRemotePlayer);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
apiCallback({
|
||||||
|
type: "actionsMenuActionClickedEvent",
|
||||||
|
typeChecker: isActionsMenuActionClickedEvent,
|
||||||
|
callback: (payloadData: ActionsMenuActionClickedEvent) => {
|
||||||
|
this.currentlyClickedRemotePlayer?.callAction(payloadData.actionName);
|
||||||
|
},
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup {
|
public addActionsMenuKeyToRemotePlayer(id: number, actionKey: string): void {
|
||||||
|
sendToWorkadventure({
|
||||||
|
type: "addActionsMenuKeyToRemotePlayer",
|
||||||
|
data: { id, actionKey },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeActionsMenuKeyFromRemotePlayer(id: number, actionKey: string): void {
|
||||||
|
sendToWorkadventure({
|
||||||
|
type: "removeActionsMenuKeyFromRemotePlayer",
|
||||||
|
data: { id, actionKey },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup {
|
||||||
popupId++;
|
popupId++;
|
||||||
const popup = new Popup(popupId);
|
const popup = new Popup(popupId);
|
||||||
const btnMap = new Map<number, () => void>();
|
const btnMap = new Map<number, () => void>();
|
||||||
@ -119,7 +224,10 @@ export class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventure
|
|||||||
return popup;
|
return popup;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerMenuCommand(commandDescriptor: string, options: MenuOptions | ((commandDescriptor: string) => void)): Menu {
|
public registerMenuCommand(
|
||||||
|
commandDescriptor: string,
|
||||||
|
options: MenuOptions | ((commandDescriptor: string) => void)
|
||||||
|
): Menu {
|
||||||
const menu = new Menu(commandDescriptor);
|
const menu = new Menu(commandDescriptor);
|
||||||
|
|
||||||
if (typeof options === "function") {
|
if (typeof options === "function") {
|
||||||
@ -168,15 +276,15 @@ export class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventure
|
|||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
displayBubble(): void {
|
public displayBubble(): void {
|
||||||
sendToWorkadventure({ type: "displayBubble", data: null });
|
sendToWorkadventure({ type: "displayBubble", data: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
removeBubble(): void {
|
public removeBubble(): void {
|
||||||
sendToWorkadventure({ type: "removeBubble", data: null });
|
sendToWorkadventure({ type: "removeBubble", data: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
displayActionMessage(actionMessageOptions: ActionMessageOptions): ActionMessage {
|
public displayActionMessage(actionMessageOptions: ActionMessageOptions): ActionMessage {
|
||||||
const actionMessage = new ActionMessage(actionMessageOptions, () => {
|
const actionMessage = new ActionMessage(actionMessageOptions, () => {
|
||||||
actionMessages.delete(actionMessage.uuid);
|
actionMessages.delete(actionMessage.uuid);
|
||||||
});
|
});
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
import { actionsMenuStore } from "../../Stores/ActionsMenuStore";
|
import { actionsMenuStore } from "../../Stores/ActionsMenuStore";
|
||||||
import { onDestroy } from "svelte";
|
import { onDestroy } from "svelte";
|
||||||
|
|
||||||
|
import type { ActionsMenuAction } from "../../Stores/ActionsMenuStore";
|
||||||
import type { Unsubscriber } from "svelte/store";
|
import type { Unsubscriber } from "svelte/store";
|
||||||
import type { ActionsMenuData } from "../../Stores/ActionsMenuStore";
|
import type { ActionsMenuData } from "../../Stores/ActionsMenuStore";
|
||||||
|
|
||||||
let actionsMenuData: ActionsMenuData | undefined;
|
let actionsMenuData: ActionsMenuData | undefined;
|
||||||
|
let sortedActions: ActionsMenuAction[] | undefined;
|
||||||
|
|
||||||
let actionsMenuStoreUnsubscriber: Unsubscriber | null;
|
let actionsMenuStoreUnsubscriber: Unsubscriber | null;
|
||||||
|
|
||||||
@ -21,6 +23,20 @@
|
|||||||
|
|
||||||
actionsMenuStoreUnsubscriber = actionsMenuStore.subscribe((value) => {
|
actionsMenuStoreUnsubscriber = actionsMenuStore.subscribe((value) => {
|
||||||
actionsMenuData = value;
|
actionsMenuData = value;
|
||||||
|
if (actionsMenuData) {
|
||||||
|
sortedActions = [...actionsMenuData.actions.values()].sort((a, b) => {
|
||||||
|
const ap = a.priority ?? 0;
|
||||||
|
const bp = b.priority ?? 0;
|
||||||
|
if (ap > bp) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ap < bp) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
@ -37,15 +53,15 @@
|
|||||||
<button type="button" class="nes-btn is-error close" on:click={closeActionsMenu}>×</button>
|
<button type="button" class="nes-btn is-error close" on:click={closeActionsMenu}>×</button>
|
||||||
<h2>{actionsMenuData.playerName}</h2>
|
<h2>{actionsMenuData.playerName}</h2>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
{#each [...actionsMenuData.actions] as { actionName, callback }}
|
{#each sortedActions ?? [] as action}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="nes-btn"
|
class="nes-btn {action.style ?? ''}"
|
||||||
on:click|preventDefault={() => {
|
on:click|preventDefault={() => {
|
||||||
callback();
|
action.callback();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{actionName}
|
{action.actionName}
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
@ -68,7 +84,7 @@
|
|||||||
color: whitesmoke;
|
color: whitesmoke;
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
max-height: calc(100% - 50px);
|
max-height: 30vh;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { requestVisitCardsStore } from "../../Stores/GameStore";
|
import { requestVisitCardsStore } from "../../Stores/GameStore";
|
||||||
import { ActionsMenuData, actionsMenuStore } from "../../Stores/ActionsMenuStore";
|
import { ActionsMenuAction, ActionsMenuData, actionsMenuStore } from "../../Stores/ActionsMenuStore";
|
||||||
import { Character } from "../Entity/Character";
|
import { Character } from "../Entity/Character";
|
||||||
import type { GameScene } from "../Game/GameScene";
|
import type { GameScene } from "../Game/GameScene";
|
||||||
import type { PointInterface } from "../../Connexion/ConnexionModels";
|
import type { PointInterface } from "../../Connexion/ConnexionModels";
|
||||||
@ -8,21 +8,28 @@ import type { Unsubscriber } from "svelte/store";
|
|||||||
import type { ActivatableInterface } from "../Game/ActivatableInterface";
|
import type { ActivatableInterface } from "../Game/ActivatableInterface";
|
||||||
import type CancelablePromise from "cancelable-promise";
|
import type CancelablePromise from "cancelable-promise";
|
||||||
import LL from "../../i18n/i18n-svelte";
|
import LL from "../../i18n/i18n-svelte";
|
||||||
|
import { blackListManager } from "../../WebRtc/BlackListManager";
|
||||||
|
import { showReportScreenStore } from "../../Stores/ShowReportScreenStore";
|
||||||
|
|
||||||
|
export enum RemotePlayerEvent {
|
||||||
|
Clicked = "Clicked",
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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)
|
||||||
*/
|
*/
|
||||||
export class RemotePlayer extends Character implements ActivatableInterface {
|
export class RemotePlayer extends Character implements ActivatableInterface {
|
||||||
public userId: number;
|
public readonly userId: number;
|
||||||
|
public readonly userUuid: string;
|
||||||
public readonly activationRadius: number;
|
public readonly activationRadius: number;
|
||||||
|
|
||||||
private registeredActions: { actionName: string; callback: Function }[];
|
|
||||||
private visitCardUrl: string | null;
|
private visitCardUrl: string | null;
|
||||||
private isActionsMenuInitialized: boolean = false;
|
private isActionsMenuInitialized: boolean = false;
|
||||||
private actionsMenuStoreUnsubscriber: Unsubscriber;
|
private actionsMenuStoreUnsubscriber: Unsubscriber;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
userId: number,
|
userId: number,
|
||||||
|
userUuid: string,
|
||||||
Scene: GameScene,
|
Scene: GameScene,
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
@ -39,10 +46,9 @@ export class RemotePlayer extends Character implements ActivatableInterface {
|
|||||||
|
|
||||||
//set data
|
//set data
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
|
this.userUuid = userUuid;
|
||||||
this.visitCardUrl = visitCardUrl;
|
this.visitCardUrl = visitCardUrl;
|
||||||
this.registeredActions = [];
|
this.setClickable(this.getDefaultActionsMenuActions().length > 0);
|
||||||
this.registerDefaultActionsMenuActions();
|
|
||||||
this.setClickable(this.registeredActions.length > 0);
|
|
||||||
this.activationRadius = activationRadius ?? 96;
|
this.activationRadius = activationRadius ?? 96;
|
||||||
this.actionsMenuStoreUnsubscriber = actionsMenuStore.subscribe((value: ActionsMenuData | undefined) => {
|
this.actionsMenuStoreUnsubscriber = actionsMenuStore.subscribe((value: ActionsMenuData | undefined) => {
|
||||||
this.isActionsMenuInitialized = value ? true : false;
|
this.isActionsMenuInitialized = value ? true : false;
|
||||||
@ -63,17 +69,19 @@ export class RemotePlayer extends Character implements ActivatableInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public registerActionsMenuAction(action: { actionName: string; callback: Function }): void {
|
public registerActionsMenuAction(action: ActionsMenuAction): void {
|
||||||
this.registeredActions.push(action);
|
actionsMenuStore.addAction({
|
||||||
this.updateIsClickable();
|
...action,
|
||||||
|
priority: action.priority ?? 0,
|
||||||
|
callback: () => {
|
||||||
|
action.callback();
|
||||||
|
actionsMenuStore.clear();
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public unregisterActionsMenuAction(actionName: string) {
|
public unregisterActionsMenuAction(actionName: string) {
|
||||||
const index = this.registeredActions.findIndex((action) => action.actionName === actionName);
|
actionsMenuStore.removeAction(actionName);
|
||||||
if (index !== -1) {
|
|
||||||
this.registeredActions.splice(index, 1);
|
|
||||||
}
|
|
||||||
this.updateIsClickable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public activate(): void {
|
public activate(): void {
|
||||||
@ -90,37 +98,52 @@ export class RemotePlayer extends Character implements ActivatableInterface {
|
|||||||
return this.isClickable();
|
return this.isClickable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateIsClickable(): void {
|
|
||||||
this.setClickable(this.registeredActions.length > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private toggleActionsMenu(): void {
|
private toggleActionsMenu(): void {
|
||||||
if (this.isActionsMenuInitialized) {
|
if (this.isActionsMenuInitialized) {
|
||||||
actionsMenuStore.clear();
|
actionsMenuStore.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
actionsMenuStore.initialize(this.playerName);
|
actionsMenuStore.initialize(this.playerName);
|
||||||
for (const action of this.registeredActions) {
|
for (const action of this.getDefaultActionsMenuActions()) {
|
||||||
actionsMenuStore.addAction(action.actionName, action.callback);
|
actionsMenuStore.addAction(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerDefaultActionsMenuActions(): void {
|
private getDefaultActionsMenuActions(): ActionsMenuAction[] {
|
||||||
|
const actions: ActionsMenuAction[] = [];
|
||||||
if (this.visitCardUrl) {
|
if (this.visitCardUrl) {
|
||||||
this.registeredActions.push({
|
actions.push({
|
||||||
actionName: LL.woka.menu.businessCard(),
|
actionName: LL.woka.menu.businessCard(),
|
||||||
|
protected: true,
|
||||||
|
priority: 1,
|
||||||
callback: () => {
|
callback: () => {
|
||||||
requestVisitCardsStore.set(this.visitCardUrl);
|
requestVisitCardsStore.set(this.visitCardUrl);
|
||||||
actionsMenuStore.clear();
|
actionsMenuStore.clear();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actions.push({
|
||||||
|
actionName: blackListManager.isBlackListed(this.userUuid)
|
||||||
|
? LL.report.block.unblock()
|
||||||
|
: LL.report.block.block(),
|
||||||
|
protected: true,
|
||||||
|
priority: -1,
|
||||||
|
style: "is-error",
|
||||||
|
callback: () => {
|
||||||
|
showReportScreenStore.set({ userId: this.userId, userName: this.name });
|
||||||
|
actionsMenuStore.clear();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bindEventHandlers(): void {
|
private bindEventHandlers(): void {
|
||||||
this.on(Phaser.Input.Events.POINTER_DOWN, (event: Phaser.Input.Pointer) => {
|
this.on(Phaser.Input.Events.POINTER_DOWN, (event: Phaser.Input.Pointer) => {
|
||||||
if (event.downElement.nodeName === "CANVAS" && event.leftButtonDown()) {
|
if (event.downElement.nodeName === "CANVAS" && event.leftButtonDown()) {
|
||||||
this.toggleActionsMenu();
|
this.toggleActionsMenu();
|
||||||
|
this.emit(RemotePlayerEvent.Clicked);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ import { localUserStore } from "../../Connexion/LocalUserStore";
|
|||||||
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
||||||
import { SimplePeer } from "../../WebRtc/SimplePeer";
|
import { SimplePeer } from "../../WebRtc/SimplePeer";
|
||||||
import { Loader } from "../Components/Loader";
|
import { Loader } from "../Components/Loader";
|
||||||
import { RemotePlayer } from "../Entity/RemotePlayer";
|
import { RemotePlayer, RemotePlayerEvent } from "../Entity/RemotePlayer";
|
||||||
import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene";
|
import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene";
|
||||||
import { PlayerAnimationDirections } from "../Player/Animation";
|
import { PlayerAnimationDirections } from "../Player/Animation";
|
||||||
import { hasMovedEventName, Player, requestEmoteEventName } from "../Player/Player";
|
import { hasMovedEventName, Player, requestEmoteEventName } from "../Player/Player";
|
||||||
@ -1108,6 +1108,23 @@ ${escapedMessage}
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.iframeSubscriptionList.push(
|
||||||
|
iframeListener.addActionsMenuKeyToRemotePlayerStream.subscribe((data) => {
|
||||||
|
this.MapPlayersByKey.get(data.id)?.registerActionsMenuAction({
|
||||||
|
actionName: data.actionKey,
|
||||||
|
callback: () => {
|
||||||
|
iframeListener.sendActionsMenuActionClickedEvent({ actionName: data.actionKey, id: data.id });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.iframeSubscriptionList.push(
|
||||||
|
iframeListener.removeActionsMenuKeyFromRemotePlayerEvent.subscribe((data) => {
|
||||||
|
this.MapPlayersByKey.get(data.id)?.unregisterActionsMenuAction(data.actionKey);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
this.iframeSubscriptionList.push(
|
this.iframeSubscriptionList.push(
|
||||||
iframeListener.trackCameraUpdateStream.subscribe(() => {
|
iframeListener.trackCameraUpdateStream.subscribe(() => {
|
||||||
if (!this.firstCameraUpdateSent) {
|
if (!this.firstCameraUpdateSent) {
|
||||||
@ -1893,6 +1910,7 @@ ${escapedMessage}
|
|||||||
const texturesPromise = lazyLoadPlayerCharacterTextures(this.load, addPlayerData.characterLayers);
|
const texturesPromise = lazyLoadPlayerCharacterTextures(this.load, addPlayerData.characterLayers);
|
||||||
const player = new RemotePlayer(
|
const player = new RemotePlayer(
|
||||||
addPlayerData.userId,
|
addPlayerData.userId,
|
||||||
|
addPlayerData.userUuid,
|
||||||
this,
|
this,
|
||||||
addPlayerData.position.x,
|
addPlayerData.position.x,
|
||||||
addPlayerData.position.y,
|
addPlayerData.position.y,
|
||||||
@ -1920,6 +1938,10 @@ ${escapedMessage}
|
|||||||
this.activatablesManager.handlePointerOutActivatableObject();
|
this.activatablesManager.handlePointerOutActivatableObject();
|
||||||
this.markDirty();
|
this.markDirty();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
player.on(RemotePlayerEvent.Clicked, () => {
|
||||||
|
iframeListener.sendRemotePlayerClickedEvent({ id: player.userId });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
|
|
||||||
|
export type ActionsMenuAction = {
|
||||||
|
actionName: string;
|
||||||
|
callback: Function;
|
||||||
|
protected?: boolean;
|
||||||
|
priority?: number;
|
||||||
|
style?: "is-success" | "is-error" | "is-primary";
|
||||||
|
};
|
||||||
export interface ActionsMenuData {
|
export interface ActionsMenuData {
|
||||||
playerName: string;
|
playerName: string;
|
||||||
actions: { actionName: string; callback: Function }[];
|
actions: Map<string, ActionsMenuAction>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createActionsMenuStore() {
|
function createActionsMenuStore() {
|
||||||
@ -13,21 +20,18 @@ function createActionsMenuStore() {
|
|||||||
initialize: (playerName: string) => {
|
initialize: (playerName: string) => {
|
||||||
set({
|
set({
|
||||||
playerName,
|
playerName,
|
||||||
actions: [],
|
actions: new Map<string, ActionsMenuAction>(),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
addAction: (actionName: string, callback: Function) => {
|
addAction: (action: ActionsMenuAction) => {
|
||||||
update((data) => {
|
update((data) => {
|
||||||
data?.actions.push({ actionName, callback });
|
data?.actions.set(action.actionName, action);
|
||||||
return data;
|
return data;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
removeAction: (actionName: string) => {
|
removeAction: (actionName: string) => {
|
||||||
update((data) => {
|
update((data) => {
|
||||||
const actionIndex = data?.actions.findIndex((action) => action.actionName === actionName);
|
data?.actions.delete(actionName);
|
||||||
if (actionIndex !== undefined && actionIndex != -1) {
|
|
||||||
data?.actions.splice(actionIndex, 1);
|
|
||||||
}
|
|
||||||
return data;
|
return data;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
162
maps/tests/AlterActionMenuApi/map.json
Normal file
162
maps/tests/AlterActionMenuApi/map.json
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":30,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
"height":30,
|
||||||
|
"id":1,
|
||||||
|
"name":"floor",
|
||||||
|
"opacity":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"openWebsite",
|
||||||
|
"type":"string",
|
||||||
|
"value":"script.php"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"openWebsiteAllowApi",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}],
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":30,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":30,
|
||||||
|
"id":6,
|
||||||
|
"name":"furnitures",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":30,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":30,
|
||||||
|
"id":2,
|
||||||
|
"name":"start",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":30,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"draworder":"topdown",
|
||||||
|
"id":3,
|
||||||
|
"name":"floorLayer",
|
||||||
|
"objects":[
|
||||||
|
{
|
||||||
|
"height":19,
|
||||||
|
"id":11,
|
||||||
|
"name":"",
|
||||||
|
"rotation":0,
|
||||||
|
"text":
|
||||||
|
{
|
||||||
|
"text":"1. Log in with at least 2 users",
|
||||||
|
"wrap":true
|
||||||
|
},
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":315.510479739171,
|
||||||
|
"x":320,
|
||||||
|
"y":256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":19,
|
||||||
|
"id":12,
|
||||||
|
"name":"",
|
||||||
|
"rotation":0,
|
||||||
|
"text":
|
||||||
|
{
|
||||||
|
"text":"2. Click on the second user to open ActionsMenu",
|
||||||
|
"wrap":true
|
||||||
|
},
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":388.01,
|
||||||
|
"x":320,
|
||||||
|
"y":288
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":19,
|
||||||
|
"id":13,
|
||||||
|
"name":"",
|
||||||
|
"rotation":0,
|
||||||
|
"text":
|
||||||
|
{
|
||||||
|
"text":"3. Try to add new action when menu is opened",
|
||||||
|
"wrap":true
|
||||||
|
},
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":376.51,
|
||||||
|
"x":320,
|
||||||
|
"y":320
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":19,
|
||||||
|
"id":14,
|
||||||
|
"name":"",
|
||||||
|
"rotation":0,
|
||||||
|
"text":
|
||||||
|
{
|
||||||
|
"text":"4. Click on actions to call them",
|
||||||
|
"wrap":true
|
||||||
|
},
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":376.51,
|
||||||
|
"x":320,
|
||||||
|
"y":352
|
||||||
|
}],
|
||||||
|
"opacity":1,
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":7,
|
||||||
|
"nextobjectid":15,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"openWebsite",
|
||||||
|
"type":"string",
|
||||||
|
"value":"script.php"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"openWebsiteAllowApi",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}],
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.7.2",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[
|
||||||
|
{
|
||||||
|
"columns":11,
|
||||||
|
"firstgid":1,
|
||||||
|
"image":"..\/tileset1.png",
|
||||||
|
"imageheight":352,
|
||||||
|
"imagewidth":352,
|
||||||
|
"margin":0,
|
||||||
|
"name":"tileset1",
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":121,
|
||||||
|
"tileheight":32,
|
||||||
|
"tilewidth":32
|
||||||
|
}],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":"1.6",
|
||||||
|
"width":30
|
||||||
|
}
|
43
maps/tests/AlterActionMenuApi/script.php
Normal file
43
maps/tests/AlterActionMenuApi/script.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<script src="<?php echo $_SERVER["FRONT_URL"] ?>/iframe_api.js"></script>
|
||||||
|
<script>
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
//@ts-ignore
|
||||||
|
WA.onInit().then(() => {
|
||||||
|
const addActionButton = document.getElementById('addActionButton');
|
||||||
|
let lastRemotePlayerClicked;
|
||||||
|
|
||||||
|
WA.ui.onRemotePlayerClicked.subscribe((remotePlayer) => {
|
||||||
|
lastRemotePlayerClicked = remotePlayer;
|
||||||
|
const action = remotePlayer.addAction('This will disappear!', () => {
|
||||||
|
console.log('You managed to click me, young hero');
|
||||||
|
});
|
||||||
|
window.setTimeout(
|
||||||
|
() => {
|
||||||
|
action.remove();
|
||||||
|
},
|
||||||
|
1000,
|
||||||
|
);
|
||||||
|
remotePlayer.addAction('Ask to tell a joke', () => {
|
||||||
|
console.log('I am NOT telling you a joke!');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionButton.addEventListener('click', () => {
|
||||||
|
const randomActionName = (Math.floor(Math.random() * 100)).toString();
|
||||||
|
action = lastRemotePlayerClicked?.addAction(randomActionName, () => {
|
||||||
|
console.log(`called action ${randomActionName}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<button id="addActionButton">Add Action</button>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -259,6 +259,14 @@
|
|||||||
<a href="#" class="testLink" data-testmap="CameraApi/camera_api_test.json" target="_blank">Test camera API</a>
|
<a href="#" class="testLink" data-testmap="CameraApi/camera_api_test.json" target="_blank">Test camera API</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="radio" name="test-alter-action-menu"> Success <input type="radio" name="test-alter-action-menu"> Failure <input type="radio" name="test-alter-action-menu" checked> Pending
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="#" class="testLink" data-testmap="AlterActionMenuApi/map.json" target="_blank">Test Alter ActionMenu API</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<input type="radio" name="test-set-tiles"> Success <input type="radio" name="test-set-tiles"> Failure <input type="radio" name="test-set-tiles" checked> Pending
|
<input type="radio" name="test-set-tiles"> Success <input type="radio" name="test-set-tiles"> Failure <input type="radio" name="test-set-tiles" checked> Pending
|
||||||
|
Loading…
Reference in New Issue
Block a user