Merge pull request #227 from thecodingmachine/outline
Working on object support in maps
24
.github/workflows/build-and-deploy.yml
vendored
@ -79,6 +79,30 @@ jobs:
|
|||||||
tags: ${{ env.GITHUB_REF_SLUG }}
|
tags: ${{ env.GITHUB_REF_SLUG }}
|
||||||
add_git_labels: true
|
add_git_labels: true
|
||||||
|
|
||||||
|
build-maps:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
|
||||||
|
# Create a slugified value of the branch
|
||||||
|
- uses: rlespinasse/github-slug-action@master
|
||||||
|
|
||||||
|
- name: "Build and push front image"
|
||||||
|
uses: docker/build-push-action@v1
|
||||||
|
with:
|
||||||
|
dockerfile: maps/Dockerfile
|
||||||
|
path: maps/
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
repository: thecodingmachine/workadventure-maps
|
||||||
|
tags: ${{ env.GITHUB_REF_SLUG }}
|
||||||
|
add_git_labels: true
|
||||||
|
|
||||||
deeploy:
|
deeploy:
|
||||||
needs:
|
needs:
|
||||||
- build-front
|
- build-front
|
||||||
|
@ -18,6 +18,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";
|
||||||
import {uuid} from 'uuidv4';
|
import {uuid} from 'uuidv4';
|
||||||
import {isUserMovesInterface} from "../Model/Websocket/UserMovesMessage";
|
import {isUserMovesInterface} from "../Model/Websocket/UserMovesMessage";
|
||||||
import {isViewport} from "../Model/Websocket/ViewportMessage";
|
import {isViewport} from "../Model/Websocket/ViewportMessage";
|
||||||
@ -37,6 +38,7 @@ enum SockerIoEvent {
|
|||||||
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',
|
||||||
SET_SILENT = "set_silent", // Set or unset the silent mode for this user.
|
SET_SILENT = "set_silent", // Set or unset the silent mode for this user.
|
||||||
SET_VIEWPORT = "set-viewport",
|
SET_VIEWPORT = "set-viewport",
|
||||||
BATCH = "batch",
|
BATCH = "batch",
|
||||||
@ -223,6 +225,11 @@ export class IoSocketController {
|
|||||||
return new MessageUserPosition(user.id, player.name, player.characterLayers, player.position);
|
return new MessageUserPosition(user.id, player.name, player.characterLayers, player.position);
|
||||||
}, users);
|
}, users);
|
||||||
|
|
||||||
|
const listOfItems: {[itemId: string]: unknown} = {};
|
||||||
|
for (const [itemId, item] of world.getItemsState().entries()) {
|
||||||
|
listOfItems[itemId] = item;
|
||||||
|
}
|
||||||
|
|
||||||
//console.warn('ANSWER PLAYER POSITIONS', listOfUsers);
|
//console.warn('ANSWER PLAYER POSITIONS', listOfUsers);
|
||||||
if (answerFn === undefined && ALLOW_ARTILLERY === true) {
|
if (answerFn === undefined && ALLOW_ARTILLERY === true) {
|
||||||
/*console.error("TYPEOF answerFn", typeof(answerFn));
|
/*console.error("TYPEOF answerFn", typeof(answerFn));
|
||||||
@ -231,7 +238,11 @@ export class IoSocketController {
|
|||||||
// For some reason, answerFn can be undefined if we use Artillery (?)
|
// For some reason, answerFn can be undefined if we use Artillery (?)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
answerFn(listOfUsers);
|
|
||||||
|
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);
|
||||||
@ -368,6 +379,29 @@ export class IoSocketController {
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,8 +19,9 @@ export class MapController {
|
|||||||
// Returns a map mapping map name to file name of the map
|
// Returns a map mapping map name to file name of the map
|
||||||
getStartMap() {
|
getStartMap() {
|
||||||
this.App.get("/start-map", (req: Request, res: Response) => {
|
this.App.get("/start-map", (req: Request, res: Response) => {
|
||||||
|
const url = req.headers.host?.replace('api.', 'maps.') + URL_ROOM_STARTED;
|
||||||
res.status(OK).send({
|
res.status(OK).send({
|
||||||
mapUrlStart: req.headers.host + "/map/files" + URL_ROOM_STARTED,
|
mapUrlStart: url,
|
||||||
startInstance: "global"
|
startInstance: "global"
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
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>;
|
@ -30,6 +30,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>();
|
||||||
|
|
||||||
private readonly positionNotifier: PositionNotifier;
|
private readonly positionNotifier: PositionNotifier;
|
||||||
|
|
||||||
constructor(connectCallback: ConnectCallback,
|
constructor(connectCallback: ConnectCallback,
|
||||||
@ -273,6 +275,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;
|
||||||
|
@ -32,6 +32,14 @@
|
|||||||
"TURN_PASSWORD": "WorkAdventure123"
|
"TURN_PASSWORD": "WorkAdventure123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"maps": {
|
||||||
|
"image": "thecodingmachine/workadventure-maps:"+tag,
|
||||||
|
"host": {
|
||||||
|
"url": "maps."+url,
|
||||||
|
"https": "enable"
|
||||||
|
},
|
||||||
|
"ports": [80]
|
||||||
|
},
|
||||||
"website": {
|
"website": {
|
||||||
"image": "thecodingmachine/workadventure-website:"+tag,
|
"image": "thecodingmachine/workadventure-website:"+tag,
|
||||||
"host": {
|
"host": {
|
||||||
|
@ -19,7 +19,7 @@ services:
|
|||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
|
||||||
front:
|
front:
|
||||||
image: thecodingmachine/nodejs:12
|
image: thecodingmachine/nodejs:14
|
||||||
environment:
|
environment:
|
||||||
DEBUG_MODE: "$DEBUG_MODE"
|
DEBUG_MODE: "$DEBUG_MODE"
|
||||||
JITSI_URL: $JITSI_URL
|
JITSI_URL: $JITSI_URL
|
||||||
@ -42,6 +42,28 @@ services:
|
|||||||
- "traefik.http.routers.front-ssl.tls=true"
|
- "traefik.http.routers.front-ssl.tls=true"
|
||||||
- "traefik.http.routers.front-ssl.service=front"
|
- "traefik.http.routers.front-ssl.service=front"
|
||||||
|
|
||||||
|
maps:
|
||||||
|
image: thecodingmachine/nodejs:12-apache
|
||||||
|
environment:
|
||||||
|
DEBUG_MODE: "$DEBUG_MODE"
|
||||||
|
HOST: "0.0.0.0"
|
||||||
|
NODE_ENV: development
|
||||||
|
#APACHE_DOCUMENT_ROOT: dist/
|
||||||
|
#APACHE_EXTENSIONS: headers
|
||||||
|
#APACHE_EXTENSION_HEADERS: 1
|
||||||
|
STARTUP_COMMAND_0: sudo a2enmod headers
|
||||||
|
STARTUP_COMMAND_1: yarn install
|
||||||
|
STARTUP_COMMAND_2: yarn run dev &
|
||||||
|
volumes:
|
||||||
|
- ./maps:/var/www/html
|
||||||
|
labels:
|
||||||
|
- "traefik.http.routers.maps.rule=Host(`maps.workadventure.localhost`)"
|
||||||
|
- "traefik.http.routers.maps.entryPoints=web,traefik"
|
||||||
|
- "traefik.http.services.maps.loadbalancer.server.port=80"
|
||||||
|
- "traefik.http.routers.maps-ssl.rule=Host(`maps.workadventure.localhost`)"
|
||||||
|
- "traefik.http.routers.maps-ssl.entryPoints=websecure"
|
||||||
|
- "traefik.http.routers.maps-ssl.tls=true"
|
||||||
|
- "traefik.http.routers.maps-ssl.service=maps"
|
||||||
|
|
||||||
back:
|
back:
|
||||||
image: thecodingmachine/nodejs:12
|
image: thecodingmachine/nodejs:12
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# we are rebuilding on each deploy to cope with the API_URL environment URL
|
# we are rebuilding on each deploy to cope with the API_URL environment URL
|
||||||
FROM thecodingmachine/nodejs:12-apache
|
FROM thecodingmachine/nodejs:14-apache
|
||||||
|
|
||||||
COPY --chown=docker:docker . .
|
COPY --chown=docker:docker . .
|
||||||
RUN yarn install
|
RUN yarn install
|
||||||
|
BIN
front/dist/resources/items/computer/computer.png
vendored
Normal file
After Width: | Height: | Size: 577 B |
47
front/dist/resources/items/computer/computer_atlas.json
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "computer_off",
|
||||||
|
"frame": {
|
||||||
|
"w": 42,
|
||||||
|
"h": 40,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"anchor": {
|
||||||
|
"x": 0.5,
|
||||||
|
"y": 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "computer_on1",
|
||||||
|
"frame": {
|
||||||
|
"w": 42,
|
||||||
|
"h": 40,
|
||||||
|
"x": 0,
|
||||||
|
"y": 40
|
||||||
|
},
|
||||||
|
"anchor": {
|
||||||
|
"x": 0.5,
|
||||||
|
"y": 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "computer_on2",
|
||||||
|
"frame": {
|
||||||
|
"w": 42,
|
||||||
|
"h": 40,
|
||||||
|
"x": 42,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"anchor": {
|
||||||
|
"x": 0.5,
|
||||||
|
"y": 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"description": "Atlas generado con Atlas Packer Gamma V2",
|
||||||
|
"web": "https://gammafp.github.io/atlas-packer-phaser/"
|
||||||
|
}
|
||||||
|
}
|
BIN
front/dist/resources/items/computer/original/computer.png
vendored
Normal file
After Width: | Height: | Size: 577 B |
47
front/dist/resources/items/computer/original/computer_atlas.json
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "computer_off",
|
||||||
|
"frame": {
|
||||||
|
"w": 42,
|
||||||
|
"h": 40,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"anchor": {
|
||||||
|
"x": 0.5,
|
||||||
|
"y": 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "computer_on1",
|
||||||
|
"frame": {
|
||||||
|
"w": 42,
|
||||||
|
"h": 40,
|
||||||
|
"x": 0,
|
||||||
|
"y": 40
|
||||||
|
},
|
||||||
|
"anchor": {
|
||||||
|
"x": 0.5,
|
||||||
|
"y": 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "computer_on2",
|
||||||
|
"frame": {
|
||||||
|
"w": 42,
|
||||||
|
"h": 40,
|
||||||
|
"x": 42,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"anchor": {
|
||||||
|
"x": 0.5,
|
||||||
|
"y": 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"description": "Atlas generado con Atlas Packer Gamma V2",
|
||||||
|
"web": "https://gammafp.github.io/atlas-packer-phaser/"
|
||||||
|
}
|
||||||
|
}
|
BIN
front/dist/resources/items/computer/unpack/computer_off.png
vendored
Normal file
After Width: | Height: | Size: 379 B |
BIN
front/dist/resources/items/computer/unpack/computer_on1.png
vendored
Normal file
After Width: | Height: | Size: 492 B |
BIN
front/dist/resources/items/computer/unpack/computer_on2.png
vendored
Normal file
After Width: | Height: | Size: 452 B |
@ -22,10 +22,12 @@
|
|||||||
"@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",
|
||||||
"socket.io-client": "^2.3.0"
|
"socket.io-client": "^2.3.0",
|
||||||
|
"webpack-require-http": "^0.4.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "webpack-dev-server --open",
|
"start": "webpack-dev-server --open",
|
||||||
|
@ -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",
|
||||||
SET_SILENT = "set_silent", // Set or unset the silent mode for this user.
|
SET_SILENT = "set_silent", // Set or unset the silent mode for this user.
|
||||||
@ -114,6 +115,18 @@ export interface BatchedMessageInterface {
|
|||||||
payload: unknown
|
payload: unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
@ -182,14 +195,14 @@ export class Connection implements Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public joinARoom(roomId: string, startX: number, startY: number, direction: string, moving: boolean, viewport: ViewportInterface): Promise<MessageUserPositionInterface[]> {
|
public joinARoom(roomId: string, startX: number, startY: number, direction: string, moving: boolean, viewport: ViewportInterface): Promise<RoomJoinedMessageInterface> {
|
||||||
const promise = new Promise<MessageUserPositionInterface[]>((resolve, reject) => {
|
const promise = new Promise<RoomJoinedMessageInterface>((resolve, reject) => {
|
||||||
this.socket.emit(EventMessage.JOIN_ROOM, {
|
this.socket.emit(EventMessage.JOIN_ROOM, {
|
||||||
roomId,
|
roomId,
|
||||||
position: {x: startX, y: startY, direction, moving },
|
position: {x: startX, y: startY, direction, moving },
|
||||||
viewport,
|
viewport,
|
||||||
}, (userPositions: MessageUserPositionInterface[]) => {
|
}, (roomJoinedMessage: RoomJoinedMessageInterface) => {
|
||||||
resolve(userPositions);
|
resolve(roomJoinedMessage);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
return promise;
|
return promise;
|
||||||
@ -279,4 +292,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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,17 @@ import {
|
|||||||
MessageUserMovedInterface,
|
MessageUserMovedInterface,
|
||||||
MessageUserPositionInterface,
|
MessageUserPositionInterface,
|
||||||
PointInterface,
|
PointInterface,
|
||||||
PositionInterface
|
PositionInterface,
|
||||||
|
RoomJoinedMessageInterface
|
||||||
} from "../../Connection";
|
} from "../../Connection";
|
||||||
import {CurrentGamerInterface, hasMovedEventName, Player} from "../Player/Player";
|
import {CurrentGamerInterface, hasMovedEventName, Player} from "../Player/Player";
|
||||||
import {DEBUG_MODE, JITSI_URL, POSITION_DELAY, RESOLUTION, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable";
|
import {DEBUG_MODE, JITSI_URL, POSITION_DELAY, RESOLUTION, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable";
|
||||||
import {ITiledMap, ITiledMapLayer, ITiledMapLayerProperty, ITiledTileSet} from "../Map/ITiledMap";
|
import {
|
||||||
|
ITiledMap,
|
||||||
|
ITiledMapLayer,
|
||||||
|
ITiledMapLayerProperty, ITiledMapObject,
|
||||||
|
ITiledTileSet
|
||||||
|
} from "../Map/ITiledMap";
|
||||||
import {PLAYER_RESOURCES, PlayerResourceDescriptionInterface} from "../Entity/Character";
|
import {PLAYER_RESOURCES, PlayerResourceDescriptionInterface} from "../Entity/Character";
|
||||||
import {AddPlayerInterface} from "./AddPlayerInterface";
|
import {AddPlayerInterface} from "./AddPlayerInterface";
|
||||||
import {PlayerAnimationNames} from "../Player/Animation";
|
import {PlayerAnimationNames} from "../Player/Animation";
|
||||||
@ -20,7 +26,6 @@ import {RemotePlayer} from "../Entity/RemotePlayer";
|
|||||||
import {Queue} from 'queue-typescript';
|
import {Queue} from 'queue-typescript';
|
||||||
import {SimplePeer, UserSimplePeerInterface} from "../../WebRtc/SimplePeer";
|
import {SimplePeer, UserSimplePeerInterface} from "../../WebRtc/SimplePeer";
|
||||||
import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene";
|
import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene";
|
||||||
import {FourOFourSceneName} from "../Reconnecting/FourOFourScene";
|
|
||||||
import {loadAllLayers} from "../Entity/body_character";
|
import {loadAllLayers} from "../Entity/body_character";
|
||||||
import {CenterListener, layoutManager, LayoutMode} from "../../WebRtc/LayoutManager";
|
import {CenterListener, layoutManager, LayoutMode} from "../../WebRtc/LayoutManager";
|
||||||
import Texture = Phaser.Textures.Texture;
|
import Texture = Phaser.Textures.Texture;
|
||||||
@ -31,6 +36,10 @@ import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
|
|||||||
import {GameMap} from "./GameMap";
|
import {GameMap} from "./GameMap";
|
||||||
import {CoWebsiteManager} from "../../WebRtc/CoWebsiteManager";
|
import {CoWebsiteManager} from "../../WebRtc/CoWebsiteManager";
|
||||||
import {mediaManager} from "../../WebRtc/MediaManager";
|
import {mediaManager} from "../../WebRtc/MediaManager";
|
||||||
|
import {FourOFourSceneName} from "../Reconnecting/FourOFourScene";
|
||||||
|
import {ItemFactoryInterface} from "../Items/ItemFactoryInterface";
|
||||||
|
import {ActionableItem} from "../Items/ActionableItem";
|
||||||
|
import {UserInputManager} from "../UserInput/UserInputManager";
|
||||||
|
|
||||||
|
|
||||||
export enum Textures {
|
export enum Textures {
|
||||||
@ -92,6 +101,11 @@ export class GameScene extends Phaser.Scene implements CenterListener {
|
|||||||
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)
|
||||||
|
private createPromise: Promise<void>;
|
||||||
|
private createPromiseResolve!: (value?: void | PromiseLike<void>) => void;
|
||||||
|
|
||||||
MapKey: string;
|
MapKey: string;
|
||||||
MapUrlFile: string;
|
MapUrlFile: string;
|
||||||
@ -113,6 +127,10 @@ export class GameScene extends Phaser.Scene implements CenterListener {
|
|||||||
private chatModeSprite!: Sprite;
|
private chatModeSprite!: Sprite;
|
||||||
private onResizeCallback!: (this: Window, ev: UIEvent) => void;
|
private onResizeCallback!: (this: Window, ev: UIEvent) => void;
|
||||||
private gameMap!: GameMap;
|
private gameMap!: GameMap;
|
||||||
|
private actionableItems: Map<number, ActionableItem> = new Map<number, ActionableItem>();
|
||||||
|
// The item that can be selected by pressing the space key.
|
||||||
|
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);
|
||||||
@ -135,6 +153,13 @@ export class GameScene extends Phaser.Scene implements CenterListener {
|
|||||||
this.MapKey = MapKey;
|
this.MapKey = MapKey;
|
||||||
this.MapUrlFile = MapUrlFile;
|
this.MapUrlFile = MapUrlFile;
|
||||||
this.RoomId = this.instance + '__' + MapKey;
|
this.RoomId = this.instance + '__' + MapKey;
|
||||||
|
|
||||||
|
this.createPromise = new Promise<void>((resolve, reject): void => {
|
||||||
|
this.createPromiseResolve = resolve;
|
||||||
|
})
|
||||||
|
this.connectionAnswerPromise = new Promise<RoomJoinedMessageInterface>((resolve, reject): void => {
|
||||||
|
this.connectionAnswerPromiseResolve = resolve;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//hook preload scene
|
//hook preload scene
|
||||||
@ -229,6 +254,15 @@ export class GameScene extends Phaser.Scene implements CenterListener {
|
|||||||
window.removeEventListener('resize', this.onResizeCallback);
|
window.removeEventListener('resize', this.onResizeCallback);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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);
|
||||||
const self = this;
|
const self = this;
|
||||||
@ -254,7 +288,7 @@ export class GameScene extends Phaser.Scene implements CenterListener {
|
|||||||
|
|
||||||
// FIXME: we need to put a "unknown" instead of a "any" and validate the structure of the JSON we are receiving.
|
// FIXME: we need to put a "unknown" instead of a "any" and validate the structure of the JSON we are receiving.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
private onMapLoad(data: any): void {
|
private async onMapLoad(data: any): Promise<void> {
|
||||||
// Triggered when the map is loaded
|
// Triggered when the map is loaded
|
||||||
// Load tiles attached to the map recursively
|
// Load tiles attached to the map recursively
|
||||||
this.mapFile = data.data;
|
this.mapFile = data.data;
|
||||||
@ -267,6 +301,92 @@ export class GameScene extends Phaser.Scene implements CenterListener {
|
|||||||
//TODO strategy to add access token
|
//TODO strategy to add access token
|
||||||
this.load.image(`${url}/${tileset.image}`, `${url}/${tileset.image}`);
|
this.load.image(`${url}/${tileset.image}`, `${url}/${tileset.image}`);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Scan the object layers for objects to load and load them.
|
||||||
|
const objects = new Map<string, ITiledMapObject[]>();
|
||||||
|
|
||||||
|
for (const layer of this.mapFile.layers) {
|
||||||
|
if (layer.type === 'objectgroup') {
|
||||||
|
for (const object of layer.objects) {
|
||||||
|
let objectsOfType: ITiledMapObject[]|undefined;
|
||||||
|
if (!objects.has(object.type)) {
|
||||||
|
objectsOfType = new Array<ITiledMapObject>();
|
||||||
|
} else {
|
||||||
|
objectsOfType = objects.get(object.type);
|
||||||
|
if (objectsOfType === undefined) {
|
||||||
|
throw new Error('Unexpected object type not found');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
objectsOfType.push(object);
|
||||||
|
objects.set(object.type, objectsOfType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [itemType, objectsOfType] of objects) {
|
||||||
|
// FIXME: we would ideally need for the loader to WAIT for the import to be performed, which means writing our own loader plugin.
|
||||||
|
|
||||||
|
let itemFactory: ItemFactoryInterface;
|
||||||
|
|
||||||
|
switch (itemType) {
|
||||||
|
case 'computer': {
|
||||||
|
const module = await import('../Items/Computer/computer');
|
||||||
|
itemFactory = module.default;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported object type: "'+ itemType +'"');
|
||||||
|
}
|
||||||
|
|
||||||
|
itemFactory.preload(this.load);
|
||||||
|
this.load.start(); // Let's manually start the loader because the import might be over AFTER the loading ends.
|
||||||
|
|
||||||
|
this.load.on('complete', () => {
|
||||||
|
// 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
|
||||||
|
this.createPromise.then(async () => {
|
||||||
|
itemFactory.create(this);
|
||||||
|
|
||||||
|
const roomJoinedAnswer = await this.connectionAnswerPromise;
|
||||||
|
|
||||||
|
for (const object of objectsOfType) {
|
||||||
|
// TODO: we should pass here a factory to create sprites (maybe?)
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// import(/* webpackIgnore: true */ scriptUrl).then(result => {
|
||||||
|
//
|
||||||
|
// result.default.preload(this.load);
|
||||||
|
//
|
||||||
|
// this.load.start(); // Let's manually start the loader because the import might be over AFTER the loading ends.
|
||||||
|
// this.load.on('complete', () => {
|
||||||
|
// // 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
|
||||||
|
// this.createPromise.then(() => {
|
||||||
|
// result.default.create(this);
|
||||||
|
//
|
||||||
|
// for (let object of objectsOfType) {
|
||||||
|
// // TODO: we should pass here a factory to create sprites (maybe?)
|
||||||
|
// let objectSprite = result.default.factory(this, object);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST: let's load a module dynamically!
|
||||||
|
/*let foo = "http://maps.workadventure.localhost/computer.js";
|
||||||
|
import(/* webpackIgnore: true * / foo).then(result => {
|
||||||
|
console.log(result);
|
||||||
|
|
||||||
|
});*/
|
||||||
}
|
}
|
||||||
|
|
||||||
//hook initialisation
|
//hook initialisation
|
||||||
@ -352,13 +472,15 @@ export class GameScene extends Phaser.Scene implements CenterListener {
|
|||||||
//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) {
|
||||||
@ -392,6 +514,13 @@ export class GameScene extends Phaser.Scene implements CenterListener {
|
|||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.createPromiseResolve();
|
||||||
|
|
||||||
|
// TODO: use inputmanager instead
|
||||||
|
this.input.keyboard.on('keyup-SPACE', () => {
|
||||||
|
this.outlinedItem?.activate();
|
||||||
|
});
|
||||||
|
|
||||||
this.presentationModeSprite = this.add.sprite(2, this.game.renderer.height - 2, 'layout_modes', 0);
|
this.presentationModeSprite = this.add.sprite(2, this.game.renderer.height - 2, 'layout_modes', 0);
|
||||||
this.presentationModeSprite.setScrollFactor(0, 0);
|
this.presentationModeSprite.setScrollFactor(0, 0);
|
||||||
this.presentationModeSprite.setOrigin(0, 1);
|
this.presentationModeSprite.setOrigin(0, 1);
|
||||||
@ -627,7 +756,8 @@ export class GameScene extends Phaser.Scene implements CenterListener {
|
|||||||
this.GameManager.getPlayerName(),
|
this.GameManager.getPlayerName(),
|
||||||
this.GameManager.getCharacterSelected(),
|
this.GameManager.getCharacterSelected(),
|
||||||
PlayerAnimationNames.WalkDown,
|
PlayerAnimationNames.WalkDown,
|
||||||
false
|
false,
|
||||||
|
this.userInputManager
|
||||||
);
|
);
|
||||||
|
|
||||||
//create collision
|
//create collision
|
||||||
@ -646,12 +776,14 @@ export class GameScene extends Phaser.Scene implements CenterListener {
|
|||||||
top: camera.scrollY,
|
top: camera.scrollY,
|
||||||
right: camera.scrollX + camera.width,
|
right: camera.scrollX + camera.width,
|
||||||
bottom: camera.scrollY + camera.height,
|
bottom: camera.scrollY + camera.height,
|
||||||
}).then((userPositions: MessageUserPositionInterface[]) => {
|
}).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
|
||||||
this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this))
|
this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this))
|
||||||
|
this.CurrentPlayer.on(hasMovedEventName, this.outlineItem.bind(this))
|
||||||
this.CurrentPlayer.on(hasMovedEventName, (event: HasMovedEvent) => {
|
this.CurrentPlayer.on(hasMovedEventName, (event: HasMovedEvent) => {
|
||||||
this.gameMap.setPosition(event.x, event.y);
|
this.gameMap.setPosition(event.x, event.y);
|
||||||
})
|
})
|
||||||
@ -684,6 +816,49 @@ export class GameScene extends Phaser.Scene implements CenterListener {
|
|||||||
// Otherwise, do nothing.
|
// Otherwise, do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the correct item to outline and outline it (if there is an item to be outlined)
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
private outlineItem(event: HasMovedEvent): void {
|
||||||
|
let x = event.x;
|
||||||
|
let y = event.y;
|
||||||
|
switch (event.direction) {
|
||||||
|
case PlayerAnimationNames.WalkUp:
|
||||||
|
y -= 32;
|
||||||
|
break;
|
||||||
|
case PlayerAnimationNames.WalkDown:
|
||||||
|
y += 32;
|
||||||
|
break;
|
||||||
|
case PlayerAnimationNames.WalkLeft:
|
||||||
|
x -= 32;
|
||||||
|
break;
|
||||||
|
case PlayerAnimationNames.WalkRight:
|
||||||
|
x += 32;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('Unexpected direction "' + event.direction + '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
let shortestDistance: number = Infinity;
|
||||||
|
let selectedItem: ActionableItem|null = null;
|
||||||
|
for (const item of this.actionableItems.values()) {
|
||||||
|
const distance = item.actionableDistance(x, y);
|
||||||
|
if (distance !== null && distance < shortestDistance) {
|
||||||
|
shortestDistance = distance;
|
||||||
|
selectedItem = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.outlinedItem === selectedItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.outlinedItem?.notSelectable();
|
||||||
|
this.outlinedItem = selectedItem;
|
||||||
|
this.outlinedItem?.selectable();
|
||||||
|
}
|
||||||
|
|
||||||
private doPushPlayerPosition(event: HasMovedEvent): void {
|
private doPushPlayerPosition(event: HasMovedEvent): void {
|
||||||
this.lastMoveEventSent = event;
|
this.lastMoveEventSent = event;
|
||||||
this.lastSentTick = this.currentTick;
|
this.lastSentTick = this.currentTick;
|
||||||
@ -764,10 +939,7 @@ export class GameScene extends Phaser.Scene implements CenterListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
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);
|
||||||
|
|
||||||
@ -946,6 +1118,18 @@ export class GameScene extends Phaser.Scene implements CenterListener {
|
|||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
private onResize(): void {
|
private onResize(): void {
|
||||||
this.reposition();
|
this.reposition();
|
||||||
|
|
||||||
|
92
front/src/Phaser/Items/ActionableItem.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* An actionable item represents an in-game object that can be activated using the space-bar.
|
||||||
|
* It has coordinates and an "activation radius"
|
||||||
|
*/
|
||||||
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
|
import {OutlinePipeline} from "../Shaders/OutlinePipeline";
|
||||||
|
import {GameScene} from "../Game/GameScene";
|
||||||
|
|
||||||
|
type EventCallback = (state: unknown, parameters: unknown) => void;
|
||||||
|
|
||||||
|
export class ActionableItem {
|
||||||
|
private readonly activationRadiusSquared : number;
|
||||||
|
private isSelectable: boolean = false;
|
||||||
|
private callbacks: Map<string, Array<EventCallback>> = new Map<string, Array<EventCallback>>();
|
||||||
|
|
||||||
|
public constructor(private id: number, private sprite: Sprite, private eventHandler: GameScene, private activationRadius: number, private onActivateCallback: (item: ActionableItem) => void) {
|
||||||
|
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
|
||||||
|
* OR null if we are out of range.
|
||||||
|
*/
|
||||||
|
public actionableDistance(x: number, y: number): number|null {
|
||||||
|
const distanceSquared = (x - this.sprite.x)*(x - this.sprite.x) + (y - this.sprite.y)*(y - this.sprite.y);
|
||||||
|
if (distanceSquared < this.activationRadiusSquared) {
|
||||||
|
return distanceSquared;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the outline of the sprite.
|
||||||
|
*/
|
||||||
|
public selectable(): void {
|
||||||
|
if (this.isSelectable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isSelectable = true;
|
||||||
|
this.sprite.setPipeline(OutlinePipeline.KEY);
|
||||||
|
this.sprite.pipeline.setFloat2('uTextureSize',
|
||||||
|
this.sprite.texture.getSourceImage().width, this.sprite.texture.getSourceImage().height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the outline of the sprite
|
||||||
|
*/
|
||||||
|
public notSelectable(): void {
|
||||||
|
if (!this.isSelectable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isSelectable = false;
|
||||||
|
this.sprite.resetPipeline();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when the "space" key is pressed and the object is in range of being activated.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
86
front/src/Phaser/Items/Computer/computer.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import * as Phaser from 'phaser';
|
||||||
|
import {Scene} from "phaser";
|
||||||
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
|
import {ITiledMapObject} from "../../Map/ITiledMap";
|
||||||
|
import {ItemFactoryInterface} from "../ItemFactoryInterface";
|
||||||
|
import {GameScene} from "../../Game/GameScene";
|
||||||
|
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 {
|
||||||
|
preload: (loader: Phaser.Loader.LoaderPlugin): void => {
|
||||||
|
loader.atlas('computer', '/resources/items/computer/computer.png', '/resources/items/computer/computer_atlas.json');
|
||||||
|
},
|
||||||
|
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, initState: unknown): ActionableItem => {
|
||||||
|
if (initState !== undefined) {
|
||||||
|
if (!isComputerState(initState)) {
|
||||||
|
throw new Error('Invalid state received for computer object');
|
||||||
|
}
|
||||||
|
state = initState;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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');
|
||||||
|
}
|
||||||
|
} as ItemFactoryInterface;
|
10
front/src/Phaser/Items/ItemFactoryInterface.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
||||||
|
import {GameScene} from "../Game/GameScene";
|
||||||
|
import {ITiledMapObject} from "../Map/ITiledMap";
|
||||||
|
import {ActionableItem} from "./ActionableItem";
|
||||||
|
|
||||||
|
export interface ItemFactoryInterface {
|
||||||
|
preload: (loader: LoaderPlugin) => void;
|
||||||
|
create: (scene: GameScene) => void;
|
||||||
|
factory: (scene: GameScene, object: ITiledMapObject, state: unknown) => ActionableItem;
|
||||||
|
}
|
@ -3,6 +3,7 @@ import {GameScene, Textures} from "../Game/GameScene";
|
|||||||
import {MessageUserPositionInterface, PointInterface} from "../../Connection";
|
import {MessageUserPositionInterface, PointInterface} from "../../Connection";
|
||||||
import {ActiveEventList, UserInputEvent, UserInputManager} from "../UserInput/UserInputManager";
|
import {ActiveEventList, UserInputEvent, UserInputManager} from "../UserInput/UserInputManager";
|
||||||
import {Character} from "../Entity/Character";
|
import {Character} from "../Entity/Character";
|
||||||
|
import {OutlinePipeline} from "../Shaders/OutlinePipeline";
|
||||||
|
|
||||||
|
|
||||||
export const hasMovedEventName = "hasMoved";
|
export const hasMovedEventName = "hasMoved";
|
||||||
@ -12,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 = PlayerAnimationNames.WalkDown;
|
||||||
previousDirection: string = PlayerAnimationNames.WalkDown;
|
private wasMoving: boolean = false;
|
||||||
wasMoving: boolean = false;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
Scene: GameScene,
|
Scene: GameScene,
|
||||||
@ -23,13 +23,11 @@ export class Player extends Character implements CurrentGamerInterface {
|
|||||||
name: string,
|
name: string,
|
||||||
PlayerTextures: string[],
|
PlayerTextures: string[],
|
||||||
direction: string,
|
direction: string,
|
||||||
moving: boolean
|
moving: boolean,
|
||||||
|
private userInputManager: UserInputManager
|
||||||
) {
|
) {
|
||||||
super(Scene, x, y, PlayerTextures, name, direction, moving, 1);
|
super(Scene, x, y, PlayerTextures, 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.getBody().setImmovable(false);
|
this.getBody().setImmovable(false);
|
||||||
}
|
}
|
||||||
|
61
front/src/Phaser/Shaders/OutlinePipeline.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
export class OutlinePipeline extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline
|
||||||
|
{
|
||||||
|
|
||||||
|
// the unique id of this pipeline
|
||||||
|
public static readonly KEY = 'Outline';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Phaser.Game} game - the controller of the game instance
|
||||||
|
*/
|
||||||
|
constructor(game: Phaser.Game)
|
||||||
|
{
|
||||||
|
super({
|
||||||
|
game: game,
|
||||||
|
renderer: game.renderer,
|
||||||
|
fragShader: `
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
uniform sampler2D uMainSampler;
|
||||||
|
uniform vec2 uTextureSize;
|
||||||
|
|
||||||
|
varying vec2 outTexCoord;
|
||||||
|
varying float outTintEffect;
|
||||||
|
varying vec4 outTint;
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
vec4 texture = texture2D(uMainSampler, outTexCoord);
|
||||||
|
vec4 texel = vec4(outTint.rgb * outTint.a, outTint.a);
|
||||||
|
vec4 color = texture;
|
||||||
|
|
||||||
|
if (outTintEffect == 0.0)
|
||||||
|
{
|
||||||
|
color = texture * texel;
|
||||||
|
}
|
||||||
|
else if (outTintEffect == 1.0)
|
||||||
|
{
|
||||||
|
color.rgb = mix(texture.rgb, outTint.rgb * outTint.a, texture.a);
|
||||||
|
color.a = texture.a * texel.a;
|
||||||
|
}
|
||||||
|
else if (outTintEffect == 2.0)
|
||||||
|
{
|
||||||
|
color = texel;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 onePixel = vec2(1.0, 1.0) / uTextureSize;
|
||||||
|
float upAlpha = texture2D(uMainSampler, outTexCoord + vec2(0.0, onePixel.y)).a;
|
||||||
|
float leftAlpha = texture2D(uMainSampler, outTexCoord + vec2(-onePixel.x, 0.0)).a;
|
||||||
|
float downAlpha = texture2D(uMainSampler, outTexCoord + vec2(0.0, -onePixel.y)).a;
|
||||||
|
float rightAlpha = texture2D(uMainSampler, outTexCoord + vec2(onePixel.x, 0.0)).a;
|
||||||
|
|
||||||
|
if (texture.a == 0.0 && max(max(upAlpha, downAlpha), max(leftAlpha, rightAlpha)) == 1.0)
|
||||||
|
{
|
||||||
|
color = vec4(1.0, 1.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = color;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -47,6 +47,7 @@ export class UserInputManager {
|
|||||||
{event: UserInputEvent.SpeedUp, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT) },
|
{event: UserInputEvent.SpeedUp, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT) },
|
||||||
|
|
||||||
{event: UserInputEvent.Interact, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E) },
|
{event: UserInputEvent.Interact, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E) },
|
||||||
|
{event: UserInputEvent.Interact, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE) },
|
||||||
{event: UserInputEvent.Shout, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F) },
|
{event: UserInputEvent.Shout, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F) },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene";
|
|||||||
import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene";
|
import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene";
|
||||||
import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene";
|
import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene";
|
||||||
import {FourOFourScene} from "./Phaser/Reconnecting/FourOFourScene";
|
import {FourOFourScene} from "./Phaser/Reconnecting/FourOFourScene";
|
||||||
|
import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer;
|
||||||
|
import {OutlinePipeline} from "./Phaser/Shaders/OutlinePipeline";
|
||||||
import {CustomizeScene} from "./Phaser/Login/CustomizeScene";
|
import {CustomizeScene} from "./Phaser/Login/CustomizeScene";
|
||||||
import {HtmlUtils} from "./WebRtc/HtmlUtils";
|
import {HtmlUtils} from "./WebRtc/HtmlUtils";
|
||||||
import {CoWebsiteManager} from "./WebRtc/CoWebsiteManager";
|
import {CoWebsiteManager} from "./WebRtc/CoWebsiteManager";
|
||||||
@ -34,6 +36,13 @@ const config: GameConfig = {
|
|||||||
arcade: {
|
arcade: {
|
||||||
debug: DEBUG_MODE
|
debug: DEBUG_MODE
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
callbacks: {
|
||||||
|
postBoot: game => {
|
||||||
|
// FIXME: we should fore WebGL in the config.
|
||||||
|
const renderer = game.renderer as WebGLRenderer;
|
||||||
|
renderer.addPipeline(OutlinePipeline.KEY, new OutlinePipeline(game));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"module": "CommonJS",
|
"module": "CommonJS",
|
||||||
"target": "es6",
|
"target": "ES2015",
|
||||||
"downlevelIteration": true,
|
"downlevelIteration": true,
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
|
@ -33,6 +33,9 @@ module.exports = {
|
|||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'dist'),
|
||||||
publicPath: '/'
|
publicPath: '/'
|
||||||
},
|
},
|
||||||
|
externals:[
|
||||||
|
require('webpack-require-http')
|
||||||
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
new HtmlWebpackPlugin(
|
new HtmlWebpackPlugin(
|
||||||
{
|
{
|
||||||
|
@ -912,6 +912,11 @@ chardet@^0.7.0:
|
|||||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||||
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
||||||
|
|
||||||
|
charenc@~0.0.1:
|
||||||
|
version "0.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
|
||||||
|
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
|
||||||
|
|
||||||
chokidar@^2.1.8:
|
chokidar@^2.1.8:
|
||||||
version "2.1.8"
|
version "2.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
|
||||||
@ -1207,6 +1212,11 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5:
|
|||||||
shebang-command "^1.2.0"
|
shebang-command "^1.2.0"
|
||||||
which "^1.2.9"
|
which "^1.2.9"
|
||||||
|
|
||||||
|
crypt@~0.0.1:
|
||||||
|
version "0.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
|
||||||
|
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
|
||||||
|
|
||||||
crypto-browserify@^3.11.0:
|
crypto-browserify@^3.11.0:
|
||||||
version "3.12.0"
|
version "3.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
|
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
|
||||||
@ -2114,6 +2124,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"
|
||||||
@ -2632,7 +2647,7 @@ is-binary-path@~2.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
binary-extensions "^2.0.0"
|
binary-extensions "^2.0.0"
|
||||||
|
|
||||||
is-buffer@^1.1.5:
|
is-buffer@^1.1.5, is-buffer@~1.1.1:
|
||||||
version "1.1.6"
|
version "1.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||||
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
||||||
@ -3005,6 +3020,15 @@ md5.js@^1.3.4:
|
|||||||
inherits "^2.0.1"
|
inherits "^2.0.1"
|
||||||
safe-buffer "^5.1.2"
|
safe-buffer "^5.1.2"
|
||||||
|
|
||||||
|
md5@^2.0.0:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
|
||||||
|
integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=
|
||||||
|
dependencies:
|
||||||
|
charenc "~0.0.1"
|
||||||
|
crypt "~0.0.1"
|
||||||
|
is-buffer "~1.1.1"
|
||||||
|
|
||||||
media-typer@0.3.0:
|
media-typer@0.3.0:
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||||
@ -4982,6 +5006,14 @@ webpack-merge@^4.2.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
lodash "^4.17.15"
|
lodash "^4.17.15"
|
||||||
|
|
||||||
|
webpack-require-http@^0.4.3:
|
||||||
|
version "0.4.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/webpack-require-http/-/webpack-require-http-0.4.3.tgz#5690d8cc57246a53a81f1ccffd20d0394d70261c"
|
||||||
|
integrity sha1-VpDYzFckalOoHxzP/SDQOU1wJhw=
|
||||||
|
dependencies:
|
||||||
|
md5 "^2.0.0"
|
||||||
|
url "^0.11.0"
|
||||||
|
|
||||||
webpack-sources@^1.4.0, webpack-sources@^1.4.1:
|
webpack-sources@^1.4.0, webpack-sources@^1.4.1:
|
||||||
version "1.4.3"
|
version "1.4.3"
|
||||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
|
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
|
||||||
|
1
maps/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/node_modules/
|
1
maps/.htaccess
Normal file
@ -0,0 +1 @@
|
|||||||
|
Header set Access-Control-Allow-Origin "*"
|
10
maps/Dockerfile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# we are rebuilding on each deploy to cope with the API_URL environment URL
|
||||||
|
FROM thecodingmachine/nodejs:12-apache
|
||||||
|
|
||||||
|
COPY --chown=docker:docker . .
|
||||||
|
#RUN yarn install
|
||||||
|
|
||||||
|
#ENV NODE_ENV=production
|
||||||
|
#ENV STARTUP_COMMAND_1="yarn run build"
|
||||||
|
#ENV APACHE_DOCUMENT_ROOT=dist/
|
||||||
|
RUN sudo a2enmod headers
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 671 KiB After Width: | Height: | Size: 671 KiB |
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
56
maps/objects/computer.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import * as Phaser from 'phaser';
|
||||||
|
import {Scene} from "phaser";
|
||||||
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
|
|
||||||
|
interface ITiledMapObject {
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tile object id
|
||||||
|
*/
|
||||||
|
gid: number;
|
||||||
|
height: number;
|
||||||
|
name: string;
|
||||||
|
properties: {[key: string]: string};
|
||||||
|
rotation: number;
|
||||||
|
type: string;
|
||||||
|
visible: boolean;
|
||||||
|
width: number;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not object is an ellipse
|
||||||
|
*/
|
||||||
|
ellipse: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Polygon points
|
||||||
|
*/
|
||||||
|
polygon: {x: number, y: number}[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Polyline points
|
||||||
|
*/
|
||||||
|
polyline: {x: number, y: number}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
class MySprite extends Sprite {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
preload: (loader: Phaser.Loader.LoaderPlugin) => {
|
||||||
|
loader.atlas('computer', '/resources/items/computer/computer.png', '/resources/items/computer/computer_atlas.json');
|
||||||
|
},
|
||||||
|
create: (scene: Scene) => {
|
||||||
|
|
||||||
|
},
|
||||||
|
factory: (scene: Scene, object: ITiledMapObject) => {
|
||||||
|
// Idée: ESSAYER WebPack? https://paultavares.wordpress.com/2018/07/02/webpack-how-to-generate-an-es-module-bundle/
|
||||||
|
let foo = new MySprite(scene, object.x, object.y, 'computer');
|
||||||
|
scene.add.existing(foo);
|
||||||
|
//scene.add.sprite(object.x, object.y, 'computer');
|
||||||
|
}
|
||||||
|
};
|
49
maps/package.json
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "workadventuremaps",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"tsc": "tsc",
|
||||||
|
"dev": "tsc -w",
|
||||||
|
"prod": "tsc",
|
||||||
|
"test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json",
|
||||||
|
"lint": "node_modules/.bin/eslint src/ . --ext .ts",
|
||||||
|
"fix": "node_modules/.bin/eslint --fix src/ . --ext .ts"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/thecodingmachine/workadventure.git"
|
||||||
|
},
|
||||||
|
"contributors": [
|
||||||
|
{
|
||||||
|
"name": "Grégoire Parant",
|
||||||
|
"email": "g.parant@thecodingmachine.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "David Négrier",
|
||||||
|
"email": "d.negrier@thecodingmachine.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Arthmaël Poly",
|
||||||
|
"email": "a.poly@thecodingmachine.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "SEE LICENSE IN LICENSE.txt",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/thecodingmachine/workadventure/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/thecodingmachine/workadventure#readme",
|
||||||
|
"dependencies": {
|
||||||
|
"phaser": "^3.24.1",
|
||||||
|
"ts-node-dev": "^1.0.0-pre.44",
|
||||||
|
"typescript": "^3.8.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jasmine": "^3.5.10",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^2.26.0",
|
||||||
|
"@typescript-eslint/parser": "^2.26.0",
|
||||||
|
"eslint": "^6.8.0",
|
||||||
|
"jasmine": "^3.5.0"
|
||||||
|
}
|
||||||
|
}
|
24
maps/tsconfig.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./dist/",
|
||||||
|
"sourceMap": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"module": "ESNext",
|
||||||
|
"target": "ES2015",
|
||||||
|
"downlevelIteration": true,
|
||||||
|
"jsx": "react",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
"strict": false, /* Enable all strict type-checking options. */
|
||||||
|
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||||
|
"strictNullChecks": true, /* Enable strict null checks. */
|
||||||
|
"strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||||
|
"strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||||
|
"strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */
|
||||||
|
"noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||||
|
"alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||||
|
|
||||||
|
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||||
|
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */
|
||||||
|
}
|
||||||
|
}
|