Adding event support to items
This commit is contained in:
parent
513244ae1f
commit
ee612f6585
@ -19,6 +19,7 @@ import {isJoinRoomMessageInterface} from "../Model/Websocket/JoinRoomMessage";
|
|||||||
import {isPointInterface, PointInterface} from "../Model/Websocket/PointInterface";
|
import {isPointInterface, PointInterface} from "../Model/Websocket/PointInterface";
|
||||||
import {isWebRtcSignalMessageInterface} from "../Model/Websocket/WebRtcSignalMessage";
|
import {isWebRtcSignalMessageInterface} from "../Model/Websocket/WebRtcSignalMessage";
|
||||||
import {UserInGroupInterface} from "../Model/Websocket/UserInGroupInterface";
|
import {UserInGroupInterface} from "../Model/Websocket/UserInGroupInterface";
|
||||||
|
import {isItemEventMessageInterface} from "../Model/Websocket/ItemEventMessage";
|
||||||
|
|
||||||
enum SockerIoEvent {
|
enum SockerIoEvent {
|
||||||
CONNECTION = "connection",
|
CONNECTION = "connection",
|
||||||
@ -33,7 +34,8 @@ enum SockerIoEvent {
|
|||||||
MESSAGE_ERROR = "message-error",
|
MESSAGE_ERROR = "message-error",
|
||||||
GROUP_CREATE_UPDATE = "group-create-update",
|
GROUP_CREATE_UPDATE = "group-create-update",
|
||||||
GROUP_DELETE = "group-delete",
|
GROUP_DELETE = "group-delete",
|
||||||
SET_PLAYER_DETAILS = "set-player-details"
|
SET_PLAYER_DETAILS = "set-player-details",
|
||||||
|
ITEM_EVENT = 'item-event',
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IoSocketController {
|
export class IoSocketController {
|
||||||
@ -190,7 +192,16 @@ export class IoSocketController {
|
|||||||
}
|
}
|
||||||
return new MessageUserPosition(user.id, player.name, player.character, player.position);
|
return new MessageUserPosition(user.id, player.name, player.character, player.position);
|
||||||
}).filter((item: MessageUserPosition|null) => item !== null);
|
}).filter((item: MessageUserPosition|null) => item !== null);
|
||||||
answerFn(listOfUsers);
|
|
||||||
|
const listOfItems: {[itemId: string]: unknown} = {};
|
||||||
|
for (const [itemId, item] of world.getItemsState().entries()) {
|
||||||
|
listOfItems[itemId] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
answerFn({
|
||||||
|
users: listOfUsers,
|
||||||
|
items: listOfItems
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('An error occurred on "join_room" event');
|
console.error('An error occurred on "join_room" event');
|
||||||
console.error(e);
|
console.error(e);
|
||||||
@ -281,6 +292,29 @@ export class IoSocketController {
|
|||||||
Client.character = playerDetails.character;
|
Client.character = playerDetails.character;
|
||||||
answerFn(Client.userId);
|
answerFn(Client.userId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on(SockerIoEvent.ITEM_EVENT, (itemEvent: unknown) => {
|
||||||
|
if (!isItemEventMessageInterface(itemEvent)) {
|
||||||
|
socket.emit(SockerIoEvent.MESSAGE_ERROR, {message: 'Invalid ITEM_EVENT message.'});
|
||||||
|
console.warn('Invalid ITEM_EVENT message received: ', itemEvent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const Client = (socket as ExSocketInterface);
|
||||||
|
|
||||||
|
socket.to(Client.roomId).emit(SockerIoEvent.ITEM_EVENT, itemEvent);
|
||||||
|
|
||||||
|
const world = this.Worlds.get(Client.roomId);
|
||||||
|
if (!world) {
|
||||||
|
console.error("Could not find world with id '", Client.roomId, "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
world.setItemState(itemEvent.itemId, itemEvent.state);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('An error occurred on "item_event"');
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
back/src/Model/Websocket/ItemEventMessage.ts
Normal file
10
back/src/Model/Websocket/ItemEventMessage.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import * as tg from "generic-type-guard";
|
||||||
|
|
||||||
|
export const isItemEventMessageInterface =
|
||||||
|
new tg.IsInterface().withProperties({
|
||||||
|
itemId: tg.isNumber,
|
||||||
|
event: tg.isString,
|
||||||
|
state: tg.isUnknown,
|
||||||
|
parameters: tg.isUnknown,
|
||||||
|
}).get();
|
||||||
|
export type ItemEventMessageInterface = tg.GuardedType<typeof isItemEventMessageInterface>;
|
@ -27,6 +27,8 @@ export class World {
|
|||||||
private readonly groupUpdatedCallback: GroupUpdatedCallback;
|
private readonly groupUpdatedCallback: GroupUpdatedCallback;
|
||||||
private readonly groupDeletedCallback: GroupDeletedCallback;
|
private readonly groupDeletedCallback: GroupDeletedCallback;
|
||||||
|
|
||||||
|
private itemsState: Map<number, unknown> = new Map<number, unknown>();
|
||||||
|
|
||||||
constructor(connectCallback: ConnectCallback,
|
constructor(connectCallback: ConnectCallback,
|
||||||
disconnectCallback: DisconnectCallback,
|
disconnectCallback: DisconnectCallback,
|
||||||
minDistance: number,
|
minDistance: number,
|
||||||
@ -227,6 +229,14 @@ export class World {
|
|||||||
return Math.sqrt(Math.pow(position2.x - position1.x, 2) + Math.pow(position2.y - position1.y, 2));
|
return Math.sqrt(Math.pow(position2.x - position1.x, 2) + Math.pow(position2.y - position1.y, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setItemState(itemId: number, state: unknown) {
|
||||||
|
this.itemsState.set(itemId, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getItemsState(): Map<number, unknown> {
|
||||||
|
return this.itemsState;
|
||||||
|
}
|
||||||
|
|
||||||
/*getDistancesBetweenGroupUsers(group: Group): Distance[]
|
/*getDistancesBetweenGroupUsers(group: Group): Distance[]
|
||||||
{
|
{
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
"@types/axios": "^0.14.0",
|
"@types/axios": "^0.14.0",
|
||||||
"@types/simple-peer": "^9.6.0",
|
"@types/simple-peer": "^9.6.0",
|
||||||
"@types/socket.io-client": "^1.4.32",
|
"@types/socket.io-client": "^1.4.32",
|
||||||
|
"generic-type-guard": "^3.2.0",
|
||||||
"phaser": "^3.22.0",
|
"phaser": "^3.22.0",
|
||||||
"queue-typescript": "^1.0.1",
|
"queue-typescript": "^1.0.1",
|
||||||
"simple-peer": "^9.6.2",
|
"simple-peer": "^9.6.2",
|
||||||
|
@ -22,6 +22,7 @@ enum EventMessage{
|
|||||||
GROUP_CREATE_UPDATE = "group-create-update",
|
GROUP_CREATE_UPDATE = "group-create-update",
|
||||||
GROUP_DELETE = "group-delete",
|
GROUP_DELETE = "group-delete",
|
||||||
SET_PLAYER_DETAILS = "set-player-details", // Send the name and character to the server (on connect), receive back the id.
|
SET_PLAYER_DETAILS = "set-player-details", // Send the name and character to the server (on connect), receive back the id.
|
||||||
|
ITEM_EVENT = 'item-event',
|
||||||
|
|
||||||
CONNECT_ERROR = "connect_error",
|
CONNECT_ERROR = "connect_error",
|
||||||
}
|
}
|
||||||
@ -91,6 +92,18 @@ export interface StartMapInterface {
|
|||||||
startInstance: string
|
startInstance: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ItemEventMessageInterface {
|
||||||
|
itemId: number,
|
||||||
|
event: string,
|
||||||
|
state: unknown,
|
||||||
|
parameters: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RoomJoinedMessageInterface {
|
||||||
|
users: MessageUserPositionInterface[]
|
||||||
|
items: { [itemId: number] : unknown }
|
||||||
|
}
|
||||||
|
|
||||||
export class Connection implements Connection {
|
export class Connection implements Connection {
|
||||||
private readonly socket: Socket;
|
private readonly socket: Socket;
|
||||||
private userId: string|null = null;
|
private userId: string|null = null;
|
||||||
@ -147,10 +160,10 @@ export class Connection implements Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public joinARoom(roomId: string, startX: number, startY: number, direction: string, moving: boolean): Promise<MessageUserPositionInterface[]> {
|
public joinARoom(roomId: string, startX: number, startY: number, direction: string, moving: boolean): Promise<RoomJoinedMessageInterface> {
|
||||||
const promise = new Promise<MessageUserPositionInterface[]>((resolve, reject) => {
|
const promise = new Promise<RoomJoinedMessageInterface>((resolve, reject) => {
|
||||||
this.socket.emit(EventMessage.JOIN_ROOM, { roomId, position: {x: startX, y: startY, direction, moving }}, (userPositions: MessageUserPositionInterface[]) => {
|
this.socket.emit(EventMessage.JOIN_ROOM, { roomId, position: {x: startX, y: startY, direction, moving }}, (roomJoinedMessage: RoomJoinedMessageInterface) => {
|
||||||
resolve(userPositions);
|
resolve(roomJoinedMessage);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
return promise;
|
return promise;
|
||||||
@ -223,4 +236,17 @@ export class Connection implements Connection {
|
|||||||
disconnectMessage(callback: (message: WebRtcDisconnectMessageInterface) => void): void {
|
disconnectMessage(callback: (message: WebRtcDisconnectMessageInterface) => void): void {
|
||||||
this.socket.on(EventMessage.WEBRTC_DISCONNECT, callback);
|
this.socket.on(EventMessage.WEBRTC_DISCONNECT, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emitActionableEvent(itemId: number, event: string, state: unknown, parameters: unknown) {
|
||||||
|
return this.socket.emit(EventMessage.ITEM_EVENT, {
|
||||||
|
itemId,
|
||||||
|
event,
|
||||||
|
state,
|
||||||
|
parameters
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onActionableEvent(callback: (message: ItemEventMessageInterface) => void): void {
|
||||||
|
this.socket.on(EventMessage.ITEM_EVENT, callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import {
|
|||||||
Connection,
|
Connection,
|
||||||
GroupCreatedUpdatedMessageInterface, MessageUserJoined,
|
GroupCreatedUpdatedMessageInterface, MessageUserJoined,
|
||||||
MessageUserMovedInterface,
|
MessageUserMovedInterface,
|
||||||
MessageUserPositionInterface, PointInterface, PositionInterface
|
MessageUserPositionInterface, PointInterface, PositionInterface, RoomJoinedMessageInterface
|
||||||
} from "../../Connection";
|
} from "../../Connection";
|
||||||
import {CurrentGamerInterface, hasMovedEventName, Player} from "../Player/Player";
|
import {CurrentGamerInterface, hasMovedEventName, Player} from "../Player/Player";
|
||||||
import { DEBUG_MODE, ZOOM_LEVEL, POSITION_DELAY } from "../../Enum/EnvironmentVariable";
|
import { DEBUG_MODE, ZOOM_LEVEL, POSITION_DELAY } from "../../Enum/EnvironmentVariable";
|
||||||
@ -30,6 +30,7 @@ import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
|
|||||||
import {FourOFourSceneName} from "../Reconnecting/FourOFourScene";
|
import {FourOFourSceneName} from "../Reconnecting/FourOFourScene";
|
||||||
import {ItemFactoryInterface} from "../Items/ItemFactoryInterface";
|
import {ItemFactoryInterface} from "../Items/ItemFactoryInterface";
|
||||||
import {ActionableItem} from "../Items/ActionableItem";
|
import {ActionableItem} from "../Items/ActionableItem";
|
||||||
|
import {UserInputManager} from "../UserInput/UserInputManager";
|
||||||
|
|
||||||
|
|
||||||
export enum Textures {
|
export enum Textures {
|
||||||
@ -91,6 +92,8 @@ export class GameScene extends Phaser.Scene {
|
|||||||
private connection: Connection;
|
private connection: Connection;
|
||||||
private simplePeer : SimplePeer;
|
private simplePeer : SimplePeer;
|
||||||
private connectionPromise: Promise<Connection>
|
private connectionPromise: Promise<Connection>
|
||||||
|
private connectionAnswerPromise: Promise<RoomJoinedMessageInterface>;
|
||||||
|
private connectionAnswerPromiseResolve: (value?: RoomJoinedMessageInterface | PromiseLike<RoomJoinedMessageInterface>) => void;
|
||||||
// A promise that will resolve when the "create" method is called (signaling loading is ended)
|
// A promise that will resolve when the "create" method is called (signaling loading is ended)
|
||||||
private createPromise: Promise<void>;
|
private createPromise: Promise<void>;
|
||||||
private createPromiseResolve: (value?: void | PromiseLike<void>) => void;
|
private createPromiseResolve: (value?: void | PromiseLike<void>) => void;
|
||||||
@ -111,9 +114,10 @@ export class GameScene extends Phaser.Scene {
|
|||||||
|
|
||||||
private PositionNextScene: Array<Array<{ key: string, hash: string }>> = new Array<Array<{ key: string, hash: string }>>();
|
private PositionNextScene: Array<Array<{ key: string, hash: string }>> = new Array<Array<{ key: string, hash: string }>>();
|
||||||
private startLayerName: string|undefined;
|
private startLayerName: string|undefined;
|
||||||
private actionableItems: Array<ActionableItem> = new Array<ActionableItem>();
|
private actionableItems: Map<number, ActionableItem> = new Map<number, ActionableItem>();
|
||||||
// The item that can be selected by pressing the space key.
|
// The item that can be selected by pressing the space key.
|
||||||
private outlinedItem: ActionableItem|null = null;
|
private outlinedItem: ActionableItem|null = null;
|
||||||
|
private userInputManager: UserInputManager;
|
||||||
|
|
||||||
static createFromUrl(mapUrlFile: string, instance: string, key: string|null = null): GameScene {
|
static createFromUrl(mapUrlFile: string, instance: string, key: string|null = null): GameScene {
|
||||||
const mapKey = GameScene.getMapKeyByUrl(mapUrlFile);
|
const mapKey = GameScene.getMapKeyByUrl(mapUrlFile);
|
||||||
@ -140,6 +144,9 @@ export class GameScene extends Phaser.Scene {
|
|||||||
this.createPromise = new Promise<void>((resolve, reject): void => {
|
this.createPromise = new Promise<void>((resolve, reject): void => {
|
||||||
this.createPromiseResolve = resolve;
|
this.createPromiseResolve = resolve;
|
||||||
})
|
})
|
||||||
|
this.connectionAnswerPromise = new Promise<RoomJoinedMessageInterface>((resolve, reject): void => {
|
||||||
|
this.connectionAnswerPromiseResolve = resolve;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//hook preload scene
|
//hook preload scene
|
||||||
@ -225,6 +232,15 @@ export class GameScene extends Phaser.Scene {
|
|||||||
this.scene.remove(this.scene.key);
|
this.scene.remove(this.scene.key);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
connection.onActionableEvent((message => {
|
||||||
|
const item = this.actionableItems.get(message.itemId);
|
||||||
|
if (item === undefined) {
|
||||||
|
console.warn('Received an event about object "'+message.itemId+'" but cannot find this item on the map.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
item.fire(message.event, message.state, message.parameters);
|
||||||
|
}));
|
||||||
|
|
||||||
// When connection is performed, let's connect SimplePeer
|
// When connection is performed, let's connect SimplePeer
|
||||||
this.simplePeer = new SimplePeer(this.connection);
|
this.simplePeer = new SimplePeer(this.connection);
|
||||||
|
|
||||||
@ -293,13 +309,19 @@ export class GameScene extends Phaser.Scene {
|
|||||||
this.load.on('complete', () => {
|
this.load.on('complete', () => {
|
||||||
// FIXME: the factory might fail because the resources might not be loaded yet...
|
// FIXME: the factory might fail because the resources might not be loaded yet...
|
||||||
// We would need to add a loader ended event in addition to the createPromise
|
// We would need to add a loader ended event in addition to the createPromise
|
||||||
this.createPromise.then(() => {
|
this.createPromise.then(async () => {
|
||||||
itemFactory.create(this);
|
itemFactory.create(this);
|
||||||
|
|
||||||
|
const roomJoinedAnswer = await this.connectionAnswerPromise;
|
||||||
|
|
||||||
for (const object of objectsOfType) {
|
for (const object of objectsOfType) {
|
||||||
// TODO: we should pass here a factory to create sprites (maybe?)
|
// TODO: we should pass here a factory to create sprites (maybe?)
|
||||||
const actionableItem = itemFactory.factory(this, object);
|
|
||||||
this.actionableItems.push(actionableItem);
|
// Do we have a state for this object?
|
||||||
|
const state = roomJoinedAnswer.items[object.id];
|
||||||
|
|
||||||
|
const actionableItem = itemFactory.factory(this, object, state);
|
||||||
|
this.actionableItems.set(actionableItem.getId(), actionableItem);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -414,13 +436,15 @@ export class GameScene extends Phaser.Scene {
|
|||||||
//initialise list of other player
|
//initialise list of other player
|
||||||
this.MapPlayers = this.physics.add.group({ immovable: true });
|
this.MapPlayers = this.physics.add.group({ immovable: true });
|
||||||
|
|
||||||
|
//create input to move
|
||||||
|
this.userInputManager = new UserInputManager(this);
|
||||||
|
|
||||||
//notify game manager can to create currentUser in map
|
//notify game manager can to create currentUser in map
|
||||||
this.createCurrentPlayer();
|
this.createCurrentPlayer();
|
||||||
|
|
||||||
//initialise camera
|
//initialise camera
|
||||||
this.initCamera();
|
this.initCamera();
|
||||||
|
|
||||||
|
|
||||||
// Let's generate the circle for the group delimiter
|
// Let's generate the circle for the group delimiter
|
||||||
const circleElement = Object.values(this.textures.list).find((object: Texture) => object.key === 'circleSprite');
|
const circleElement = Object.values(this.textures.list).find((object: Texture) => object.key === 'circleSprite');
|
||||||
if(circleElement) {
|
if(circleElement) {
|
||||||
@ -455,6 +479,11 @@ export class GameScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.createPromiseResolve();
|
this.createPromiseResolve();
|
||||||
|
|
||||||
|
// TODO: use inputmanager instead
|
||||||
|
this.input.keyboard.on('keyup-SPACE', () => {
|
||||||
|
this.outlinedItem?.activate();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private getExitSceneUrl(layer: ITiledMapLayer): string|undefined {
|
private getExitSceneUrl(layer: ITiledMapLayer): string|undefined {
|
||||||
@ -605,7 +634,8 @@ export class GameScene extends Phaser.Scene {
|
|||||||
this.GameManager.getPlayerName(),
|
this.GameManager.getPlayerName(),
|
||||||
this.GameManager.getCharacterSelected(),
|
this.GameManager.getCharacterSelected(),
|
||||||
PlayerAnimationNames.WalkDown,
|
PlayerAnimationNames.WalkDown,
|
||||||
false
|
false,
|
||||||
|
this.userInputManager
|
||||||
);
|
);
|
||||||
|
|
||||||
//create collision
|
//create collision
|
||||||
@ -614,8 +644,9 @@ export class GameScene extends Phaser.Scene {
|
|||||||
|
|
||||||
//join room
|
//join room
|
||||||
this.connectionPromise.then((connection: Connection) => {
|
this.connectionPromise.then((connection: Connection) => {
|
||||||
connection.joinARoom(this.RoomId, this.startX, this.startY, PlayerAnimationNames.WalkDown, false).then((userPositions: MessageUserPositionInterface[]) => {
|
connection.joinARoom(this.RoomId, this.startX, this.startY, PlayerAnimationNames.WalkDown, false).then((roomJoinedMessage: RoomJoinedMessageInterface) => {
|
||||||
this.initUsersPosition(userPositions);
|
this.initUsersPosition(roomJoinedMessage.users);
|
||||||
|
this.connectionAnswerPromiseResolve(roomJoinedMessage);
|
||||||
});
|
});
|
||||||
|
|
||||||
//listen event to share position of user
|
//listen event to share position of user
|
||||||
@ -676,7 +707,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
|
|
||||||
let shortestDistance: number = Infinity;
|
let shortestDistance: number = Infinity;
|
||||||
let selectedItem: ActionableItem|null = null;
|
let selectedItem: ActionableItem|null = null;
|
||||||
for (const item of this.actionableItems) {
|
for (const item of this.actionableItems.values()) {
|
||||||
const distance = item.actionableDistance(x, y);
|
const distance = item.actionableDistance(x, y);
|
||||||
if (distance !== null && distance < shortestDistance) {
|
if (distance !== null && distance < shortestDistance) {
|
||||||
shortestDistance = distance;
|
shortestDistance = distance;
|
||||||
@ -766,10 +797,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private checkToExit(): {key: string, hash: string} | null {
|
||||||
*
|
|
||||||
*/
|
|
||||||
checkToExit(): {key: string, hash: string} | null {
|
|
||||||
const x = Math.floor(this.CurrentPlayer.x / 32);
|
const x = Math.floor(this.CurrentPlayer.x / 32);
|
||||||
const y = Math.floor(this.CurrentPlayer.y / 32);
|
const y = Math.floor(this.CurrentPlayer.y / 32);
|
||||||
|
|
||||||
@ -947,4 +975,16 @@ export class GameScene extends Phaser.Scene {
|
|||||||
const endPos = mapUrlStart.indexOf(".json");
|
const endPos = mapUrlStart.indexOf(".json");
|
||||||
return mapUrlStart.substring(startPos, endPos);
|
return mapUrlStart.substring(startPos, endPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends to the server an event emitted by one of the ActionableItems.
|
||||||
|
*
|
||||||
|
* @param itemId
|
||||||
|
* @param eventName
|
||||||
|
* @param state
|
||||||
|
* @param parameters
|
||||||
|
*/
|
||||||
|
emitActionableEvent(itemId: number, eventName: string, state: unknown, parameters: unknown) {
|
||||||
|
this.connection.emitActionableEvent(itemId, eventName, state, parameters);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,23 @@
|
|||||||
*/
|
*/
|
||||||
import Sprite = Phaser.GameObjects.Sprite;
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
import {OutlinePipeline} from "../Shaders/OutlinePipeline";
|
import {OutlinePipeline} from "../Shaders/OutlinePipeline";
|
||||||
|
import {GameScene} from "../Game/GameScene";
|
||||||
|
|
||||||
|
type EventCallback = (state: unknown, parameters: unknown) => void;
|
||||||
|
|
||||||
export class ActionableItem {
|
export class ActionableItem {
|
||||||
private readonly activationRadiusSquared : number;
|
private readonly activationRadiusSquared : number;
|
||||||
private isSelectable: boolean = false;
|
private isSelectable: boolean = false;
|
||||||
|
private callbacks: Map<string, Array<EventCallback>> = new Map<string, Array<EventCallback>>();
|
||||||
|
|
||||||
public constructor(private sprite: Sprite, private activationRadius: number) {
|
public constructor(private id: number, private sprite: Sprite, private eventHandler: GameScene, private activationRadius: number, private onActivateCallback: (item: ActionableItem) => void) {
|
||||||
this.activationRadiusSquared = activationRadius * activationRadius;
|
this.activationRadiusSquared = activationRadius * activationRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getId(): number {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the square of the distance to the object center IF we are in item action range
|
* Returns the square of the distance to the object center IF we are in item action range
|
||||||
* OR null if we are out of range.
|
* OR null if we are out of range.
|
||||||
@ -54,7 +62,31 @@ export class ActionableItem {
|
|||||||
* Triggered when the "space" key is pressed and the object is in range of being activated.
|
* Triggered when the "space" key is pressed and the object is in range of being activated.
|
||||||
*/
|
*/
|
||||||
public activate(): void {
|
public activate(): void {
|
||||||
|
this.onActivateCallback(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public emit(eventName: string, state: unknown, parameters: unknown = null): void {
|
||||||
|
this.eventHandler.emitActionableEvent(this.id, eventName, state, parameters);
|
||||||
|
// Also, execute the action locally.
|
||||||
|
this.fire(eventName, state, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public on(eventName: string, callback: EventCallback): void {
|
||||||
|
let callbacksArray: Array<EventCallback>|undefined = this.callbacks.get(eventName);
|
||||||
|
if (callbacksArray === undefined) {
|
||||||
|
callbacksArray = new Array<EventCallback>();
|
||||||
|
this.callbacks.set(eventName, callbacksArray);
|
||||||
|
}
|
||||||
|
callbacksArray.push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public fire(eventName: string, state: unknown, parameters: unknown): void {
|
||||||
|
const callbacksArray = this.callbacks.get(eventName);
|
||||||
|
if (callbacksArray === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const callback of callbacksArray) {
|
||||||
|
callback(state, parameters);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,20 +5,82 @@ import {ITiledMapObject} from "../../Map/ITiledMap";
|
|||||||
import {ItemFactoryInterface} from "../ItemFactoryInterface";
|
import {ItemFactoryInterface} from "../ItemFactoryInterface";
|
||||||
import {GameScene} from "../../Game/GameScene";
|
import {GameScene} from "../../Game/GameScene";
|
||||||
import {ActionableItem} from "../ActionableItem";
|
import {ActionableItem} from "../ActionableItem";
|
||||||
|
import * as tg from "generic-type-guard";
|
||||||
|
|
||||||
|
const isComputerState =
|
||||||
|
new tg.IsInterface().withProperties({
|
||||||
|
status: tg.isString,
|
||||||
|
}).get();
|
||||||
|
type ComputerState = tg.GuardedType<typeof isComputerState>;
|
||||||
|
|
||||||
|
let state: ComputerState = {
|
||||||
|
'status': 'off'
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
preload: (loader: Phaser.Loader.LoaderPlugin): void => {
|
preload: (loader: Phaser.Loader.LoaderPlugin): void => {
|
||||||
loader.atlas('computer', '/resources/items/computer/computer.png', '/resources/items/computer/computer_atlas.json');
|
loader.atlas('computer', '/resources/items/computer/computer.png', '/resources/items/computer/computer_atlas.json');
|
||||||
},
|
},
|
||||||
create: (scene: GameScene): void => {
|
create: (scene: GameScene): void => {
|
||||||
|
scene.anims.create({
|
||||||
|
key: 'computer_off',
|
||||||
|
frames: [
|
||||||
|
{
|
||||||
|
key: 'computer',
|
||||||
|
frame: 'computer_off'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
frameRate: 10,
|
||||||
|
repeat: -1
|
||||||
|
});
|
||||||
|
scene.anims.create({
|
||||||
|
key: 'computer_run',
|
||||||
|
frames: [
|
||||||
|
{
|
||||||
|
key: 'computer',
|
||||||
|
frame: 'computer_on1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'computer',
|
||||||
|
frame: 'computer_on2'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
frameRate: 5,
|
||||||
|
repeat: -1
|
||||||
|
});
|
||||||
},
|
},
|
||||||
factory: (scene: GameScene, object: ITiledMapObject): ActionableItem => {
|
factory: (scene: GameScene, object: ITiledMapObject, initState: unknown): ActionableItem => {
|
||||||
// Idée: ESSAYER WebPack? https://paultavares.wordpress.com/2018/07/02/webpack-how-to-generate-an-es-module-bundle/
|
if (initState !== undefined) {
|
||||||
const foo = new Sprite(scene, object.x, object.y, 'computer');
|
if (!isComputerState(initState)) {
|
||||||
scene.add.existing(foo);
|
throw new Error('Invalid state received for computer object');
|
||||||
|
}
|
||||||
|
state = initState;
|
||||||
|
}
|
||||||
|
|
||||||
return new ActionableItem(foo, 32);
|
// Idée: ESSAYER WebPack? https://paultavares.wordpress.com/2018/07/02/webpack-how-to-generate-an-es-module-bundle/
|
||||||
|
const computer = new Sprite(scene, object.x, object.y, 'computer');
|
||||||
|
scene.add.existing(computer);
|
||||||
|
if (state.status === 'on') {
|
||||||
|
computer.anims.play('computer_run');
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = new ActionableItem(object.id, computer, scene, 32, (item: ActionableItem) => {
|
||||||
|
if (state.status === 'off') {
|
||||||
|
state.status = 'on';
|
||||||
|
item.emit('TURN_ON', state);
|
||||||
|
} else {
|
||||||
|
state.status = 'off';
|
||||||
|
item.emit('TURN_OFF', state);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item.on('TURN_ON', () => {
|
||||||
|
computer.anims.play('computer_run');
|
||||||
|
});
|
||||||
|
item.on('TURN_OFF', () => {
|
||||||
|
computer.anims.play('computer_off');
|
||||||
|
});
|
||||||
|
|
||||||
|
return item;
|
||||||
//scene.add.sprite(object.x, object.y, 'computer');
|
//scene.add.sprite(object.x, object.y, 'computer');
|
||||||
}
|
}
|
||||||
} as ItemFactoryInterface;
|
} as ItemFactoryInterface;
|
||||||
|
@ -6,5 +6,5 @@ import {ActionableItem} from "./ActionableItem";
|
|||||||
export interface ItemFactoryInterface {
|
export interface ItemFactoryInterface {
|
||||||
preload: (loader: LoaderPlugin) => void;
|
preload: (loader: LoaderPlugin) => void;
|
||||||
create: (scene: GameScene) => void;
|
create: (scene: GameScene) => void;
|
||||||
factory: (scene: GameScene, object: ITiledMapObject) => ActionableItem;
|
factory: (scene: GameScene, object: ITiledMapObject, state: unknown) => ActionableItem;
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,8 @@ export interface CurrentGamerInterface extends Character{
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Player extends Character implements CurrentGamerInterface {
|
export class Player extends Character implements CurrentGamerInterface {
|
||||||
userInputManager: UserInputManager;
|
private previousDirection: string;
|
||||||
previousDirection: string;
|
private wasMoving: boolean;
|
||||||
wasMoving: boolean;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
Scene: GameScene,
|
Scene: GameScene,
|
||||||
@ -24,13 +23,11 @@ export class Player extends Character implements CurrentGamerInterface {
|
|||||||
name: string,
|
name: string,
|
||||||
PlayerTexture: string,
|
PlayerTexture: string,
|
||||||
direction: string,
|
direction: string,
|
||||||
moving: boolean
|
moving: boolean,
|
||||||
|
private userInputManager: UserInputManager
|
||||||
) {
|
) {
|
||||||
super(Scene, x, y, PlayerTexture, name, direction, moving, 1);
|
super(Scene, x, y, PlayerTexture, name, direction, moving, 1);
|
||||||
|
|
||||||
//create input to move
|
|
||||||
this.userInputManager = new UserInputManager(Scene);
|
|
||||||
|
|
||||||
//the current player model should be push away by other players to prevent conflict
|
//the current player model should be push away by other players to prevent conflict
|
||||||
this.setImmovable(false);
|
this.setImmovable(false);
|
||||||
}
|
}
|
||||||
|
@ -1904,6 +1904,11 @@ functional-red-black-tree@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
|
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
|
||||||
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
|
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
|
||||||
|
|
||||||
|
generic-type-guard@^3.2.0:
|
||||||
|
version "3.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/generic-type-guard/-/generic-type-guard-3.2.0.tgz#1fb136f934730c776486526b8a21fe96b067e691"
|
||||||
|
integrity sha512-EkkrXYbOtJ3VPB+SOrU7EhwY65rZErItGtBg5wAqywaj07BOubwOZqMYaxOWekJ9akioGqXIsw1fYk3wwbWsDQ==
|
||||||
|
|
||||||
get-browser-rtc@^1.0.0:
|
get-browser-rtc@^1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/get-browser-rtc/-/get-browser-rtc-1.0.2.tgz#bbcd40c8451a7ed4ef5c373b8169a409dd1d11d9"
|
resolved "https://registry.yarnpkg.com/get-browser-rtc/-/get-browser-rtc-1.0.2.tgz#bbcd40c8451a7ed4ef5c373b8169a409dd1d11d9"
|
||||||
|
Loading…
Reference in New Issue
Block a user