Merge branch 'master' into feature/back-players-proximity
# Conflicts: # back/src/Model/Websocket/MessageUserPosition.ts
This commit is contained in:
commit
9730df2295
1
.env.template
Normal file
1
.env.template
Normal file
@ -0,0 +1 @@
|
||||
DEBUG_MODE=false
|
102
.github/workflows/build-and-deploy.yml
vendored
Normal file
102
.github/workflows/build-and-deploy.yml
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
name: Build, push and deploy Docker image
|
||||
|
||||
on:
|
||||
- push
|
||||
|
||||
# Enables BuildKit
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1
|
||||
|
||||
jobs:
|
||||
|
||||
build-front:
|
||||
|
||||
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: front/Dockerfile
|
||||
path: front/
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
repository: thecodingmachine/workadventure-front
|
||||
tags: ${{ env.GITHUB_REF_SLUG }}
|
||||
add_git_labels: true
|
||||
|
||||
build-back:
|
||||
|
||||
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 back image"
|
||||
uses: docker/build-push-action@v1
|
||||
with:
|
||||
dockerfile: back/Dockerfile
|
||||
path: back/
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
repository: thecodingmachine/workadventure-back
|
||||
tags: ${{ env.GITHUB_REF_SLUG }}
|
||||
add_git_labels: true
|
||||
|
||||
deeploy:
|
||||
needs:
|
||||
- build-front
|
||||
- build-back
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Create a slugified value of the branch
|
||||
- uses: rlespinasse/github-slug-action@1.1.0
|
||||
|
||||
- name: Deploy
|
||||
uses: thecodingmachine/deeployer@master
|
||||
env:
|
||||
KUBE_CONFIG_FILE: ${{ secrets.KUBE_CONFIG_FILE }}
|
||||
with:
|
||||
namespace: workadventure-${{ env.GITHUB_REF_SLUG }}
|
||||
|
||||
- name: Add a comment in PR
|
||||
uses: unsplash/comment-on-pr@v1.2.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
msg: Environment deployed at https://${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com
|
||||
check_for_duplicate_msg: true
|
||||
|
||||
- name: Run Cypress tests
|
||||
uses: cypress-io/github-action@v1
|
||||
env:
|
||||
CYPRESS_BASE_URL: https://${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com
|
||||
with:
|
||||
env: host=${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com,port=80
|
||||
spec: cypress/integration/spec.js
|
||||
wait-on: https://${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com
|
||||
working-directory: e2e
|
||||
|
||||
- name: "Upload the screenshot on test failure"
|
||||
uses: actions/upload-artifact@v1
|
||||
if: failure()
|
||||
with:
|
||||
name: "screenshot"
|
||||
path: "./e2e/cypress/screenshots/spec.js/WorkAdventureGame -- loads (failed).png"
|
1
.github/workflows/continuous_integration.yml
vendored
1
.github/workflows/continuous_integration.yml
vendored
@ -65,3 +65,4 @@ jobs:
|
||||
- name: "Jasmine"
|
||||
run: yarn test
|
||||
working-directory: "back"
|
||||
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
.env
|
||||
.idea
|
||||
.vagrant
|
||||
Vagrantfile
|
13
README.md
13
README.md
@ -32,6 +32,19 @@ Note: on some OSes, you will need to add this line to your `/etc/hosts` file:
|
||||
workadventure.localhost 127.0.0.1
|
||||
```
|
||||
|
||||
## Designing a map
|
||||
|
||||
If you want to design your own map, you can use [Tiled](https://www.mapeditor.org/).
|
||||
|
||||
A few things to notice:
|
||||
|
||||
- your map can have as many layers as your want
|
||||
- your map MUST contain a layer named "floorLayer" of type "objectgroup" that represents the layer on which characters will be drawn.
|
||||
- the tilesets in your map MUST be embedded. You can refer to an external typeset in a TSX file. Click the "embed tileset" button in the tileset tab to embed tileset data.
|
||||
- your map MUST be exported in JSON format. You need to use a recent version of Tiled to get JSON format export (1.3+)
|
||||
|
||||
![](doc/images/tiled_screenshot_1.png)
|
||||
|
||||
### MacOS developers, your environment with Vagrant
|
||||
|
||||
If you are using MacOS, you can increase Docker performance using Vagrant. If you want more explanations, you can read [this medium article](https://medium.com/better-programming/vagrant-to-increase-docker-performance-with-macos-25b354b0c65c).
|
||||
|
5
back/.dockerignore
Normal file
5
back/.dockerignore
Normal file
@ -0,0 +1,5 @@
|
||||
/dist/
|
||||
/node_modules/
|
||||
/dist/bundle.js
|
||||
/yarn-error.log
|
||||
/Dockerfile
|
9
back/Dockerfile
Normal file
9
back/Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
FROM thecodingmachine/nodejs:12
|
||||
|
||||
COPY --chown=docker:docker . .
|
||||
RUN yarn install
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
CMD ["yarn", "run", "prod"]
|
||||
|
@ -25,13 +25,15 @@
|
||||
"@types/http-status-codes": "^1.2.0",
|
||||
"@types/jsonwebtoken": "^8.3.8",
|
||||
"@types/socket.io": "^2.1.4",
|
||||
"@types/uuidv4": "^5.0.0",
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.1",
|
||||
"http-status-codes": "^1.4.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"socket.io": "^2.3.0",
|
||||
"ts-node-dev": "^1.0.0-pre.44",
|
||||
"typescript": "^3.8.3"
|
||||
"typescript": "^3.8.3",
|
||||
"uuidv4": "^6.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jasmine": "^3.5.10",
|
||||
|
@ -2,6 +2,7 @@ import {Application, Request, Response} from "express";
|
||||
import Jwt from "jsonwebtoken";
|
||||
import {BAD_REQUEST, OK} from "http-status-codes";
|
||||
import {SECRET_KEY, ROOM} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..."
|
||||
import { uuid } from 'uuidv4';
|
||||
|
||||
export class AuthenticateController{
|
||||
App : Application;
|
||||
@ -21,8 +22,13 @@ export class AuthenticateController{
|
||||
});
|
||||
}
|
||||
//TODO check user email for The Coding Machine game
|
||||
let token = Jwt.sign({email: param.email, roomId: ROOM}, SECRET_KEY, {expiresIn: '24h'});
|
||||
return res.status(OK).send({token: token, roomId: ROOM});
|
||||
let userId = uuid();
|
||||
let token = Jwt.sign({email: param.email, roomId: ROOM, userId: userId}, SECRET_KEY, {expiresIn: '24h'});
|
||||
return res.status(OK).send({
|
||||
token: token,
|
||||
roomId: ROOM,
|
||||
userId: userId
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -104,7 +104,8 @@ export class IoSocketController{
|
||||
roomId: <string>,
|
||||
position: {
|
||||
x : <number>,
|
||||
y : <number>
|
||||
y : <number>,
|
||||
direction: <string>
|
||||
}
|
||||
},
|
||||
...
|
||||
@ -125,8 +126,7 @@ export class IoSocketController{
|
||||
}
|
||||
arrayMap.forEach((value : any) => {
|
||||
let roomId = value[0];
|
||||
let data = value[1];
|
||||
this.Io.in(roomId).emit('user-position', JSON.stringify(data));
|
||||
this.Io.in(roomId).emit('user-position', JSON.stringify(arrayMap));
|
||||
});
|
||||
this.seTimeOutInProgress = setTimeout(() => {
|
||||
this.shareUsersPosition();
|
||||
|
@ -4,19 +4,22 @@ import {PointInterface} from "./PointInterface";
|
||||
export class Point implements PointInterface{
|
||||
x: number;
|
||||
y: number;
|
||||
direction: string;
|
||||
|
||||
constructor(x : number, y : number) {
|
||||
constructor(x : number, y : number, direction : string = "none") {
|
||||
if(x === null || y === null){
|
||||
throw Error("position x and y cannot be null");
|
||||
}
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
toJson(){
|
||||
return {
|
||||
x : this.x,
|
||||
y: this.y
|
||||
y: this.y,
|
||||
direction: this.direction
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -24,9 +27,10 @@ export class Point implements PointInterface{
|
||||
export class MessageUserPosition extends Message{
|
||||
position: PointInterface;
|
||||
|
||||
constructor(data: any) {
|
||||
super(data);
|
||||
this.position = new Point(data.position.x, data.position.y);
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
let data = JSON.parse(message);
|
||||
this.position = new Point(data.position.x, data.position.y, data.position.direction);
|
||||
}
|
||||
|
||||
toString() {
|
||||
|
@ -1,5 +1,6 @@
|
||||
export interface PointInterface {
|
||||
x: number;
|
||||
y: number;
|
||||
direction: string;
|
||||
toJson() : object;
|
||||
}
|
@ -135,6 +135,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1"
|
||||
integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==
|
||||
|
||||
"@types/uuidv4@^5.0.0":
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/uuidv4/-/uuidv4-5.0.0.tgz#2c94e67b0c06d5adb28fb7ced1a1b5f0866ecd50"
|
||||
integrity sha512-xUrhYSJnkTq9CP79cU3svoKTLPCIbMMnu9Twf/tMpHATYSHCAAeDNeb2a/29YORhk5p4atHhCTMsIBU/tvdh6A==
|
||||
dependencies:
|
||||
uuidv4 "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^2.26.0":
|
||||
version "2.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.26.0.tgz#04c96560c8981421e5a9caad8394192363cc423f"
|
||||
@ -2094,6 +2101,18 @@ utils-merge@1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
||||
|
||||
uuid@7.0.3:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
|
||||
integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==
|
||||
|
||||
uuidv4@*, uuidv4@^6.0.7:
|
||||
version "6.0.7"
|
||||
resolved "https://registry.yarnpkg.com/uuidv4/-/uuidv4-6.0.7.tgz#15e920848e1afbbd97b4919bc50f4f2f2278f880"
|
||||
integrity sha512-4mpYRFNqO22EckzxPSJ/+xjn9GgO6SAqEJ33yt23Y+HZZoZOt/6l4U4iIjc86ZfxSN2fSCGGmHNb3kiACFNd1g==
|
||||
dependencies:
|
||||
uuid "7.0.3"
|
||||
|
||||
v8-compile-cache@^2.0.3:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
|
||||
|
35
deeployer.libsonnet
Normal file
35
deeployer.libsonnet
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
local env = std.extVar("env"),
|
||||
local namespace = env.GITHUB_REF_SLUG,
|
||||
local tag = if namespace == "master" then "latest" else namespace,
|
||||
"$schema": "https://raw.githubusercontent.com/thecodingmachine/deeployer/master/deeployer.schema.json",
|
||||
"containers": {
|
||||
"back": {
|
||||
"image": "thecodingmachine/workadventure-back:"+tag,
|
||||
"host": {
|
||||
"url": "api."+namespace+".workadventure.test.thecodingmachine.com",
|
||||
"https": "enable"
|
||||
},
|
||||
"ports": [8080],
|
||||
"env": {
|
||||
"SECRET_KEY": "tempSecretKeyNeedsToChange"
|
||||
}
|
||||
},
|
||||
"front": {
|
||||
"image": "thecodingmachine/workadventure-front:"+tag,
|
||||
"host": {
|
||||
"url": namespace+".workadventure.test.thecodingmachine.com",
|
||||
"https": "enable"
|
||||
},
|
||||
"ports": [80],
|
||||
"env": {
|
||||
"API_URL": "https://api."+namespace+".workadventure.test.thecodingmachine.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"https": {
|
||||
"mail": "d.negrier@thecodingmachine.com"
|
||||
}
|
||||
}
|
||||
}
|
BIN
doc/images/tiled_screenshot_1.png
Normal file
BIN
doc/images/tiled_screenshot_1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
20
docker-compose.ci.yml
Normal file
20
docker-compose.ci.yml
Normal file
@ -0,0 +1,20 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
wait_app:
|
||||
image: dadarek/wait-for-dependencies
|
||||
depends_on:
|
||||
- reverse-proxy
|
||||
command: front:8080
|
||||
cypress:
|
||||
# the Docker image to use from https://github.com/cypress-io/cypress-docker-images
|
||||
image: "cypress/included:3.8.3"
|
||||
depends_on:
|
||||
- reverse-proxy
|
||||
environment:
|
||||
# pass base url to test pointing at the web application
|
||||
- CYPRESS_baseUrl=http://front:8080
|
||||
working_dir: /e2e
|
||||
volumes:
|
||||
- ./e2e/:/e2e
|
@ -7,12 +7,16 @@ services:
|
||||
- "80:80"
|
||||
# The Web UI (enabled by --api.insecure=true)
|
||||
- "8080:8080"
|
||||
depends_on:
|
||||
- back
|
||||
- front
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
front:
|
||||
image: thecodingmachine/nodejs:12
|
||||
environment:
|
||||
DEBUG_MODE: "$DEBUG_MODE"
|
||||
HOST: "0.0.0.0"
|
||||
NODE_ENV: development
|
||||
API_URL: http://api.workadventure.localhost
|
||||
|
3
e2e/.gitignore
vendored
Normal file
3
e2e/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
screenshots/
|
||||
videos/
|
||||
node_modules/
|
36
e2e/CYPRESS.md
Normal file
36
e2e/CYPRESS.md
Normal file
@ -0,0 +1,36 @@
|
||||
# Testing with cypress
|
||||
|
||||
This project use [cypress](https://www.cypress.io/) to do functional testing of the website.
|
||||
Unfortunately we cannot integrate it with docker-compose for the moment, so you will need to install some packages locally on your pc.
|
||||
|
||||
## Getting Started
|
||||
|
||||
You will need to install theses dependancies on linux (don't know about mac):
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install libgtk2.0-0 libgtk-3-0 libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
|
||||
```
|
||||
|
||||
Cypress can be installed locally in the e2e directory
|
||||
```bash
|
||||
cd e2e
|
||||
npm install
|
||||
```
|
||||
|
||||
|
||||
How to use:
|
||||
```bash
|
||||
npm run cy:run
|
||||
npm run cy:open
|
||||
```
|
||||
|
||||
The first command will run all tests in the terminal, while the second will open the interactive task runner which allow you to easily manage the test workflow
|
||||
|
||||
[More details here](https://docs.cypress.io/guides/getting-started/testing-your-app.html#Step-1-Start-your-server)
|
||||
|
||||
## How to test a game
|
||||
|
||||
Cypress cannot "see" and so cannot directly manipulate the canva created by Phaser.
|
||||
|
||||
This means we have to do workarounds such as exposing core objects in the window so that cypress can manipulate them or doing console that cypress can catch.
|
7
e2e/cypress.json
Normal file
7
e2e/cypress.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"baseUrl": "http://workadventure.localhost",
|
||||
"video": false,
|
||||
"defaultCommandTimeout": 20000,
|
||||
"pluginsFile": false,
|
||||
"supportFile": false
|
||||
}
|
25
e2e/cypress/integration/spec.js
Normal file
25
e2e/cypress/integration/spec.js
Normal file
@ -0,0 +1,25 @@
|
||||
Cypress.on('window:before:load', (win) => {
|
||||
// because this is called before any scripts
|
||||
// have loaded - the ga function is undefined
|
||||
// so we need to create it.
|
||||
win.cypressAsserter = cy.stub().as('ca')
|
||||
})
|
||||
|
||||
describe('WorkAdventureGame', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/', {
|
||||
onBeforeLoad (win) {
|
||||
cy.spy(win.console, 'log').as('console.log')
|
||||
},
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
it('loads', () => {
|
||||
cy.get('@console.log').should('be.calledWith', 'Started the game')
|
||||
cy.get('@console.log').should('be.calledWith', 'Preloading')
|
||||
cy.get('@console.log').should('be.calledWith', 'Preloading done')
|
||||
cy.get('@console.log').should('be.calledWith', 'startInit')
|
||||
cy.get('@console.log').should('be.calledWith', 'startInit done')
|
||||
});
|
||||
});
|
1406
e2e/package-lock.json
generated
Normal file
1406
e2e/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
9
e2e/package.json
Normal file
9
e2e/package.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"cypress": "^3.8.3"
|
||||
},
|
||||
"scripts": {
|
||||
"cy:run": "cypress run",
|
||||
"cy:open": "cypress open"
|
||||
}
|
||||
}
|
4
front/.dockerignore
Normal file
4
front/.dockerignore
Normal file
@ -0,0 +1,4 @@
|
||||
/node_modules/
|
||||
/dist/bundle.js
|
||||
/yarn-error.log
|
||||
/Dockerfile
|
9
front/Dockerfile
Normal file
9
front/Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
# 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/
|
BIN
front/dist/maps/floortileset.png
vendored
Normal file
BIN
front/dist/maps/floortileset.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 85 KiB |
62
front/dist/maps/map.json
vendored
62
front/dist/maps/map.json
vendored
@ -3,33 +3,45 @@
|
||||
{
|
||||
"export":
|
||||
{
|
||||
"target":"."
|
||||
"format":"json",
|
||||
"target":"map.json"
|
||||
}
|
||||
},
|
||||
"height":20,
|
||||
"height":18,
|
||||
"infinite":false,
|
||||
"layers":[
|
||||
{
|
||||
"data":[1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 1, 2, 2, 2, 2, 2, 2, 3, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 17, 18, 18, 18, 18, 18, 18, 19, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 17, 18, 18, 18, 18, 18, 18, 19, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 17, 18, 18, 18, 18, 18, 18, 19, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 33, 34, 34, 34, 34, 34, 34, 35, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178],
|
||||
"height":20,
|
||||
"id":1,
|
||||
"name":"Calque 1",
|
||||
"data":[294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 294, 294, 294, 294, 294, 294, 294, 294, 294],
|
||||
"height":18,
|
||||
"id":2,
|
||||
"name":"bottom",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":20,
|
||||
"width":46,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 62, 62, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 94, 94, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 110, 110, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 193, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 195, 164, 209, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 211],
|
||||
"height":20,
|
||||
"draworder":"topdown",
|
||||
"id":3,
|
||||
"name":"Calque 2",
|
||||
"name":"floorLayer",
|
||||
"objects":[],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"data":[0, 0, 115, 51, 52, 116, 115, 51, 52, 116, 115, 51, 52, 116, 0, 0, 57, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 245, 246, 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 68, 0, 0, 67, 68, 0, 0, 67, 68, 0, 0, 0, 73, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 245, 246, 247, 0, 0, 153, 155, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 155, 0, 0, 0, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 155, 0, 0, 0, 199, 0, 0, 0, 0, 0, 0, 0, 0, 115, 67, 68, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 0, 0, 29, 30, 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 169, 171, 0, 0, 0, 215, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 29, 30, 0, 0, 0, 0, 0, 0, 0, 29, 30, 29, 30, 0, 0, 0, 0, 185, 187, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 52, 116, 0, 0, 51, 52, 116, 0, 0, 0, 0, 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 67, 68, 0, 0, 115, 67, 68, 0, 0, 0, 0, 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 52, 116, 0, 0, 51, 52, 116, 0, 0, 0, 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 0, 0, 0, 0, 0, 98, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 67, 68, 0, 0, 115, 67, 68, 0, 0, 0, 0, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 52, 116, 0, 0, 51, 52, 116, 0, 0, 0, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 181, 0, 0, 0, 0, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 67, 68, 0, 0, 115, 67, 68, 0, 0, 0, 0, 45, 46, 45, 46, 45, 46, 45, 46, 45, 46, 45, 46, 45, 46, 45, 46, 197, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 113, 0, 113, 0, 113, 0, 0, 51, 52, 0, 0, 0, 113, 0, 113, 0, 113, 0, 0, 51, 49, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 50, 0, 0, 0, 0, 0, 51, 49, 50, 49, 50, 49, 50, 0, 115, 67, 68, 116, 0, 49, 50, 49, 50, 49, 50, 0, 115, 67, 65, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 52, 0, 0, 0, 0, 115, 67, 65, 66, 65, 66, 65, 66, 0, 0, 51, 52, 0, 0, 65, 66, 65, 66, 65, 66, 0, 0, 0, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 67, 68, 116, 0, 0, 0, 0, 0, 114, 0, 114, 0, 114, 0, 0, 115, 67, 68, 116, 0, 114, 0, 114, 0, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"height":18,
|
||||
"id":1,
|
||||
"name":"top",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":20,
|
||||
"width":46,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
@ -41,20 +53,34 @@
|
||||
"tileheight":32,
|
||||
"tilesets":[
|
||||
{
|
||||
"columns":16,
|
||||
"firstgid":1,
|
||||
"image":"tiles.png",
|
||||
"imageheight":512,
|
||||
"imagewidth":512,
|
||||
"source":"office_1.tsx"
|
||||
},
|
||||
{
|
||||
"columns":8,
|
||||
"firstgid":257,
|
||||
"image":"floortileset.png",
|
||||
"imageheight":256,
|
||||
"imagewidth":256,
|
||||
"margin":0,
|
||||
"name":"tiles",
|
||||
"name":"floortileset",
|
||||
"spacing":0,
|
||||
"tilecount":256,
|
||||
"tilecount":64,
|
||||
"tileheight":32,
|
||||
"tiles":[
|
||||
{
|
||||
"id":37,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
}],
|
||||
"tilewidth":32
|
||||
}],
|
||||
"tilewidth":32,
|
||||
"type":"map",
|
||||
"version":1.2,
|
||||
"width":20
|
||||
"width":46
|
||||
}
|
19
front/dist/maps/map2.json
vendored
19
front/dist/maps/map2.json
vendored
File diff suppressed because one or more lines are too long
254
front/dist/maps/office_1.tsx
vendored
Normal file
254
front/dist/maps/office_1.tsx
vendored
Normal file
@ -0,0 +1,254 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.2" tiledversion="1.3.3" name="office_1" tilewidth="32" tileheight="32" tilecount="256" columns="16">
|
||||
<image source="tilesets_deviant_milkian_1.png" width="512" height="512"/>
|
||||
<tile id="7">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="12">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="13">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="14">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="15">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="23">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="28">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="29">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="30">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="31">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="39">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="44">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="45">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="48">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="49">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="50">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="51">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="52">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="56">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="57">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="64">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="65">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="66">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="67">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="68">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="72">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="73">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="84">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="152">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="153">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="154">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="155">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="156">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="157">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="161">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="162">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="168">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="169">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="170">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="171">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="172">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="173">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="177">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="178">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="184">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="185">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="186">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="196">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="198">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="214">
|
||||
<properties>
|
||||
<property name="collides" type="bool" value="true"/>
|
||||
</properties>
|
||||
</tile>
|
||||
</tileset>
|
BIN
front/dist/maps/tilesets_deviant_milkian_1.png
vendored
Normal file
BIN
front/dist/maps/tilesets_deviant_milkian_1.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
BIN
front/dist/resources/objects/rockSprite.png
vendored
Normal file
BIN
front/dist/resources/objects/rockSprite.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
@ -1,14 +1,10 @@
|
||||
import {GameManagerInterface} from "./Phaser/Game/GameManager";
|
||||
|
||||
const SocketIo = require('socket.io-client');
|
||||
import Axios from "axios";
|
||||
import {API_URL} from "./Enum/EnvironmentVariable";
|
||||
|
||||
export interface PointInterface {
|
||||
x: number;
|
||||
y: number;
|
||||
toJson() : object;
|
||||
}
|
||||
|
||||
export class Message {
|
||||
class Message {
|
||||
userId: string;
|
||||
roomId: string;
|
||||
|
||||
@ -25,27 +21,42 @@ export class Message {
|
||||
}
|
||||
}
|
||||
|
||||
export class Point implements PointInterface{
|
||||
export interface PointInterface {
|
||||
x: number;
|
||||
y: number;
|
||||
direction : string;
|
||||
toJson() : object;
|
||||
}
|
||||
|
||||
constructor(x : number, y : number) {
|
||||
class Point implements PointInterface{
|
||||
x: number;
|
||||
y: number;
|
||||
direction : string;
|
||||
|
||||
constructor(x : number, y : number, direction : string = "none") {
|
||||
if(x === null || y === null){
|
||||
throw Error("position x and y cannot be null");
|
||||
}
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
toJson(){
|
||||
return {
|
||||
x : this.x,
|
||||
y: this.y
|
||||
y: this.y,
|
||||
direction: this.direction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class MessageUserPosition extends Message{
|
||||
export interface MessageUserPositionInterface {
|
||||
userId: string;
|
||||
roomId: string;
|
||||
position: PointInterface;
|
||||
}
|
||||
class MessageUserPosition extends Message implements MessageUserPositionInterface{
|
||||
position: PointInterface;
|
||||
|
||||
constructor(userId : string, roomId : string, point : Point) {
|
||||
@ -64,18 +75,61 @@ export class MessageUserPosition extends Message{
|
||||
}
|
||||
}
|
||||
|
||||
export class Connexion {
|
||||
export interface ListMessageUserPositionInterface {
|
||||
roomId : string;
|
||||
listUsersPosition: Array<MessageUserPosition>;
|
||||
}
|
||||
class ListMessageUserPosition{
|
||||
roomId : string;
|
||||
listUsersPosition: Array<MessageUserPosition>;
|
||||
|
||||
constructor(roomId : string, data : any) {
|
||||
this.roomId = roomId;
|
||||
this.listUsersPosition = new Array<MessageUserPosition>();
|
||||
data.forEach((userPosition: any) => {
|
||||
this.listUsersPosition.push(new MessageUserPosition(
|
||||
userPosition.userId,
|
||||
userPosition.roomId,
|
||||
new Point(
|
||||
userPosition.position.x,
|
||||
userPosition.position.y,
|
||||
userPosition.position.direction
|
||||
)
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
export interface ConnexionInterface {
|
||||
socket : any;
|
||||
token : string;
|
||||
email : string;
|
||||
userId: string;
|
||||
startedRoom : string;
|
||||
createConnexion() : Promise<any>;
|
||||
joinARoom(roomId : string) : void;
|
||||
sharePosition(roomId : string, x : number, y : number, direction : string) : void;
|
||||
positionOfAllUser() : void;
|
||||
}
|
||||
export class Connexion implements ConnexionInterface{
|
||||
socket : any;
|
||||
token : string;
|
||||
email : string;
|
||||
userId: string;
|
||||
startedRoom : string;
|
||||
|
||||
constructor(email : string) {
|
||||
GameManager: GameManagerInterface;
|
||||
|
||||
constructor(email : string, GameManager: GameManagerInterface) {
|
||||
this.email = email;
|
||||
Axios.post(`${API_URL}/login`, {email: email})
|
||||
this.GameManager = GameManager;
|
||||
}
|
||||
|
||||
createConnexion() : Promise<ConnexionInterface>{
|
||||
return Axios.post(`${API_URL}/login`, {email: this.email})
|
||||
.then((res) => {
|
||||
this.token = res.data.token;
|
||||
this.startedRoom = res.data.roomId;
|
||||
this.userId = res.data.userId;
|
||||
|
||||
this.socket = SocketIo(`${API_URL}`, {
|
||||
query: {
|
||||
@ -87,12 +141,13 @@ export class Connexion {
|
||||
this.joinARoom(this.startedRoom);
|
||||
|
||||
//share your first position
|
||||
this.sharePosition(0, 0);
|
||||
this.sharePosition(this.startedRoom, 0, 0);
|
||||
|
||||
//create listen event to get all data user shared by the back
|
||||
this.positionOfAllUser();
|
||||
|
||||
this.errorMessage();
|
||||
|
||||
return this;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
@ -104,18 +159,23 @@ export class Connexion {
|
||||
* Permit to join a room
|
||||
* @param roomId
|
||||
*/
|
||||
joinARoom(roomId : string){
|
||||
let messageUserPosition = new MessageUserPosition(this.email, this.startedRoom, new Point(0, 0));
|
||||
joinARoom(roomId : string) : void {
|
||||
let messageUserPosition = new MessageUserPosition(this.userId, this.startedRoom, new Point(0, 0));
|
||||
this.socket.emit('join-room', messageUserPosition.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Permit to share your position in map
|
||||
*
|
||||
* @param roomId
|
||||
* @param x
|
||||
* @param y
|
||||
* @param direction
|
||||
*/
|
||||
sharePosition(x : number, y : number){
|
||||
let messageUserPosition = new MessageUserPosition(this.email, this.startedRoom, new Point(x, y));
|
||||
sharePosition(roomId : string, x : number, y : number, direction : string = "none") : void{
|
||||
if(!this.socket){
|
||||
return;
|
||||
}
|
||||
let messageUserPosition = new MessageUserPosition(this.userId, roomId, new Point(x, y, direction));
|
||||
this.socket.emit('user-position', messageUserPosition.toString());
|
||||
}
|
||||
|
||||
@ -127,20 +187,24 @@ export class Connexion {
|
||||
* roomId: <string>,
|
||||
* position: {
|
||||
* x : <number>,
|
||||
* y : <number>
|
||||
* y : <number>,
|
||||
* direction: <string>
|
||||
* }
|
||||
* },
|
||||
* ...
|
||||
* ]
|
||||
**/
|
||||
positionOfAllUser(){
|
||||
this.socket.on("user-position", (message : string) => {
|
||||
//TODO show all user in map
|
||||
console.info("user-position", message);
|
||||
positionOfAllUser() : void {
|
||||
this.socket.on("user-position", (message: string) => {
|
||||
let dataList = JSON.parse(message);
|
||||
dataList.forEach((UserPositions: any) => {
|
||||
let listMessageUserPosition = new ListMessageUserPosition(UserPositions[0], UserPositions[1]);
|
||||
this.GameManager.shareUserPosition(listMessageUserPosition);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
errorMessage(){
|
||||
errorMessage() : void {
|
||||
this.socket.on('message-error', (message : string) => {
|
||||
console.error("message-error", message);
|
||||
})
|
||||
|
32
front/src/Cypress/CypressAsserter.ts
Normal file
32
front/src/Cypress/CypressAsserter.ts
Normal file
@ -0,0 +1,32 @@
|
||||
declare let window:any;
|
||||
|
||||
//this class is used to communicate with cypress, our e2e testing client
|
||||
//Since cypress cannot manipulate canvas, we notified it with console logs
|
||||
class CypressAsserter {
|
||||
|
||||
constructor() {
|
||||
window.cypressAsserter = this
|
||||
}
|
||||
|
||||
gameStarted() {
|
||||
console.log('Started the game')
|
||||
}
|
||||
|
||||
preloadStarted() {
|
||||
console.log('Preloading')
|
||||
}
|
||||
|
||||
preloadFinished() {
|
||||
console.log('Preloading done')
|
||||
}
|
||||
|
||||
initStarted() {
|
||||
console.log('startInit')
|
||||
}
|
||||
|
||||
initFinished() {
|
||||
console.log('startInit done')
|
||||
}
|
||||
}
|
||||
|
||||
export const cypressAsserter = new CypressAsserter()
|
@ -1,7 +1,13 @@
|
||||
const DEBUG_MODE: boolean = !!process.env.DEBUG_MODE || false;
|
||||
const API_URL = process.env.API_URL || "http://api.workadventure.localhost";
|
||||
const ROOM = [process.env.ROOM || "THECODINGMACHINE"];
|
||||
const RESOLUTION = 2;
|
||||
const ZOOM_LEVEL = 3/4;
|
||||
|
||||
export {
|
||||
DEBUG_MODE,
|
||||
API_URL,
|
||||
RESOLUTION
|
||||
RESOLUTION,
|
||||
ZOOM_LEVEL,
|
||||
ROOM
|
||||
}
|
@ -1,185 +0,0 @@
|
||||
import {RESOLUTION} from "./Enum/EnvironmentVariable";
|
||||
|
||||
export class GameScene extends Phaser.Scene {
|
||||
private player: Phaser.GameObjects.Sprite;
|
||||
|
||||
private keyZ: Phaser.Input.Keyboard.Key;
|
||||
private keyQ: Phaser.Input.Keyboard.Key;
|
||||
private keyS: Phaser.Input.Keyboard.Key;
|
||||
private keyD: Phaser.Input.Keyboard.Key;
|
||||
private keyRight: Phaser.Input.Keyboard.Key;
|
||||
private keyLeft: Phaser.Input.Keyboard.Key;
|
||||
private keyUp: Phaser.Input.Keyboard.Key;
|
||||
private keyDown: Phaser.Input.Keyboard.Key;
|
||||
private keyShift: Phaser.Input.Keyboard.Key;
|
||||
|
||||
private Mappy : Phaser.Tilemaps.Tilemap;
|
||||
|
||||
private startX = ((window.innerWidth / 2) / RESOLUTION);
|
||||
private startY = ((window.innerHeight / 2) / RESOLUTION);
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
key: "GameScene"
|
||||
});
|
||||
}
|
||||
|
||||
preload(): void {
|
||||
this.load.image('tiles', 'maps/tiles.png');
|
||||
this.load.tilemapTiledJSON('map', 'maps/map2.json');
|
||||
this.load.spritesheet('player',
|
||||
'resources/characters/pipoya/Male 01-1.png',
|
||||
{ frameWidth: 32, frameHeight: 32 }
|
||||
);
|
||||
}
|
||||
|
||||
init(): void {
|
||||
this.keyShift = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT);
|
||||
|
||||
this.keyZ = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z);
|
||||
this.keyQ = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q);
|
||||
this.keyS = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S);
|
||||
this.keyD = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D);
|
||||
|
||||
this.keyUp = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP);
|
||||
this.keyLeft = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT);
|
||||
this.keyDown = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN);
|
||||
this.keyRight = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT);
|
||||
}
|
||||
|
||||
private moveCamera(x:number, y:number, speedMultiplier: number): void {
|
||||
this.cameras.main.scrollX += speedMultiplier * 2 * x;
|
||||
this.cameras.main.scrollY += speedMultiplier * 2 * y;
|
||||
}
|
||||
|
||||
create(): void {
|
||||
this.Mappy = this.add.tilemap("map");
|
||||
let terrain = this.Mappy.addTilesetImage("tiles", "tiles");
|
||||
|
||||
let bottomLayer = this.Mappy.createStaticLayer("Calque 1", [terrain], 0, 0);
|
||||
let topLayer = this.Mappy.createStaticLayer("Calque 2", [terrain], 0, 0);
|
||||
|
||||
// Let's manage animations of the player
|
||||
this.anims.create({
|
||||
key: 'down',
|
||||
frames: this.anims.generateFrameNumbers('player', { start: 0, end: 2 }),
|
||||
frameRate: 10,
|
||||
repeat: -1
|
||||
});
|
||||
|
||||
this.anims.create({
|
||||
key: 'left',
|
||||
frames: this.anims.generateFrameNumbers('player', { start: 3, end: 5 }),
|
||||
frameRate: 10,
|
||||
repeat: -1
|
||||
});
|
||||
|
||||
this.anims.create({
|
||||
key: 'right',
|
||||
frames: this.anims.generateFrameNumbers('player', { start: 6, end: 8 }),
|
||||
frameRate: 10,
|
||||
repeat: -1
|
||||
});
|
||||
|
||||
this.anims.create({
|
||||
key: 'up',
|
||||
frames: this.anims.generateFrameNumbers('player', { start: 9, end: 11 }),
|
||||
frameRate: 10,
|
||||
repeat: -1
|
||||
});
|
||||
|
||||
//let player = this.add.sprite(450, 450, 'player');
|
||||
//player.anims.play('down');
|
||||
//player.setBounce(0.2);
|
||||
//player.setCollideWorldBounds(true);
|
||||
this.player = this.add.sprite(this.startX, this.startY, 'player');
|
||||
}
|
||||
|
||||
private angle: number = 0;
|
||||
|
||||
update(dt: number): void {
|
||||
let xCameraPosition = this.cameras.main.scrollX;
|
||||
let yCameraPosition = this.cameras.main.scrollY;
|
||||
|
||||
let speedMultiplier = this.keyShift.isDown ? 5 : 1;
|
||||
|
||||
if (this.keyZ.isDown || this.keyUp.isDown) {
|
||||
this.managePlayerAnimation('up');
|
||||
if (this.player.y > 0) {
|
||||
this.player.setY(this.player.y - 2);
|
||||
} else {
|
||||
this.player.setY(0);
|
||||
}
|
||||
|
||||
if (yCameraPosition > 0) {
|
||||
if (this.player.y < (this.Mappy.widthInPixels - this.startY)) {
|
||||
this.moveCamera(0, -1, speedMultiplier);
|
||||
}
|
||||
} else {
|
||||
this.cameras.main.scrollY = 0;
|
||||
}
|
||||
} else if (this.keyQ.isDown || this.keyLeft.isDown) {
|
||||
|
||||
this.managePlayerAnimation('left');
|
||||
if (this.player.x > 0) {
|
||||
this.player.setX(this.player.x - 2);
|
||||
} else {
|
||||
this.player.setX(0);
|
||||
}
|
||||
|
||||
if (xCameraPosition > 0) {
|
||||
if (this.player.x < (this.Mappy.heightInPixels - this.startX)) {
|
||||
this.moveCamera(-1, 0, speedMultiplier);
|
||||
}
|
||||
} else {
|
||||
this.cameras.main.scrollX = 0;
|
||||
}
|
||||
} else if (this.keyS.isDown || this.keyDown.isDown) {
|
||||
|
||||
this.managePlayerAnimation('down');
|
||||
if (this.Mappy.heightInPixels > this.player.y) {
|
||||
this.player.setY(this.player.y + 2);
|
||||
} else {
|
||||
this.player.setY(this.Mappy.heightInPixels);
|
||||
}
|
||||
|
||||
if (this.Mappy.heightInPixels > (yCameraPosition + (window.innerHeight / RESOLUTION))) {
|
||||
if (this.player.y > this.startY) {
|
||||
this.moveCamera(0, 1, speedMultiplier);
|
||||
}
|
||||
} else {
|
||||
this.cameras.main.scrollY = (this.Mappy.heightInPixels - (window.innerHeight / RESOLUTION));
|
||||
}
|
||||
} else if (this.keyD.isDown || this.keyRight.isDown) {
|
||||
|
||||
this.managePlayerAnimation('right');
|
||||
if (this.Mappy.widthInPixels > this.player.x) {
|
||||
this.player.setX(this.player.x + 2)
|
||||
} else {
|
||||
this.player.setX(this.Mappy.widthInPixels)
|
||||
}
|
||||
|
||||
if (this.Mappy.widthInPixels > (xCameraPosition + (window.innerWidth / RESOLUTION))) {
|
||||
if (this.player.x > this.startX) {
|
||||
this.moveCamera(1, 0, speedMultiplier);
|
||||
}
|
||||
} else {
|
||||
this.cameras.main.scrollX = (this.Mappy.widthInPixels - (window.innerWidth / RESOLUTION));
|
||||
}
|
||||
} else {
|
||||
this.managePlayerAnimation('none');
|
||||
}
|
||||
/*this.cameras.main.scrollX = Math.floor(300 + 300 * Math.cos(this.angle));
|
||||
this.cameras.main.scrollY = Math.floor(300 + 300 * Math.sin(this.angle));
|
||||
|
||||
this.angle = dt * 0.0001;*/
|
||||
}
|
||||
|
||||
managePlayerAnimation(direction: string) {
|
||||
if (!this.player.anims.currentAnim || this.player.anims.currentAnim.key !== direction) {
|
||||
this.player.anims.play(direction);
|
||||
} else if (direction === 'none' && this.player.anims.currentAnim) {
|
||||
this.player.anims.currentAnim.destroy();
|
||||
}
|
||||
}
|
||||
}
|
58
front/src/Phaser/Entity/PlayableCaracter.ts
Normal file
58
front/src/Phaser/Entity/PlayableCaracter.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import {getPlayerAnimations, playAnimation, PlayerAnimationNames} from "../Player/Animation";
|
||||
import {ActiveEventList, UserInputEvent} from "../UserInput/UserInputManager";
|
||||
import {SpeechBubble} from "./SpeechBubble";
|
||||
|
||||
export class PlayableCaracter extends Phaser.Physics.Arcade.Sprite {
|
||||
private bubble: SpeechBubble;
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, texture: string, frame?: string | number) {
|
||||
super(scene, x, y, texture, frame);
|
||||
|
||||
this.scene.sys.updateList.add(this);
|
||||
this.scene.sys.displayList.add(this);
|
||||
this.setScale(2);
|
||||
this.scene.physics.world.enableBody(this);
|
||||
this.setImmovable(true);
|
||||
this.setCollideWorldBounds(true);
|
||||
this.setSize(16, 16); //edit the hitbox to better match the caracter model
|
||||
this.setOffset(8, 16);
|
||||
}
|
||||
|
||||
move(x: number, y: number){
|
||||
|
||||
this.setVelocity(x, y);
|
||||
|
||||
//todo improve animations to better account for diagonal movement
|
||||
if (this.body.velocity.x > 0) { //moving right
|
||||
this.play(PlayerAnimationNames.WalkRight, true);
|
||||
}
|
||||
if (this.body.velocity.x < 0) { //moving left
|
||||
this.anims.playReverse(PlayerAnimationNames.WalkLeft, true);
|
||||
}
|
||||
if (this.body.velocity.y < 0) { //moving up
|
||||
this.play(PlayerAnimationNames.WalkUp, true);
|
||||
}
|
||||
if (this.body.velocity.y > 0) { //moving down
|
||||
this.play(PlayerAnimationNames.WalkDown, true);
|
||||
}
|
||||
|
||||
if(this.bubble) {
|
||||
this.bubble.moveBubble(this.x, this.y);
|
||||
}
|
||||
}
|
||||
|
||||
stop(){
|
||||
this.setVelocity(0, 0);
|
||||
this.play(PlayerAnimationNames.None, true);
|
||||
}
|
||||
|
||||
say(text: string) {
|
||||
if (this.bubble) return;
|
||||
this.bubble = new SpeechBubble(this.scene, this, text)
|
||||
//todo make the buble destroy on player movement?
|
||||
setTimeout(() => {
|
||||
this.bubble.destroy();
|
||||
this.bubble = null;
|
||||
}, 3000)
|
||||
}
|
||||
}
|
88
front/src/Phaser/Entity/SpeechBubble.ts
Normal file
88
front/src/Phaser/Entity/SpeechBubble.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import Scene = Phaser.Scene;
|
||||
import {PlayableCaracter} from "./PlayableCaracter";
|
||||
|
||||
export class SpeechBubble {
|
||||
private bubble: Phaser.GameObjects.Graphics;
|
||||
private content: Phaser.GameObjects.Text;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param scene
|
||||
* @param player
|
||||
* @param text
|
||||
*/
|
||||
constructor(scene: Scene, player: PlayableCaracter, text: string = "") {
|
||||
|
||||
let bubbleHeight = 50;
|
||||
let bubblePadding = 10;
|
||||
let bubbleWidth = bubblePadding * 2 + text.length * 10;
|
||||
let arrowHeight = bubbleHeight / 4;
|
||||
|
||||
this.bubble = scene.add.graphics({ x: player.x + 16, y: player.y - 80 });
|
||||
|
||||
// Bubble shadow
|
||||
this.bubble.fillStyle(0x222222, 0.5);
|
||||
this.bubble.fillRoundedRect(6, 6, bubbleWidth, bubbleHeight, 16);
|
||||
|
||||
// this.bubble color
|
||||
this.bubble.fillStyle(0xffffff, 1);
|
||||
|
||||
// this.bubble outline line style
|
||||
this.bubble.lineStyle(4, 0x565656, 1);
|
||||
|
||||
// this.bubble shape and outline
|
||||
this.bubble.strokeRoundedRect(0, 0, bubbleWidth, bubbleHeight, 16);
|
||||
this.bubble.fillRoundedRect(0, 0, bubbleWidth, bubbleHeight, 16);
|
||||
|
||||
// Calculate arrow coordinates
|
||||
let point1X = Math.floor(bubbleWidth / 7);
|
||||
let point1Y = bubbleHeight;
|
||||
let point2X = Math.floor((bubbleWidth / 7) * 2);
|
||||
let point2Y = bubbleHeight;
|
||||
let point3X = Math.floor(bubbleWidth / 7);
|
||||
let point3Y = Math.floor(bubbleHeight + arrowHeight);
|
||||
|
||||
// bubble arrow shadow
|
||||
this.bubble.lineStyle(4, 0x222222, 0.5);
|
||||
this.bubble.lineBetween(point2X - 1, point2Y + 6, point3X + 2, point3Y);
|
||||
|
||||
// bubble arrow fill
|
||||
this.bubble.fillTriangle(point1X, point1Y, point2X, point2Y, point3X, point3Y);
|
||||
this.bubble.lineStyle(2, 0x565656, 1);
|
||||
this.bubble.lineBetween(point2X, point2Y, point3X, point3Y);
|
||||
this.bubble.lineBetween(point1X, point1Y, point3X, point3Y);
|
||||
|
||||
this.content = scene.add.text(0, 0, text, { fontFamily: 'Arial', fontSize: 20, color: '#000000', align: 'center', wordWrap: { width: bubbleWidth - (bubblePadding * 2) } });
|
||||
|
||||
let bounds = this.content.getBounds();
|
||||
this.content.setPosition(this.bubble.x + (bubbleWidth / 2) - (bounds.width / 2), this.bubble.y + (bubbleHeight / 2) - (bounds.height / 2));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
*/
|
||||
moveBubble(x : number, y : number) {
|
||||
if (this.bubble) {
|
||||
this.bubble.setPosition((x + 16), (y - 80));
|
||||
}
|
||||
if (this.content) {
|
||||
let bubbleHeight = 50;
|
||||
let bubblePadding = 10;
|
||||
let bubbleWidth = bubblePadding * 2 + this.content.text.length * 10;
|
||||
let bounds = this.content.getBounds();
|
||||
//this.content.setPosition(x, y);
|
||||
this.content.setPosition(this.bubble.x + (bubbleWidth / 2) - (bounds.width / 2), this.bubble.y + (bubbleHeight / 2) - (bounds.height / 2));
|
||||
}
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.bubble.setVisible(false) //todo find a better way
|
||||
this.bubble.destroy();
|
||||
this.bubble = null;
|
||||
this.content.destroy();
|
||||
this.content = null;
|
||||
}
|
||||
|
||||
}
|
8
front/src/Phaser/Entity/Sprite.ts
Normal file
8
front/src/Phaser/Entity/Sprite.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export class Sprite extends Phaser.GameObjects.Sprite {
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, texture: string, frame?: number | string) {
|
||||
super(scene, x, y, texture, frame);
|
||||
scene.sys.updateList.add(this);
|
||||
scene.sys.displayList.add(this);
|
||||
}
|
||||
}
|
77
front/src/Phaser/Game/GameManager.ts
Normal file
77
front/src/Phaser/Game/GameManager.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import {GameSceneInterface, GameScene} from "./GameScene";
|
||||
import {ROOM} from "../../Enum/EnvironmentVariable"
|
||||
import {Connexion, ConnexionInterface, ListMessageUserPositionInterface} from "../../Connexion";
|
||||
|
||||
export enum StatusGameManagerEnum {
|
||||
IN_PROGRESS = 1,
|
||||
CURRENT_USER_CREATED = 2
|
||||
}
|
||||
|
||||
export let ConnexionInstance : ConnexionInterface;
|
||||
|
||||
export interface GameManagerInterface {
|
||||
GameScenes: Array<GameSceneInterface>;
|
||||
status : number;
|
||||
createCurrentPlayer() : void;
|
||||
shareUserPosition(ListMessageUserPosition : ListMessageUserPositionInterface): void;
|
||||
}
|
||||
export class GameManager implements GameManagerInterface {
|
||||
GameScenes: Array<GameSceneInterface> = [];
|
||||
status: number;
|
||||
|
||||
constructor() {
|
||||
this.status = StatusGameManagerEnum.IN_PROGRESS;
|
||||
ConnexionInstance = new Connexion("test@gmail.com", this);
|
||||
}
|
||||
|
||||
createGame(){
|
||||
return ConnexionInstance.createConnexion().then(() => {
|
||||
this.configureGame();
|
||||
/** TODO add loader in the page **/
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* permit to config rooms
|
||||
*/
|
||||
configureGame() {
|
||||
ROOM.forEach((roomId) => {
|
||||
let newGame = new GameScene(roomId, this);
|
||||
this.GameScenes.push((newGame as GameSceneInterface));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Permit to create player in started room
|
||||
* @param RoomId
|
||||
* @param UserId
|
||||
*/
|
||||
createCurrentPlayer(): void {
|
||||
//Get started room send by the backend
|
||||
let game: GameSceneInterface = this.GameScenes.find((Game: GameSceneInterface) => Game.RoomId === ConnexionInstance.startedRoom);
|
||||
game.createCurrentPlayer(ConnexionInstance.userId);
|
||||
this.status = StatusGameManagerEnum.CURRENT_USER_CREATED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Share position in game
|
||||
* @param ListMessageUserPosition
|
||||
*/
|
||||
shareUserPosition(ListMessageUserPosition: ListMessageUserPositionInterface): void {
|
||||
if (this.status === StatusGameManagerEnum.IN_PROGRESS) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let Game: GameSceneInterface = this.GameScenes.find((Game: GameSceneInterface) => Game.RoomId === ListMessageUserPosition.roomId);
|
||||
if (!Game) {
|
||||
return;
|
||||
}
|
||||
Game.shareUserPosition(ListMessageUserPosition.listUsersPosition)
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
266
front/src/Phaser/Game/GameScene.ts
Normal file
266
front/src/Phaser/Game/GameScene.ts
Normal file
@ -0,0 +1,266 @@
|
||||
import {GameManagerInterface, StatusGameManagerEnum} from "./GameManager";
|
||||
import {MessageUserPositionInterface} from "../../Connexion";
|
||||
import {CurrentGamerInterface, GamerInterface, Player} from "../Player/Player";
|
||||
import {DEBUG_MODE, RESOLUTION, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable";
|
||||
import Tile = Phaser.Tilemaps.Tile;
|
||||
import {ITiledMap, ITiledTileSet} from "../Map/ITiledMap";
|
||||
import {cypressAsserter} from "../../Cypress/CypressAsserter";
|
||||
|
||||
export enum Textures {
|
||||
Rock = 'rock',
|
||||
Player = 'playerModel',
|
||||
Map = 'map'
|
||||
}
|
||||
|
||||
export interface GameSceneInterface extends Phaser.Scene {
|
||||
RoomId : string;
|
||||
Map: Phaser.Tilemaps.Tilemap;
|
||||
createCurrentPlayer(UserId : string) : void;
|
||||
shareUserPosition(UsersPosition : Array<MessageUserPositionInterface>): void;
|
||||
}
|
||||
export class GameScene extends Phaser.Scene implements GameSceneInterface{
|
||||
GameManager : GameManagerInterface;
|
||||
RoomId : string;
|
||||
Terrains : Array<Phaser.Tilemaps.Tileset>;
|
||||
CurrentPlayer: CurrentGamerInterface;
|
||||
MapPlayers : Phaser.Physics.Arcade.Group;
|
||||
Map: Phaser.Tilemaps.Tilemap;
|
||||
Layers : Array<Phaser.Tilemaps.StaticTilemapLayer>;
|
||||
Objects : Array<Phaser.Physics.Arcade.Sprite>;
|
||||
map: ITiledMap;
|
||||
startX = (window.innerWidth / 2) / RESOLUTION;
|
||||
startY = (window.innerHeight / 2) / RESOLUTION;
|
||||
|
||||
|
||||
constructor(RoomId : string, GameManager : GameManagerInterface) {
|
||||
super({
|
||||
key: "GameScene"
|
||||
});
|
||||
this.RoomId = RoomId;
|
||||
this.GameManager = GameManager;
|
||||
this.Terrains = [];
|
||||
}
|
||||
|
||||
//hook preload scene
|
||||
preload(): void {
|
||||
cypressAsserter.preloadStarted();
|
||||
let mapUrl = 'maps/map.json';
|
||||
this.load.on('filecomplete-tilemapJSON-'+Textures.Map, (key: string, type: string, data: any) => {
|
||||
// Triggered when the map is loaded
|
||||
// Load tiles attached to the map recursively
|
||||
this.map = data.data;
|
||||
this.map.tilesets.forEach((tileset) => {
|
||||
if (typeof tileset.name === 'undefined' || typeof tileset.image === 'undefined') {
|
||||
console.warn("Don't know how to handle tileset ", tileset)
|
||||
return;
|
||||
}
|
||||
let path = mapUrl.substr(0, mapUrl.lastIndexOf('/'));
|
||||
this.load.image(tileset.name, path + '/' + tileset.image);
|
||||
})
|
||||
});
|
||||
this.load.tilemapTiledJSON(Textures.Map, mapUrl);
|
||||
this.load.image(Textures.Rock, 'resources/objects/rockSprite.png');
|
||||
this.load.spritesheet(Textures.Player,
|
||||
'resources/characters/pipoya/Male 01-1.png',
|
||||
{ frameWidth: 32, frameHeight: 32 }
|
||||
);
|
||||
cypressAsserter.preloadFinished();
|
||||
}
|
||||
|
||||
//hook initialisation
|
||||
init(){}
|
||||
|
||||
//hook create scene
|
||||
create(): void {
|
||||
cypressAsserter.initStarted();
|
||||
|
||||
//initalise map
|
||||
this.Map = this.add.tilemap("map");
|
||||
this.map.tilesets.forEach((tileset: ITiledTileSet) => {
|
||||
this.Terrains.push(this.Map.addTilesetImage(tileset.name, tileset.name));
|
||||
});
|
||||
|
||||
//permit to set bound collision
|
||||
this.physics.world.setBounds(0,0, this.Map.widthInPixels, this.Map.heightInPixels);
|
||||
|
||||
//add layer on map
|
||||
this.Layers = new Array<Phaser.Tilemaps.StaticTilemapLayer>();
|
||||
let depth = -2;
|
||||
this.map.layers.forEach((layer) => {
|
||||
if (layer.type === 'tilelayer') {
|
||||
this.addLayer( this.Map.createStaticLayer(layer.name, this.Terrains, 0, 0).setDepth(depth) );
|
||||
} else if (layer.type === 'objectgroup' && layer.name === 'floorLayer') {
|
||||
depth = -1;
|
||||
}
|
||||
});
|
||||
|
||||
if (depth === -2) {
|
||||
throw new Error('Your map MUST contain a layer of type "objectgroup" whose name is "floorLayer" that represents the layer characters are drawn at.');
|
||||
}
|
||||
|
||||
//add entities
|
||||
this.Objects = new Array<Phaser.Physics.Arcade.Sprite>();
|
||||
this.addSpite(this.physics.add.sprite(200, 400, Textures.Rock, 26));
|
||||
|
||||
//init event click
|
||||
this.EventToClickOnTile();
|
||||
|
||||
//initialise list of other player
|
||||
this.MapPlayers = this.physics.add.group({ immovable: true });
|
||||
|
||||
//notify game manager can to create currentUser in map
|
||||
this.GameManager.createCurrentPlayer();
|
||||
|
||||
|
||||
//initialise camera
|
||||
this.initCamera();
|
||||
cypressAsserter.initFinished();
|
||||
}
|
||||
|
||||
//todo: in a dedicated class/function?
|
||||
initCamera() {
|
||||
this.cameras.main.setBounds(0,0, this.Map.widthInPixels, this.Map.heightInPixels);
|
||||
this.cameras.main.startFollow(this.CurrentPlayer);
|
||||
this.cameras.main.setZoom(ZOOM_LEVEL);
|
||||
}
|
||||
|
||||
addLayer(Layer : Phaser.Tilemaps.StaticTilemapLayer){
|
||||
this.Layers.push(Layer);
|
||||
}
|
||||
|
||||
createCollisionWithPlayer() {
|
||||
//add collision layer
|
||||
this.Layers.forEach((Layer: Phaser.Tilemaps.StaticTilemapLayer) => {
|
||||
this.physics.add.collider(this.CurrentPlayer, Layer, (object1: any, object2: any) => {
|
||||
//this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name)
|
||||
});
|
||||
Layer.setCollisionByProperty({collides: true});
|
||||
if (DEBUG_MODE) {
|
||||
//debug code to see the collision hitbox of the object in the top layer
|
||||
Layer.renderDebug(this.add.graphics(), {
|
||||
tileColor: null, //non-colliding tiles
|
||||
collidingTileColor: new Phaser.Display.Color(243, 134, 48, 200), // Colliding tiles,
|
||||
faceColor: new Phaser.Display.Color(40, 39, 37, 255) // Colliding face edges
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addSpite(Object : Phaser.Physics.Arcade.Sprite){
|
||||
Object.setImmovable(true);
|
||||
this.Objects.push(Object);
|
||||
}
|
||||
|
||||
createCollisionObject(){
|
||||
this.Objects.forEach((Object : Phaser.Physics.Arcade.Sprite) => {
|
||||
this.physics.add.collider(this.CurrentPlayer, Object, (object1: any, object2: any) => {
|
||||
//this.CurrentPlayer.say("Collision with object : " + (object2 as Phaser.Physics.Arcade.Sprite).texture.key)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
createCurrentPlayer(UserId : string){
|
||||
//initialise player
|
||||
this.CurrentPlayer = new Player(
|
||||
UserId,
|
||||
this,
|
||||
this.startX,
|
||||
this.startY,
|
||||
);
|
||||
this.CurrentPlayer.initAnimation();
|
||||
|
||||
//create collision
|
||||
this.createCollisionWithPlayer();
|
||||
this.createCollisionObject();
|
||||
}
|
||||
|
||||
EventToClickOnTile(){
|
||||
// debug code to get a tile properties by clicking on it
|
||||
this.input.on("pointerdown", (pointer: Phaser.Input.Pointer)=>{
|
||||
//pixel position toz tile position
|
||||
let tile = this.Map.getTileAt(this.Map.worldToTileX(pointer.worldX), this.Map.worldToTileY(pointer.worldY));
|
||||
if(tile){
|
||||
console.log(tile)
|
||||
this.CurrentPlayer.say("Your touch " + tile.layer.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param time
|
||||
* @param delta The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate.
|
||||
*/
|
||||
update(time: number, delta: number) : void {
|
||||
this.CurrentPlayer.moveUser(delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Share position in scene
|
||||
* @param UsersPosition
|
||||
*/
|
||||
shareUserPosition(UsersPosition : Array<MessageUserPositionInterface>): void {
|
||||
this.updateOrCreateMapPlayer(UsersPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new player and clean the player on the map
|
||||
* @param UsersPosition
|
||||
*/
|
||||
updateOrCreateMapPlayer(UsersPosition : Array<MessageUserPositionInterface>){
|
||||
if(!this.CurrentPlayer){
|
||||
return;
|
||||
}
|
||||
|
||||
//add or create new user
|
||||
UsersPosition.forEach((userPosition : MessageUserPositionInterface) => {
|
||||
if(userPosition.userId === this.CurrentPlayer.userId){
|
||||
return;
|
||||
}
|
||||
let player = this.findPlayerInMap(userPosition.userId);
|
||||
if(!player){
|
||||
this.addPlayer(userPosition);
|
||||
}else{
|
||||
player.updatePosition(userPosition);
|
||||
}
|
||||
});
|
||||
|
||||
//clean map
|
||||
this.MapPlayers.getChildren().forEach((player: GamerInterface) => {
|
||||
if(UsersPosition.find((message : MessageUserPositionInterface) => message.userId === player.userId)){
|
||||
return;
|
||||
}
|
||||
player.destroy();
|
||||
this.MapPlayers.remove(player);
|
||||
});
|
||||
}
|
||||
|
||||
private findPlayerInMap(UserId : string) : GamerInterface | null{
|
||||
let player = this.MapPlayers.getChildren().find((player: Player) => UserId === player.userId);
|
||||
if(!player){
|
||||
return null;
|
||||
}
|
||||
return (player as GamerInterface);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new player
|
||||
* @param MessageUserPosition
|
||||
*/
|
||||
addPlayer(MessageUserPosition : MessageUserPositionInterface){
|
||||
//initialise player
|
||||
let player = new Player(
|
||||
MessageUserPosition.userId,
|
||||
this,
|
||||
MessageUserPosition.position.x,
|
||||
MessageUserPosition.position.y,
|
||||
);
|
||||
player.initAnimation();
|
||||
this.MapPlayers.add(player);
|
||||
player.updatePosition(MessageUserPosition);
|
||||
|
||||
//init colision
|
||||
this.physics.add.collider(this.CurrentPlayer, player, (CurrentPlayer: CurrentGamerInterface, MapPlayer: GamerInterface) => {
|
||||
CurrentPlayer.say("Hello, how are you ? ");
|
||||
});
|
||||
}
|
||||
}
|
113
front/src/Phaser/Map/ITiledMap.ts
Normal file
113
front/src/Phaser/Map/ITiledMap.ts
Normal file
@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Tiled Map Interface
|
||||
*
|
||||
* Represents the interface for the Tiled exported data structure (JSON). Used
|
||||
* when loading resources via Resource loader.
|
||||
*/
|
||||
export interface ITiledMap {
|
||||
width: number;
|
||||
height: number;
|
||||
layers: ITiledMapLayer[];
|
||||
nextobjectid: number;
|
||||
|
||||
/**
|
||||
* Map orientation (orthogonal)
|
||||
*/
|
||||
orientation: string;
|
||||
properties: {[key: string]: string};
|
||||
|
||||
/**
|
||||
* Render order (right-down)
|
||||
*/
|
||||
renderorder: string;
|
||||
tileheight: number;
|
||||
tilewidth: number;
|
||||
tilesets: ITiledTileSet[];
|
||||
version: number;
|
||||
}
|
||||
|
||||
export interface ITiledMapLayer {
|
||||
data: number[]|string;
|
||||
height: number;
|
||||
name: string;
|
||||
opacity: number;
|
||||
properties: {[key: string]: string};
|
||||
encoding: string;
|
||||
compression?: string;
|
||||
|
||||
/**
|
||||
* Type of layer (tilelayer, objectgroup)
|
||||
*/
|
||||
type: string;
|
||||
visible: boolean;
|
||||
width: number;
|
||||
x: number;
|
||||
y: number;
|
||||
|
||||
/**
|
||||
* Draw order (topdown (default), index)
|
||||
*/
|
||||
draworder: string;
|
||||
objects: ITiledMapObject[];
|
||||
}
|
||||
|
||||
export 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}[];
|
||||
}
|
||||
|
||||
export interface ITiledTileSet {
|
||||
firstgid: number;
|
||||
image: string;
|
||||
|
||||
imageheight: number;
|
||||
imagewidth: number;
|
||||
margin: number;
|
||||
name: string;
|
||||
properties: {[key: string]: string};
|
||||
spacing: number;
|
||||
tilecount: number;
|
||||
tileheight: number;
|
||||
tilewidth: number;
|
||||
transparentcolor: string;
|
||||
terrains: ITiledMapTerrain[];
|
||||
tiles: {[key: string]: { terrain: number[] }};
|
||||
|
||||
/**
|
||||
* Refers to external tileset file (should be JSON)
|
||||
*/
|
||||
source: string;
|
||||
}
|
||||
|
||||
export interface ITiledMapTerrain {
|
||||
name: string;
|
||||
tile: number;
|
||||
}
|
40
front/src/Phaser/NonPlayer/NonPlayer.ts
Normal file
40
front/src/Phaser/NonPlayer/NonPlayer.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import {PlayableCaracter} from "../Entity/PlayableCaracter";
|
||||
import {Textures} from "../Game/GameScene";
|
||||
import {UserInputEvent} from "../UserInput/UserInputManager";
|
||||
import {Player} from "../Player/Player";
|
||||
import {MessageUserPositionInterface} from "../../Connexion";
|
||||
import {playAnimation} from "../Player/Animation";
|
||||
|
||||
export class NonPlayer extends PlayableCaracter {
|
||||
|
||||
isFleeing: boolean = false;
|
||||
fleeingDirection:any = null //todo create a vector class
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number) {
|
||||
super(scene, x, y, Textures.Player, 1);
|
||||
this.setSize(32, 32); //edit the hitbox to better match the caracter model
|
||||
}
|
||||
|
||||
|
||||
updatePosition(MessageUserPosition : MessageUserPositionInterface){
|
||||
playAnimation(this, MessageUserPosition.position.direction);
|
||||
this.setX(MessageUserPosition.position.x);
|
||||
this.setY(MessageUserPosition.position.y);
|
||||
}
|
||||
|
||||
fleeFrom(player:Player) {
|
||||
if (this.isFleeing) return;
|
||||
this.say("Don't touch me!");
|
||||
this.isFleeing = true;
|
||||
|
||||
setTimeout(() => {
|
||||
this.say("Feww, I escaped.");
|
||||
this.isFleeing = false
|
||||
this.fleeingDirection = null
|
||||
}, 3000);
|
||||
|
||||
let vectorX = this.x - player.x;
|
||||
let vectorY = this.y - player.y;
|
||||
this.fleeingDirection = {x: vectorX, y: vectorY}
|
||||
}
|
||||
}
|
58
front/src/Phaser/Player/Animation.ts
Normal file
58
front/src/Phaser/Player/Animation.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import {Textures} from "../Game/GameScene";
|
||||
|
||||
interface AnimationData {
|
||||
key: string;
|
||||
frameRate: number;
|
||||
repeat: number;
|
||||
frameModel: string; //todo use an enum
|
||||
frameStart: number;
|
||||
frameEnd: number;
|
||||
}
|
||||
|
||||
export enum PlayerAnimationNames {
|
||||
WalkDown = 'down',
|
||||
WalkLeft = 'left',
|
||||
WalkUp = 'up',
|
||||
WalkRight = 'right',
|
||||
None = 'none',
|
||||
}
|
||||
|
||||
export const getPlayerAnimations = (): AnimationData[] => {
|
||||
return [{
|
||||
key: PlayerAnimationNames.WalkDown,
|
||||
frameModel: Textures.Player,
|
||||
frameStart: 0,
|
||||
frameEnd: 2,
|
||||
frameRate: 10,
|
||||
repeat: -1
|
||||
}, {
|
||||
key: PlayerAnimationNames.WalkLeft,
|
||||
frameModel: Textures.Player,
|
||||
frameStart: 3,
|
||||
frameEnd: 5,
|
||||
frameRate: 10,
|
||||
repeat: -1
|
||||
}, {
|
||||
key: PlayerAnimationNames.WalkRight,
|
||||
frameModel: Textures.Player,
|
||||
frameStart: 6,
|
||||
frameEnd: 8,
|
||||
frameRate: 10,
|
||||
repeat: -1
|
||||
}, {
|
||||
key: PlayerAnimationNames.WalkUp,
|
||||
frameModel: Textures.Player,
|
||||
frameStart: 9,
|
||||
frameEnd: 11,
|
||||
frameRate: 10,
|
||||
repeat: -1
|
||||
}];
|
||||
};
|
||||
|
||||
export const playAnimation = (Player : Phaser.GameObjects.Sprite, direction : string) => {
|
||||
if (!Player.anims.currentAnim || Player.anims.currentAnim.key !== direction) {
|
||||
Player.anims.play(direction);
|
||||
} else if (direction === PlayerAnimationNames.None && Player.anims.currentAnim) {
|
||||
Player.anims.currentAnim.destroy();
|
||||
}
|
||||
}
|
107
front/src/Phaser/Player/Player.ts
Normal file
107
front/src/Phaser/Player/Player.ts
Normal file
@ -0,0 +1,107 @@
|
||||
import {getPlayerAnimations, playAnimation, PlayerAnimationNames} from "./Animation";
|
||||
import {GameSceneInterface, Textures} from "../Game/GameScene";
|
||||
import {ConnexionInstance} from "../Game/GameManager";
|
||||
import {MessageUserPositionInterface} from "../../Connexion";
|
||||
import {ActiveEventList, UserInputEvent, UserInputManager} from "../UserInput/UserInputManager";
|
||||
import {PlayableCaracter} from "../Entity/PlayableCaracter";
|
||||
|
||||
export interface CurrentGamerInterface extends PlayableCaracter{
|
||||
userId : string;
|
||||
PlayerValue : string;
|
||||
initAnimation() : void;
|
||||
moveUser(delta: number) : void;
|
||||
say(text : string) : void;
|
||||
}
|
||||
|
||||
export interface GamerInterface extends PlayableCaracter{
|
||||
userId : string;
|
||||
PlayerValue : string;
|
||||
initAnimation() : void;
|
||||
updatePosition(MessageUserPosition : MessageUserPositionInterface) : void;
|
||||
say(text : string) : void;
|
||||
}
|
||||
|
||||
export class Player extends PlayableCaracter implements CurrentGamerInterface, GamerInterface {
|
||||
userId: string;
|
||||
PlayerValue: string;
|
||||
userInputManager: UserInputManager;
|
||||
|
||||
constructor(
|
||||
userId: string,
|
||||
Scene: GameSceneInterface,
|
||||
x: number,
|
||||
y: number,
|
||||
PlayerValue: string = Textures.Player
|
||||
) {
|
||||
super(Scene, x, y, PlayerValue, 1);
|
||||
|
||||
//create input to move
|
||||
this.userInputManager = new UserInputManager(Scene);
|
||||
|
||||
//set data
|
||||
this.userId = userId;
|
||||
this.PlayerValue = PlayerValue;
|
||||
|
||||
//the current player model should be push away by other players to prevent conflict
|
||||
this.setImmovable(false);
|
||||
}
|
||||
|
||||
initAnimation(): void {
|
||||
getPlayerAnimations().forEach(d => {
|
||||
this.scene.anims.create({
|
||||
key: d.key,
|
||||
frames: this.scene.anims.generateFrameNumbers(d.frameModel, {start: d.frameStart, end: d.frameEnd}),
|
||||
frameRate: d.frameRate,
|
||||
repeat: d.repeat
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
moveUser(delta: number): void {
|
||||
//if user client on shift, camera and player speed
|
||||
let haveMove = false;
|
||||
let direction = null;
|
||||
|
||||
let activeEvents = this.userInputManager.getEventListForGameTick();
|
||||
let speedMultiplier = activeEvents.get(UserInputEvent.SpeedUp) ? 25 : 9;
|
||||
let moveAmount = speedMultiplier * delta;
|
||||
|
||||
if (activeEvents.get(UserInputEvent.MoveUp)) {
|
||||
this.move(0, -moveAmount);
|
||||
haveMove = true;
|
||||
direction = PlayerAnimationNames.WalkUp;
|
||||
}
|
||||
if (activeEvents.get(UserInputEvent.MoveLeft)) {
|
||||
this.move(-moveAmount, 0);
|
||||
haveMove = true;
|
||||
direction = PlayerAnimationNames.WalkLeft;
|
||||
}
|
||||
if (activeEvents.get(UserInputEvent.MoveDown)) {
|
||||
this.move(0, moveAmount);
|
||||
haveMove = true;
|
||||
direction = PlayerAnimationNames.WalkDown;
|
||||
}
|
||||
if (activeEvents.get(UserInputEvent.MoveRight)) {
|
||||
this.move(moveAmount, 0);
|
||||
haveMove = true;
|
||||
direction = PlayerAnimationNames.WalkRight;
|
||||
}
|
||||
if (!haveMove) {
|
||||
direction = PlayerAnimationNames.None;
|
||||
this.stop();
|
||||
}
|
||||
this.sharePosition(direction);
|
||||
}
|
||||
|
||||
private sharePosition(direction: string) {
|
||||
if (ConnexionInstance) {
|
||||
ConnexionInstance.sharePosition((this.scene as GameSceneInterface).RoomId, this.x, this.y, direction);
|
||||
}
|
||||
}
|
||||
|
||||
updatePosition(MessageUserPosition: MessageUserPositionInterface) {
|
||||
playAnimation(this, MessageUserPosition.position.direction);
|
||||
this.setX(MessageUserPosition.position.x);
|
||||
this.setY(MessageUserPosition.position.y);
|
||||
}
|
||||
}
|
68
front/src/Phaser/UserInput/UserInputManager.ts
Normal file
68
front/src/Phaser/UserInput/UserInputManager.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import Map = Phaser.Structs.Map;
|
||||
import {GameSceneInterface} from "../Game/GameScene";
|
||||
|
||||
interface UserInputManagerDatum {
|
||||
keyCode: number;
|
||||
keyInstance: Phaser.Input.Keyboard.Key;
|
||||
event: UserInputEvent
|
||||
}
|
||||
|
||||
export enum UserInputEvent {
|
||||
MoveLeft = 1,
|
||||
MoveUp,
|
||||
MoveRight,
|
||||
MoveDown,
|
||||
SpeedUp,
|
||||
Interact,
|
||||
Shout,
|
||||
}
|
||||
|
||||
//we cannot the map structure so we have to create a replacment
|
||||
export class ActiveEventList {
|
||||
private KeysCode : any;
|
||||
constructor() {
|
||||
this.KeysCode = {};
|
||||
}
|
||||
get(event: UserInputEvent): boolean {
|
||||
return this.KeysCode[event] || false;
|
||||
}
|
||||
set(event: UserInputEvent, value: boolean): boolean {
|
||||
return this.KeysCode[event] = true;
|
||||
}
|
||||
}
|
||||
|
||||
//this class is responsible for catching user inputs and listing all active user actions at every game tick events.
|
||||
export class UserInputManager {
|
||||
private KeysCode: UserInputManagerDatum[] = [
|
||||
{keyCode: Phaser.Input.Keyboard.KeyCodes.Z, event: UserInputEvent.MoveUp, keyInstance: null},
|
||||
{keyCode: Phaser.Input.Keyboard.KeyCodes.Q, event: UserInputEvent.MoveLeft, keyInstance: null},
|
||||
{keyCode: Phaser.Input.Keyboard.KeyCodes.S, event: UserInputEvent.MoveDown, keyInstance: null},
|
||||
{keyCode: Phaser.Input.Keyboard.KeyCodes.D, event: UserInputEvent.MoveRight, keyInstance: null},
|
||||
|
||||
{keyCode: Phaser.Input.Keyboard.KeyCodes.UP, event: UserInputEvent.MoveUp, keyInstance: null},
|
||||
{keyCode: Phaser.Input.Keyboard.KeyCodes.LEFT, event: UserInputEvent.MoveLeft, keyInstance: null},
|
||||
{keyCode: Phaser.Input.Keyboard.KeyCodes.DOWN, event: UserInputEvent.MoveDown, keyInstance: null},
|
||||
{keyCode: Phaser.Input.Keyboard.KeyCodes.RIGHT, event: UserInputEvent.MoveRight, keyInstance: null},
|
||||
|
||||
{keyCode: Phaser.Input.Keyboard.KeyCodes.SHIFT, event: UserInputEvent.SpeedUp, keyInstance: null},
|
||||
|
||||
{keyCode: Phaser.Input.Keyboard.KeyCodes.E, event: UserInputEvent.Interact, keyInstance: null},
|
||||
{keyCode: Phaser.Input.Keyboard.KeyCodes.F, event: UserInputEvent.Shout, keyInstance: null},
|
||||
];
|
||||
|
||||
constructor(Scene : GameSceneInterface) {
|
||||
this.KeysCode.forEach(d => {
|
||||
d.keyInstance = Scene.input.keyboard.addKey(d.keyCode);
|
||||
});
|
||||
}
|
||||
|
||||
getEventListForGameTick(): ActiveEventList {
|
||||
let eventsMap = new ActiveEventList();
|
||||
this.KeysCode.forEach(d => {
|
||||
if (d. keyInstance.isDown) {
|
||||
eventsMap.set(d.event, true);
|
||||
}
|
||||
});
|
||||
return eventsMap;
|
||||
}
|
||||
}
|
@ -1,22 +1,32 @@
|
||||
import 'phaser';
|
||||
import GameConfig = Phaser.Types.Core.GameConfig;
|
||||
import {GameScene} from "./GameScene";
|
||||
import {Connexion} from "./Connexion";
|
||||
import {RESOLUTION} from "./Enum/EnvironmentVariable";
|
||||
import {GameManager} from "./Phaser/Game/GameManager";
|
||||
import {DEBUG_MODE, RESOLUTION} from "./Enum/EnvironmentVariable";
|
||||
import {cypressAsserter} from "./Cypress/CypressAsserter";
|
||||
|
||||
let gameManager = new GameManager();
|
||||
|
||||
const config: GameConfig = {
|
||||
title: "Office game",
|
||||
width: window.innerWidth / RESOLUTION,
|
||||
height: window.innerHeight / RESOLUTION,
|
||||
parent: "game",
|
||||
scene: [GameScene],
|
||||
scene: gameManager.GameScenes,
|
||||
zoom: RESOLUTION,
|
||||
physics: {
|
||||
default: "arcade",
|
||||
arcade: {
|
||||
debug: DEBUG_MODE
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let game = new Phaser.Game(config);
|
||||
cypressAsserter.gameStarted();
|
||||
|
||||
window.addEventListener('resize', function (event) {
|
||||
game.scale.resize(window.innerWidth / RESOLUTION, window.innerHeight / RESOLUTION);
|
||||
gameManager.createGame().then(() => {
|
||||
let game = new Phaser.Game(config);
|
||||
|
||||
window.addEventListener('resize', function (event) {
|
||||
game.scale.resize(window.innerWidth / RESOLUTION, window.innerHeight / RESOLUTION);
|
||||
});
|
||||
});
|
||||
|
||||
const connexion = new Connexion("test@gmail.com");
|
||||
|
@ -29,6 +29,6 @@ module.exports = {
|
||||
new webpack.ProvidePlugin({
|
||||
Phaser: 'phaser'
|
||||
}),
|
||||
new webpack.EnvironmentPlugin(['API_URL'])
|
||||
new webpack.EnvironmentPlugin(['API_URL', 'DEBUG_MODE'])
|
||||
]
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user