upgrade latest dev
@ -10,6 +10,8 @@ START_ROOM_URL=/_/global/maps.workadventure.localhost/Floor0/floor0.json
|
|||||||
# If you are using Coturn, this is the value of the "static-auth-secret" parameter in your coturn config file.
|
# If you are using Coturn, this is the value of the "static-auth-secret" parameter in your coturn config file.
|
||||||
# Keep empty if you are sharing hard coded / clear text credentials.
|
# Keep empty if you are sharing hard coded / clear text credentials.
|
||||||
TURN_STATIC_AUTH_SECRET=
|
TURN_STATIC_AUTH_SECRET=
|
||||||
|
DISABLE_NOTIFICATIONS=true
|
||||||
|
SKIP_RENDER_OPTIMIZATIONS=false
|
||||||
|
|
||||||
# The email address used by Let's encrypt to send renewal warnings (compulsory)
|
# The email address used by Let's encrypt to send renewal warnings (compulsory)
|
||||||
ACME_EMAIL=
|
ACME_EMAIL=
|
||||||
|
30
.github/workflows/build-and-deploy.yml
vendored
@ -1,6 +1,8 @@
|
|||||||
name: Build, push and deploy Docker image
|
name: Build, push and deploy Docker image
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
release:
|
release:
|
||||||
types: [created]
|
types: [created]
|
||||||
pull_request:
|
pull_request:
|
||||||
@ -14,7 +16,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build-front:
|
build-front:
|
||||||
if: ${{ github.event.release || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -34,11 +36,11 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
repository: thecodingmachine/workadventure-front
|
repository: thecodingmachine/workadventure-front
|
||||||
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||||
add_git_labels: true
|
add_git_labels: true
|
||||||
|
|
||||||
build-back:
|
build-back:
|
||||||
if: ${{ github.event.release || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -57,11 +59,11 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
repository: thecodingmachine/workadventure-back
|
repository: thecodingmachine/workadventure-back
|
||||||
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||||
add_git_labels: true
|
add_git_labels: true
|
||||||
|
|
||||||
build-pusher:
|
build-pusher:
|
||||||
if: ${{ github.event.release || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -80,11 +82,11 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
repository: thecodingmachine/workadventure-pusher
|
repository: thecodingmachine/workadventure-pusher
|
||||||
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||||
add_git_labels: true
|
add_git_labels: true
|
||||||
|
|
||||||
build-uploader:
|
build-uploader:
|
||||||
if: ${{ github.event.release || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -103,11 +105,11 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
repository: thecodingmachine/workadventure-uploader
|
repository: thecodingmachine/workadventure-uploader
|
||||||
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||||
add_git_labels: true
|
add_git_labels: true
|
||||||
|
|
||||||
build-maps:
|
build-maps:
|
||||||
if: ${{ github.event.release || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -127,7 +129,7 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
repository: thecodingmachine/workadventure-maps
|
repository: thecodingmachine/workadventure-maps
|
||||||
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||||
add_git_labels: true
|
add_git_labels: true
|
||||||
|
|
||||||
deeploy:
|
deeploy:
|
||||||
@ -138,7 +140,7 @@ jobs:
|
|||||||
- build-maps
|
- build-maps
|
||||||
- build-uploader
|
- build-uploader
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@ -156,14 +158,14 @@ jobs:
|
|||||||
JITSI_URL: ${{ secrets.JITSI_URL }}
|
JITSI_URL: ${{ secrets.JITSI_URL }}
|
||||||
SECRET_JITSI_KEY: ${{ secrets.SECRET_JITSI_KEY }}
|
SECRET_JITSI_KEY: ${{ secrets.SECRET_JITSI_KEY }}
|
||||||
TURN_STATIC_AUTH_SECRET: ${{ secrets.TURN_STATIC_AUTH_SECRET }}
|
TURN_STATIC_AUTH_SECRET: ${{ secrets.TURN_STATIC_AUTH_SECRET }}
|
||||||
DEPLOY_REF: ${{ env.GITHUB_HEAD_REF_SLUG }}
|
DEPLOY_REF: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||||
with:
|
with:
|
||||||
namespace: workadventure-${{ env.GITHUB_HEAD_REF_SLUG }}
|
namespace: workadventure-${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||||
|
|
||||||
- name: Add a comment in PR
|
- name: Add a comment in PR
|
||||||
uses: unsplash/comment-on-pr@v1.2.0
|
uses: unsplash/comment-on-pr@v1.2.0
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
msg: Environment deployed at https://play.${{ env.GITHUB_HEAD_REF_SLUG }}.test.workadventu.re
|
msg: Environment deployed at https://play.${{ env.GITHUB_HEAD_REF_SLUG }}.test.workadventu.re
|
||||||
check_for_duplicate_msg: true
|
|
||||||
|
2
.github/workflows/continuous_integration.yml
vendored
@ -49,7 +49,7 @@ jobs:
|
|||||||
- name: "Build"
|
- name: "Build"
|
||||||
run: yarn run build
|
run: yarn run build
|
||||||
env:
|
env:
|
||||||
API_URL: "localhost:8080"
|
PUSHER_URL: "//localhost:8080"
|
||||||
working-directory: "front"
|
working-directory: "front"
|
||||||
|
|
||||||
- name: "Lint"
|
- name: "Lint"
|
||||||
|
23
CHANGELOG.md
@ -1,4 +1,25 @@
|
|||||||
## Version 1.3.0 - in dev
|
## Version 1.3.9 - in dev
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
- Scripting API:
|
||||||
|
- Changed function names: `restorePlayerControl` => `restorePlayerControls`, `disablePlayerControl` => `disablePlayerControls`.
|
||||||
|
Please keep in mind that the scripting API is still experimental. Some breaking changes can occur in it until we mark it as stable.
|
||||||
|
|
||||||
|
### Updates
|
||||||
|
|
||||||
|
- Mobile support has been improved
|
||||||
|
- WorkAdventure automatically sets the zoom level based on the viewport size to ensure a sensible size of the map is visible, whatever the viewport used
|
||||||
|
- Mouse wheel support to zoom in / out
|
||||||
|
- Pinch support on mobile to zoom in / out
|
||||||
|
- Improved virtual joystick size (adapts to the zoom level)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Pinch gesture does no longer move the character
|
||||||
|
|
||||||
|
## Version 1.3.0
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
|
|
||||||
|
@ -1704,9 +1704,9 @@ lodash.once@^4.0.0:
|
|||||||
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
||||||
|
|
||||||
lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19:
|
lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19:
|
||||||
version "4.17.20"
|
version "4.17.21"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
long@~3:
|
long@~3:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
local env = std.extVar("env"),
|
local env = std.extVar("env"),
|
||||||
local namespace = env.DEPLOY_REF,
|
local namespace = env.DEPLOY_REF,
|
||||||
local tag = namespace,
|
local tag = namespace,
|
||||||
local url = if namespace == "master" then "workadventu.re" else namespace+".test.workadventu.re",
|
local url = namespace+".test.workadventu.re",
|
||||||
// develop branch does not use admin because of issue with SSL certificate of admin as of now.
|
// develop branch does not use admin because of issue with SSL certificate of admin as of now.
|
||||||
local adminUrl = if namespace == "master" || namespace == "develop" || std.startsWith(namespace, "admin") then "https://"+url else null,
|
local adminUrl = if namespace == "master" || namespace == "develop" || std.startsWith(namespace, "admin") then "https://"+url else null,
|
||||||
"$schema": "https://raw.githubusercontent.com/thecodingmachine/deeployer/master/deeployer.schema.json",
|
"$schema": "https://raw.githubusercontent.com/thecodingmachine/deeployer/master/deeployer.schema.json",
|
||||||
@ -25,10 +25,7 @@
|
|||||||
"TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET,
|
"TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET,
|
||||||
} + (if adminUrl != null then {
|
} + (if adminUrl != null then {
|
||||||
"ADMIN_API_URL": adminUrl,
|
"ADMIN_API_URL": adminUrl,
|
||||||
} else {}) + if namespace != "master" then {
|
} else {})
|
||||||
// Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids!
|
|
||||||
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"back2": {
|
"back2": {
|
||||||
"image": "thecodingmachine/workadventure-back:"+tag,
|
"image": "thecodingmachine/workadventure-back:"+tag,
|
||||||
@ -47,10 +44,7 @@
|
|||||||
"TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET,
|
"TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET,
|
||||||
} + (if adminUrl != null then {
|
} + (if adminUrl != null then {
|
||||||
"ADMIN_API_URL": adminUrl,
|
"ADMIN_API_URL": adminUrl,
|
||||||
} else {}) + if namespace != "master" then {
|
} else {})
|
||||||
// Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids!
|
|
||||||
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"pusher": {
|
"pusher": {
|
||||||
"replicas": 2,
|
"replicas": 2,
|
||||||
@ -69,10 +63,7 @@
|
|||||||
"SECRET_JITSI_KEY": env.SECRET_JITSI_KEY,
|
"SECRET_JITSI_KEY": env.SECRET_JITSI_KEY,
|
||||||
} + (if adminUrl != null then {
|
} + (if adminUrl != null then {
|
||||||
"ADMIN_API_URL": adminUrl,
|
"ADMIN_API_URL": adminUrl,
|
||||||
} else {}) + if namespace != "master" then {
|
} else {})
|
||||||
// Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids!
|
|
||||||
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"front": {
|
"front": {
|
||||||
"image": "thecodingmachine/workadventure-front:"+tag,
|
"image": "thecodingmachine/workadventure-front:"+tag,
|
||||||
|
@ -33,6 +33,8 @@ services:
|
|||||||
STARTUP_COMMAND_1: ./templater.sh
|
STARTUP_COMMAND_1: ./templater.sh
|
||||||
STARTUP_COMMAND_2: yarn install
|
STARTUP_COMMAND_2: yarn install
|
||||||
TURN_SERVER: "turn:localhost:3478,turns:localhost:5349"
|
TURN_SERVER: "turn:localhost:3478,turns:localhost:5349"
|
||||||
|
DISABLE_NOTIFICATIONS: "$DISABLE_NOTIFICATIONS"
|
||||||
|
SKIP_RENDER_OPTIMIZATIONS: "$SKIP_RENDER_OPTIMIZATIONS"
|
||||||
# Use TURN_USER/TURN_PASSWORD if your Coturn server is secured via hard coded credentials.
|
# Use TURN_USER/TURN_PASSWORD if your Coturn server is secured via hard coded credentials.
|
||||||
# Advice: you should instead use Coturn REST API along the TURN_STATIC_AUTH_SECRET in the Back container
|
# Advice: you should instead use Coturn REST API along the TURN_STATIC_AUTH_SECRET in the Back container
|
||||||
TURN_USER: ""
|
TURN_USER: ""
|
||||||
@ -153,23 +155,6 @@ services:
|
|||||||
- "traefik.http.routers.uploader-ssl.tls=true"
|
- "traefik.http.routers.uploader-ssl.tls=true"
|
||||||
- "traefik.http.routers.uploader-ssl.service=uploader"
|
- "traefik.http.routers.uploader-ssl.service=uploader"
|
||||||
|
|
||||||
website:
|
|
||||||
image: thecodingmachine/nodejs:12-apache
|
|
||||||
environment:
|
|
||||||
STARTUP_COMMAND_1: npm install
|
|
||||||
STARTUP_COMMAND_2: npm run watch &
|
|
||||||
APACHE_DOCUMENT_ROOT: dist/
|
|
||||||
volumes:
|
|
||||||
- ./website:/var/www/html
|
|
||||||
labels:
|
|
||||||
- "traefik.http.routers.website.rule=Host(`workadventure.localhost`)"
|
|
||||||
- "traefik.http.routers.website.entryPoints=web"
|
|
||||||
- "traefik.http.services.website.loadbalancer.server.port=80"
|
|
||||||
- "traefik.http.routers.website-ssl.rule=Host(`workadventure.localhost`)"
|
|
||||||
- "traefik.http.routers.website-ssl.entryPoints=websecure"
|
|
||||||
- "traefik.http.routers.website-ssl.tls=true"
|
|
||||||
- "traefik.http.routers.website-ssl.service=website"
|
|
||||||
|
|
||||||
messages:
|
messages:
|
||||||
#image: thecodingmachine/nodejs:14
|
#image: thecodingmachine/nodejs:14
|
||||||
image: thecodingmachine/workadventure-back-base:latest
|
image: thecodingmachine/workadventure-back-base:latest
|
||||||
|
@ -33,6 +33,8 @@ services:
|
|||||||
STARTUP_COMMAND_2: yarn install
|
STARTUP_COMMAND_2: yarn install
|
||||||
STUN_SERVER: "stun:stun.l.google.com:19302"
|
STUN_SERVER: "stun:stun.l.google.com:19302"
|
||||||
TURN_SERVER: "turn:coturn.workadventure.localhost:3478,turns:coturn.workadventure.localhost:5349"
|
TURN_SERVER: "turn:coturn.workadventure.localhost:3478,turns:coturn.workadventure.localhost:5349"
|
||||||
|
DISABLE_NOTIFICATIONS: "$DISABLE_NOTIFICATIONS"
|
||||||
|
SKIP_RENDER_OPTIMIZATIONS: "$SKIP_RENDER_OPTIMIZATIONS"
|
||||||
# Use TURN_USER/TURN_PASSWORD if your Coturn server is secured via hard coded credentials.
|
# Use TURN_USER/TURN_PASSWORD if your Coturn server is secured via hard coded credentials.
|
||||||
# Advice: you should instead use Coturn REST API along the TURN_STATIC_AUTH_SECRET in the Back container
|
# Advice: you should instead use Coturn REST API along the TURN_STATIC_AUTH_SECRET in the Back container
|
||||||
TURN_USER: ""
|
TURN_USER: ""
|
||||||
|
237
docs/maps/api-reference.md
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
{.section-title.accent.text-primary}
|
||||||
|
# API Reference
|
||||||
|
|
||||||
|
### Sending a message in the chat
|
||||||
|
|
||||||
|
```
|
||||||
|
sendChatMessage(message: string, author: string): void
|
||||||
|
```
|
||||||
|
|
||||||
|
Sends a message in the chat. The message is only visible in the browser of the current user.
|
||||||
|
|
||||||
|
* **message**: the message to be displayed in the chat
|
||||||
|
* **author**: the name displayed for the author of the message. It does not have to be a real user.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
WA.sendChatMessage('Hello world', 'Mr Robot');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Listening to messages from the chat
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
onChatMessage(callback: (message: string) => void): void
|
||||||
|
```
|
||||||
|
|
||||||
|
Listens to messages typed by the current user and calls the callback. Messages from other users in the chat cannot be listened to.
|
||||||
|
|
||||||
|
* **callback**: the function that will be called when a message is received. It contains the message typed by the user.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
WA.onChatMessage((message => {
|
||||||
|
console.log('The user typed a message', message);
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detecting when the user enters/leaves a zone
|
||||||
|
|
||||||
|
```
|
||||||
|
onEnterZone(name: string, callback: () => void): void
|
||||||
|
onLeaveZone(name: string, callback: () => void): void
|
||||||
|
```
|
||||||
|
|
||||||
|
Listens to the position of the current user. The event is triggered when the user enters or leaves a given zone. The name of the zone is stored in the map, on a dedicated layer with the `zone` property.
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<figure class="figure">
|
||||||
|
<img src="https://workadventu.re/img/docs/trigger_event.png" class="figure-img img-fluid rounded" alt="" />
|
||||||
|
<figcaption class="figure-caption">The `zone` property, applied on a layer</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
* **name**: the name of the zone, as defined in the `zone` property.
|
||||||
|
* **callback**: the function that will be called when a user enters or leaves the zone.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
WA.onEnterZone('myZone', () => {
|
||||||
|
WA.sendChatMessage("Hello!", 'Mr Robot');
|
||||||
|
})
|
||||||
|
|
||||||
|
WA.onLeaveZone('myZone', () => {
|
||||||
|
WA.sendChatMessage("Goodbye!", 'Mr Robot');
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Opening a popup
|
||||||
|
|
||||||
|
In order to open a popup window, you must first define the position of the popup on your map.
|
||||||
|
|
||||||
|
You can position this popup by using a "rectangle" object in Tiled that you will place on an "object" layer.
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<img src="https://workadventu.re/img/docs/screen_popup_tiled.png" class="figure-img img-fluid rounded" alt="" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<img src="https://workadventu.re/img/docs/screen_popup_in_game.png" class="figure-img img-fluid rounded" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
```
|
||||||
|
openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup
|
||||||
|
```
|
||||||
|
|
||||||
|
* **targetObject**: the name of the rectangle object defined in Tiled.
|
||||||
|
* **message**: the message to display in the popup.
|
||||||
|
* **buttons**: an array of action buttons defined underneath the popup.
|
||||||
|
|
||||||
|
Action buttons are `ButtonDescriptor` objects containing these properties.
|
||||||
|
|
||||||
|
* **label (_string_)**: The label of the button.
|
||||||
|
* **className (_string_)**: The visual type of the button. Can be one of "normal", "primary", "success", "warning", "error", "disabled".
|
||||||
|
* **callback (_(popup: Popup)=>void_)**: Callback called when the button is pressed.
|
||||||
|
|
||||||
|
Please note that `openPopup` returns an object of the `Popup` class. Also, the callback called when a button is clicked is passed a `Popup` object.
|
||||||
|
|
||||||
|
The `Popup` class that represents an open popup contains a single method: `close()`. This will obviously close the popup when called.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
class Popup {
|
||||||
|
/**
|
||||||
|
* Closes the popup
|
||||||
|
*/
|
||||||
|
close() {};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
let helloWorldPopup;
|
||||||
|
|
||||||
|
// Open the popup when we enter a given zone
|
||||||
|
helloWorldPopup = WA.onEnterZone('myZone', () => {
|
||||||
|
WA.openPopup("popupRectangle", 'Hello world!', [{
|
||||||
|
label: "Close",
|
||||||
|
className: "primary",
|
||||||
|
callback: (popup) => {
|
||||||
|
// Close the popup when the "Close" button is pressed.
|
||||||
|
popup.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
|
||||||
|
// Close the popup when we leave the zone.
|
||||||
|
WA.onLeaveZone('myZone', () => {
|
||||||
|
helloWorldPopup.close();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disabling / restoring controls
|
||||||
|
|
||||||
|
```
|
||||||
|
disablePlayerControls(): void
|
||||||
|
restorePlayerControls(): void
|
||||||
|
```
|
||||||
|
|
||||||
|
These 2 methods can be used to completely disable player controls and to enable them again.
|
||||||
|
|
||||||
|
When controls are disabled, the user cannot move anymore using keyboard input. This can be useful in a "First Time User Experience" part, to display an important message to a user before letting him/her move again.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
WA.onEnterZone('myZone', () => {
|
||||||
|
WA.disablePlayerControls();
|
||||||
|
WA.openPopup("popupRectangle", 'This is an imporant message!', [{
|
||||||
|
label: "Got it!",
|
||||||
|
className: "primary",
|
||||||
|
callback: (popup) => {
|
||||||
|
WA.restorePlayerControls();
|
||||||
|
popup.close();
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Opening a web page in a new tab
|
||||||
|
|
||||||
|
```
|
||||||
|
openTab(url: string): void
|
||||||
|
```
|
||||||
|
|
||||||
|
Opens the webpage at "url" in your browser, in a new tab.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
WA.openTab('https://www.wikipedia.org/');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Opening a web page in the current tab
|
||||||
|
|
||||||
|
```
|
||||||
|
goToPage(url: string): void
|
||||||
|
```
|
||||||
|
|
||||||
|
Opens the webpage at "url" in your browser in place of WorkAdventure. WorkAdventure will be completely unloaded.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
WA.goToPage('https://www.wikipedia.org/');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Opening/closing a web page in an iFrame
|
||||||
|
|
||||||
|
```
|
||||||
|
openCoWebSite(url: string): void
|
||||||
|
closeCoWebSite(): void
|
||||||
|
```
|
||||||
|
|
||||||
|
Opens the webpage at "url" in an iFrame (on the right side of the screen) or close that iFrame.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
WA.openCoWebSite('https://www.wikipedia.org/');
|
||||||
|
// ...
|
||||||
|
WA.closeCoWebSite();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Load a sound from an url
|
||||||
|
|
||||||
|
```
|
||||||
|
loadSound(url: string): Sound
|
||||||
|
```
|
||||||
|
|
||||||
|
Load a sound from an url
|
||||||
|
|
||||||
|
Please note that `loadSound` returns an object of the `Sound` class
|
||||||
|
|
||||||
|
The `Sound` class that represents a loaded sound contains two methods: `play(soundConfig : SoundConfig|undefined)` and `stop()`
|
||||||
|
|
||||||
|
The parameter soundConfig is optional, if you call play without a Sound config the sound will be played with the basic configuration.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var mySound = WA.loadSound("Sound.ogg");
|
||||||
|
var config = {
|
||||||
|
volume : 0.5,
|
||||||
|
loop : false,
|
||||||
|
rate : 1,
|
||||||
|
detune : 1,
|
||||||
|
delay : 0,
|
||||||
|
seek : 0,
|
||||||
|
mute : false
|
||||||
|
}
|
||||||
|
mySound.play(config);
|
||||||
|
// ...
|
||||||
|
mySound.stop();
|
||||||
|
```
|
117
docs/maps/scripting.md
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
{.alert.alert-danger style="width:80%"}
|
||||||
|
This feature is "_experimental_". We may apply changes in the near future to the way it works when we gather some feedback.
|
||||||
|
|
||||||
|
{.section-title.accent.text-primary}
|
||||||
|
# Scripting WorkAdventure maps
|
||||||
|
|
||||||
|
Do you want to add a bit of intelligence to your map? Scripts allow you to create maps with special features.
|
||||||
|
|
||||||
|
You can for instance:
|
||||||
|
|
||||||
|
* Create FTUE (First Time User Experience) scenarios where a first-time user will be displayed a notification popup.
|
||||||
|
* Create NPC (non playing characters) and interact with those characters using the chat.
|
||||||
|
* Organize interactions between an iframe and your map (for instance, walking on a special zone might add a product in the cart of an eCommerce website...)
|
||||||
|
* etc...
|
||||||
|
|
||||||
|
Please note that scripting in WorkAdventure is at an early stage of development and that more features might be added in the future. You can actually voice your opinion about useful features by adding [an issue on Github](https://github.com/thecodingmachine/workadventure/issues).
|
||||||
|
|
||||||
|
{.alert.alert-warning}
|
||||||
|
**Beware:** Scripts are executed in the browser of the current user only. Generally speaking, scripts cannot be used to trigger a change that will be displayed on other users screen.
|
||||||
|
|
||||||
|
## Scripting language
|
||||||
|
|
||||||
|
Client-side scripting is done in **Javascript** (or any language that transpiles to Javascript like _Typescript_).
|
||||||
|
|
||||||
|
There are 2 ways you can use the scripting language:
|
||||||
|
|
||||||
|
* **In the map**: By directly referring a Javascript file inside your map, in the `script` property of your map.
|
||||||
|
* **In an iFrame**: By placing your Javascript script into an iFrame, your script can communicate with the WorkAdventure game
|
||||||
|
|
||||||
|
## Adding a script in the map
|
||||||
|
|
||||||
|
Create a `script` property in your map.
|
||||||
|
|
||||||
|
In Tiled, in order to access your map properties, you can click on _"Map > Map properties"_.
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<figure class="figure">
|
||||||
|
<img src="https://workadventu.re/img/docs/admin/map_properties.png" class="figure-img img-fluid rounded" alt="" />
|
||||||
|
<figcaption class="figure-caption">The Map properties menu</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Create a `script` property (a "string"), and put the URL of your script.
|
||||||
|
|
||||||
|
You can put relative URLs. If your script file is next to your map, you can simply write the name of the script file here.
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<figure class="figure">
|
||||||
|
<img src="https://workadventu.re/img/docs/script_property.png" class="figure-img img-fluid rounded" alt="" />
|
||||||
|
<figcaption class="figure-caption">The script property</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Start by testing this with a simple message sent to the chat.
|
||||||
|
|
||||||
|
**script.js**
|
||||||
|
```javascript
|
||||||
|
WA.sendChatMessage('Hello world', 'Mr Robot');
|
||||||
|
```
|
||||||
|
|
||||||
|
The `WA` objects contains a number of useful methods enabling you to interact with the WorkAdventure game. For instance, `WA.sendChatMessage` opens the chat and adds a message in it.
|
||||||
|
|
||||||
|
In your browser console, when you open the map, the chat message should be displayed right away.
|
||||||
|
|
||||||
|
## Adding a script in an iFrame
|
||||||
|
|
||||||
|
In WorkAdventure, you can easily [open an iFrame using the `openWebsite` property on a layer](special-zones). However, by default, the iFrame is not allowed to communicate with WorkAdventure.
|
||||||
|
|
||||||
|
This is done to improve security. In order to be able to execute a script that communicates with WorkAdventure inside an iFrame, you have to **explicitly allow the iFrame to use the "iFrame API"**.
|
||||||
|
|
||||||
|
In order to allow communication with WorkAdventure, you need to add an additional property: `openWebsiteAllowApi`. This property must be _boolean_ and you must set it to "true".
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<figure class="figure">
|
||||||
|
<img src="https://workadventu.re/img/docs/open_website_allow_api.png" class="figure-img img-fluid rounded" alt="" />
|
||||||
|
<figcaption class="figure-caption">The `openWebsiteAllowApi` property</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
In your iFrame HTML page, you now need to import the _WorkAdventure client API Javascript library_. This library contains the `WA` object that you can use to communicate with WorkAdventure.
|
||||||
|
|
||||||
|
The library is available at `https://play.workadventu.re/iframe_api.js`.
|
||||||
|
|
||||||
|
_Note:_ if you are using a self-hosted version of WorkAdventure, use `https://[front_domain]/iframe_api.js`
|
||||||
|
|
||||||
|
**iframe.html**
|
||||||
|
```html
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<script src="https://play.workadventu.re/iframe_api.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now start by testing this with a simple message sent to the chat.
|
||||||
|
|
||||||
|
**iframe.html**
|
||||||
|
```html
|
||||||
|
...
|
||||||
|
<script>
|
||||||
|
WA.sendChatMessage('Hello world', 'Mr Robot');
|
||||||
|
</script>
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's now review the complete list of methods available in this `WA` object.
|
||||||
|
|
||||||
|
## Using Typescript
|
||||||
|
|
||||||
|
View the dedicated page about [using Typescript with the scripting API](using-typescript).
|
||||||
|
|
||||||
|
## Available features in the client API
|
||||||
|
|
||||||
|
The list of available functions and features is [available in the API Reference page, with examples](api-reference).
|
@ -25,6 +25,15 @@
|
|||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-unused-vars": "off",
|
"no-unused-vars": "off",
|
||||||
"@typescript-eslint/no-explicit-any": "error"
|
"@typescript-eslint/no-explicit-any": "error",
|
||||||
|
|
||||||
|
// TODO: remove those ignored rules and write a stronger code!
|
||||||
|
"@typescript-eslint/no-floating-promises": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-call": "off",
|
||||||
|
"@typescript-eslint/restrict-plus-operands": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-return": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-member-access": "off",
|
||||||
|
"@typescript-eslint/restrict-template-expressions": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
front/dist/index.tmpl.html
vendored
@ -1,9 +1,9 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport"
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
|
||||||
<!-- TRACK CODE -->
|
<!-- TRACK CODE -->
|
||||||
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
<title>Partey</title>
|
<title>Partey</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body id="body" style="margin: 0; background-color: #000">
|
<body id="body" style="margin: 0; background-color: #000">
|
||||||
<div class="main-container" id="main-container">
|
<div class="main-container" id="main-container">
|
||||||
<!-- Create the editor container -->
|
<!-- Create the editor container -->
|
||||||
@ -46,6 +47,13 @@
|
|||||||
<div id="activeCam" class="activeCam">
|
<div id="activeCam" class="activeCam">
|
||||||
<div id="div-myCamVideo" class="video-container nes-container is-rounded is-dark">
|
<div id="div-myCamVideo" class="video-container nes-container is-rounded is-dark">
|
||||||
<video id="myCamVideo" autoplay muted></video>
|
<video id="myCamVideo" autoplay muted></video>
|
||||||
|
<div id="mySoundMeter" class="sound-progress">
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-cam-action">
|
<div class="btn-cam-action">
|
||||||
@ -136,4 +144,5 @@
|
|||||||
</audio>
|
</audio>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 44vh;
|
top: 40vh;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
#enableCameraScene button#enableCameraSceneFormCancel {
|
#enableCameraScene button#enableCameraSceneFormCancel {
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
<style>
|
<style>
|
||||||
#helpCameraSettings {
|
#helpCameraSettings {
|
||||||
background: #eceeee;
|
background: #eceeee;
|
||||||
border: 1px solid #42464b;
|
|
||||||
border-radius: 6px;
|
|
||||||
margin: 25px auto 0;
|
margin: 25px auto 0;
|
||||||
width: 400px;
|
width: 400px;
|
||||||
max-height: calc(48vh - 50px);
|
max-height: calc(48vh - 150px);
|
||||||
max-width: 48vw;
|
max-width: 48vw;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
@ -45,6 +43,9 @@
|
|||||||
font-size: 7px;
|
font-size: 7px;
|
||||||
margin: 0px 20px;
|
margin: 0px 20px;
|
||||||
}
|
}
|
||||||
|
#helpCameraSettings section p a{
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
#helpCameraSettings section p.err{
|
#helpCameraSettings section p.err{
|
||||||
color: #ff0000;
|
color: #ff0000;
|
||||||
}
|
}
|
||||||
@ -67,7 +68,7 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<form id="helpCameraSettings" hidden>
|
<form id="helpCameraSettings" class="nes-container" hidden>
|
||||||
<section class="text-center">
|
<section class="text-center">
|
||||||
<h5>Camera/Microphone access needed</h5>
|
<h5>Camera/Microphone access needed</h5>
|
||||||
<p class="err" id="permissionError">Permission denied</p>
|
<p class="err" id="permissionError">Permission denied</p>
|
||||||
@ -80,6 +81,10 @@
|
|||||||
<p>If you prefer to continue without allowing camera and microphone access, click on Continue</p>
|
<p>If you prefer to continue without allowing camera and microphone access, click on Continue</p>
|
||||||
<p id='browserHelpSetting'></p>
|
<p id='browserHelpSetting'></p>
|
||||||
</section>
|
</section>
|
||||||
|
<!--<section class="text-center">
|
||||||
|
<p>If your problem persist, please contact us: <a id="mailto" href="mailto:workadventure@thecodingmachine.com?subject=Support camera and microphone settings" target="_blank"> workadventure@thecodingmachine.com</a>.</p>
|
||||||
|
</section>-->
|
||||||
|
</section>
|
||||||
<section class="action">
|
<section class="action">
|
||||||
<a href="#" class="nes-btn" id="helpCameraSettingsFormRefresh">Refresh</a>
|
<a href="#" class="nes-btn" id="helpCameraSettingsFormRefresh">Refresh</a>
|
||||||
<button type="submit" class="nes-btn is-warning" id="helpCameraSettingsFormContinue">Continue</button>
|
<button type="submit" class="nes-btn is-warning" id="helpCameraSettingsFormContinue">Continue</button>
|
||||||
|
BIN
front/dist/resources/logos/logo-WA-min.png
vendored
Normal file
After Width: | Height: | Size: 2.1 KiB |
18
front/dist/resources/style/mobile-style.scss
vendored
@ -23,27 +23,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-cam-action {
|
.btn-cam-action {
|
||||||
|
min-width: 150px;
|
||||||
|
|
||||||
&:hover{
|
&:hover{
|
||||||
transform: translateY(20px);
|
transform: translateY(20px);
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
|
margin: 0 1%;
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #666;
|
background-color: #666;
|
||||||
}
|
}
|
||||||
|
margin-bottom: 30px;
|
||||||
bottom: 30px;
|
|
||||||
|
|
||||||
&.btn-micro {
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.btn-monitor {
|
|
||||||
right: 130px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.btn-video {
|
|
||||||
right: 65px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
120
front/dist/resources/style/style.css
vendored
@ -107,7 +107,7 @@ body .message-info.warning {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.video-container button.report:hover {
|
.video-container button.report:hover {
|
||||||
width: 150px;
|
width: 160px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-container button.report img {
|
.video-container button.report img {
|
||||||
@ -137,6 +137,7 @@ body .message-info.warning {
|
|||||||
|
|
||||||
.video-container video {
|
.video-container video {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-container video:focus {
|
.video-container video:focus {
|
||||||
@ -151,7 +152,7 @@ body .message-info.warning {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
right: 15px;
|
right: 15px;
|
||||||
bottom: 30px;
|
bottom: 30px;
|
||||||
max-height: 200px;
|
max-height: 20%;
|
||||||
}
|
}
|
||||||
|
|
||||||
video#myCamVideo {
|
video#myCamVideo {
|
||||||
@ -162,13 +163,63 @@ video#myCamVideo {
|
|||||||
/*height: 113px;*/
|
/*height: 113px;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sound-progress {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
right: 14px;
|
||||||
|
top: calc(50% - 5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sound-progress.active {
|
||||||
|
display: table-column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sound-progress span {
|
||||||
|
position: absolute;
|
||||||
|
color: black;
|
||||||
|
background-color: #00000020;
|
||||||
|
width: 5px;
|
||||||
|
height: 5px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sound-progress span.active {
|
||||||
|
background-color: #00c3ff66
|
||||||
|
}
|
||||||
|
|
||||||
|
.sound-progress span:nth-child(1) {
|
||||||
|
top: calc(50% + 20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sound-progress span:nth-child(2) {
|
||||||
|
top: calc(50% + 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sound-progress span:nth-child(3) {
|
||||||
|
top: calc(50% - 0px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sound-progress span:nth-child(4) {
|
||||||
|
top: calc(50% - 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sound-progress span:nth-child(5) {
|
||||||
|
top: calc(50% - 20px);
|
||||||
|
}
|
||||||
|
|
||||||
.btn-cam-action {
|
.btn-cam-action {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0px;
|
display: inline-flex;
|
||||||
right: 0px;
|
bottom: 10px;
|
||||||
width: 300px;
|
right: 15px;
|
||||||
height: 50px;
|
width: 15vw;
|
||||||
|
height: 40px;
|
||||||
|
text-align: center;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
justify-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -176,13 +227,14 @@ video#myCamVideo {
|
|||||||
|
|
||||||
.btn-cam-action div {
|
.btn-cam-action div {
|
||||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||||
position: absolute;
|
/*position: absolute;*/
|
||||||
|
width: 44px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
width: auto;
|
width: auto;
|
||||||
transform: translateY(20px);
|
transform: translateY(20px);
|
||||||
transition-timing-function: ease-in-out;
|
transition-timing-function: ease-in-out;
|
||||||
bottom: 20px;
|
margin-bottom: 20px;
|
||||||
pointer-events: none;
|
margin: 0 4%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-cam-action div.disabled {
|
.btn-cam-action div.disabled {
|
||||||
@ -204,21 +256,21 @@ video#myCamVideo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-micro {
|
.btn-micro {
|
||||||
pointer-events: auto;
|
pointer-events: none;
|
||||||
transition: all .3s;
|
transition: all .3s;
|
||||||
right: 224px;
|
/*right: 44px;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-video {
|
.btn-video {
|
||||||
pointer-events: auto;
|
pointer-events: none;
|
||||||
transition: all .25s;
|
transition: all .25s;
|
||||||
right: 134px;
|
/*right: 134px;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-monitor {
|
.btn-monitor {
|
||||||
pointer-events: auto;
|
pointer-events: none;
|
||||||
transition: all .2s;
|
transition: all .2s;
|
||||||
right: 44px;
|
/*right: 224px;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-copy {
|
.btn-copy {
|
||||||
@ -557,12 +609,20 @@ input[type=range]:focus::-ms-fill-upper {
|
|||||||
/* TODO: DO WE NEED FLEX HERE???? WE WANT A SIDEBAR OF EXACTLY 25% (note: flex useful for direction!!!) */
|
/* TODO: DO WE NEED FLEX HERE???? WE WANT A SIDEBAR OF EXACTLY 25% (note: flex useful for direction!!!) */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.game-overlay+div {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-overlay+div>div {
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.game-overlay.active {
|
.game-overlay.active {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.game-overlay video {
|
.game-overlay video {
|
||||||
width: 100%
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-section {
|
.main-section {
|
||||||
@ -578,6 +638,7 @@ input[type=range]:focus::-ms-fill-upper {
|
|||||||
flex-basis: 96%;
|
flex-basis: 96%;
|
||||||
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, flex-basis 0.2s;
|
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, flex-basis 0.2s;
|
||||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||||
|
pointer-events: auto;
|
||||||
/*flex-shrink: 2;*/
|
/*flex-shrink: 2;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -589,20 +650,23 @@ input[type=range]:focus::-ms-fill-upper {
|
|||||||
.sidebar {
|
.sidebar {
|
||||||
flex: 0 0 25%;
|
flex: 0 0 25%;
|
||||||
display: flex;
|
display: flex;
|
||||||
pointer-events: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar>div {
|
.sidebar>div {
|
||||||
margin: 2%;
|
margin: 2%;
|
||||||
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, max-height 0.2s, max-width 0.2s;
|
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, max-height 0.2s, max-width 0.2s;
|
||||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||||
padding: 12px 12px !important;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar>div:hover {
|
.sidebar>div:hover {
|
||||||
margin: 0%;
|
margin: 0%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar>div video {
|
||||||
|
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Let's make sure videos are vertically centered if they need to be cropped */
|
/* Let's make sure videos are vertically centered if they need to be cropped */
|
||||||
|
|
||||||
@ -1200,7 +1264,17 @@ div.action {
|
|||||||
animation-timing-function: ease-in-out;
|
animation-timing-function: ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.action.info,
|
||||||
|
div.action.warning,
|
||||||
|
div.action.danger {
|
||||||
|
transition: all 1s ease;
|
||||||
|
animation: mymove 1s;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
div.action p.action-body {
|
div.action p.action-body {
|
||||||
|
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@ -1208,6 +1282,16 @@ div.action p.action-body {
|
|||||||
max-width: 350px;
|
max-width: 350px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.action.warning p.action-body {
|
||||||
|
background-color: #ff9800eb;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.action.danger p.action-body {
|
||||||
|
background-color: #da0000e3;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
.popUpElement {
|
.popUpElement {
|
||||||
font-family: 'Press Start 2P';
|
font-family: 'Press Start 2P';
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
124
front/dist/static/images/favicons/manifest.json
vendored
@ -1,41 +1,149 @@
|
|||||||
{
|
{
|
||||||
"name": "App",
|
"short_name": "WA",
|
||||||
|
"name": "WorkAdventure",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "\/android-icon-36x36.png",
|
"src": "/static/images/favicons/apple-icon-57x57.png",
|
||||||
|
"sizes": "57x57",
|
||||||
|
"type": "image\/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/apple-icon-60x60.png",
|
||||||
|
"sizes": "60x60",
|
||||||
|
"type": "image\/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/apple-icon-72x72.png",
|
||||||
|
"sizes": "72x72",
|
||||||
|
"type": "image\/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/apple-icon-76x76.png",
|
||||||
|
"sizes": "76x76",
|
||||||
|
"type": "image\/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/apple-icon-114x114.png",
|
||||||
|
"sizes": "114x114",
|
||||||
|
"type": "image\/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/apple-icon-120x120.png",
|
||||||
|
"sizes": "120x120",
|
||||||
|
"type": "image\/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/apple-icon-144x144.png",
|
||||||
|
"sizes": "144x144",
|
||||||
|
"type": "image\/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/apple-icon-152x152.png",
|
||||||
|
"sizes": "152x152",
|
||||||
|
"type": "image\/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/apple-icon-180x180.png",
|
||||||
|
"sizes": "180x180",
|
||||||
|
"type": "image\/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/android-icon-36x36.png",
|
||||||
"sizes": "36x36",
|
"sizes": "36x36",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "0.75"
|
"density": "0.75"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "\/android-icon-48x48.png",
|
"src": "/static/images/favicons/android-icon-48x48.png",
|
||||||
"sizes": "48x48",
|
"sizes": "48x48",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "1.0"
|
"density": "1.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "\/android-icon-72x72.png",
|
"src": "/static/images/favicons/android-icon-72x72.png",
|
||||||
|
"sizes": "72x72",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "1.5"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/favicon-16x16.png",
|
||||||
|
"sizes": "16x16",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/favicon-32x32.png",
|
||||||
|
"sizes": "32x32",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "1.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/favicon-96x96.png",
|
||||||
|
"sizes": "96x96",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "2.0"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/android-icon-36x36.png",
|
||||||
|
"sizes": "36x36",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/android-icon-48x48.png",
|
||||||
|
"sizes": "48x48",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/android-icon-72x72.png",
|
||||||
"sizes": "72x72",
|
"sizes": "72x72",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "1.5"
|
"density": "1.5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "\/android-icon-96x96.png",
|
"src": "/static/images/favicons/android-icon-96x96.png",
|
||||||
"sizes": "96x96",
|
"sizes": "96x96",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "2.0"
|
"density": "2.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "\/android-icon-144x144.png",
|
"src": "/static/images/favicons/android-icon-144x144.png",
|
||||||
"sizes": "144x144",
|
"sizes": "144x144",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "3.0"
|
"density": "3.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "\/android-icon-192x192.png",
|
"src": "/static/images/favicons/android-icon-192x192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "4.0"
|
"density": "4.0"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"start_url": "/",
|
||||||
|
"background_color": "#000000",
|
||||||
|
"display_override": ["window-control-overlay", "minimal-ui"],
|
||||||
|
"display": "standalone",
|
||||||
|
"scope": "/",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"shortcuts": [
|
||||||
|
{
|
||||||
|
"name": "WorkAdventures",
|
||||||
|
"short_name": "WA",
|
||||||
|
"description": "WorkAdventure application",
|
||||||
|
"url": "/",
|
||||||
|
"icons": [{ "src": "/static/images/favicons/android-icon-192x192.png", "sizes": "192x192" }]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "WorkAdventure application",
|
||||||
|
"screenshots": [],
|
||||||
|
"related_applications": [{
|
||||||
|
"platform": "web",
|
||||||
|
"url": "https://workadventu.re"
|
||||||
|
}, {
|
||||||
|
"platform": "play",
|
||||||
|
"url": "https://play.workadventu.re"
|
||||||
|
}]
|
||||||
}
|
}
|
@ -7,25 +7,26 @@
|
|||||||
"@types/google-protobuf": "^3.7.3",
|
"@types/google-protobuf": "^3.7.3",
|
||||||
"@types/jasmine": "^3.5.10",
|
"@types/jasmine": "^3.5.10",
|
||||||
"@types/quill": "^1.3.7",
|
"@types/quill": "^1.3.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.26.0",
|
"@types/webpack-dev-server": "^3.11.4",
|
||||||
"@typescript-eslint/parser": "^2.26.0",
|
"@typescript-eslint/eslint-plugin": "^4.23.0",
|
||||||
"css-loader": "^5.1.3",
|
"@typescript-eslint/parser": "^4.23.0",
|
||||||
"eslint": "^6.8.0",
|
"css-loader": "^5.2.4",
|
||||||
"html-webpack-plugin": "^4.3.0",
|
"eslint": "^7.26.0",
|
||||||
|
"html-webpack-plugin": "^5.3.1",
|
||||||
"jasmine": "^3.5.0",
|
"jasmine": "^3.5.0",
|
||||||
"mini-css-extract-plugin": "^1.3.9",
|
"mini-css-extract-plugin": "^1.6.0",
|
||||||
"sass": "^1.32.8",
|
"node-polyfill-webpack-plugin": "^1.1.2",
|
||||||
"sass-loader": "10.1.1",
|
"sass": "^1.32.12",
|
||||||
"ts-loader": "^6.2.2",
|
"sass-loader": "^11.1.0",
|
||||||
"ts-node": "^8.10.2",
|
"ts-loader": "^9.1.2",
|
||||||
"typescript": "^3.8.3",
|
"ts-node": "^9.1.1",
|
||||||
"webpack": "^4.42.1",
|
"typescript": "^4.2.4",
|
||||||
"webpack-cli": "^3.3.11",
|
"webpack": "^5.37.0",
|
||||||
"webpack-dev-server": "^3.10.3",
|
"webpack-cli": "^4.7.0",
|
||||||
"webpack-merge": "^4.2.2"
|
"webpack-dev-server": "^3.11.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/press-start-2p": "^4.2.2",
|
"@fontsource/press-start-2p": "^4.3.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",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
@ -39,12 +40,11 @@
|
|||||||
"quill": "1.3.6",
|
"quill": "1.3.6",
|
||||||
"rxjs": "^6.6.3",
|
"rxjs": "^6.6.3",
|
||||||
"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 serve --open",
|
||||||
"build": "webpack --config webpack.prod.js",
|
"build": "NODE_ENV=production webpack",
|
||||||
"test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json",
|
"test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json",
|
||||||
"lint": "node_modules/.bin/eslint src/ . --ext .ts",
|
"lint": "node_modules/.bin/eslint src/ . --ext .ts",
|
||||||
"fix": "node_modules/.bin/eslint --fix src/ . --ext .ts"
|
"fix": "node_modules/.bin/eslint --fix src/ . --ext .ts"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {HtmlUtils} from "../WebRtc/HtmlUtils";
|
import {HtmlUtils} from "../WebRtc/HtmlUtils";
|
||||||
import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
import type {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
||||||
import {RoomConnection} from "../Connexion/RoomConnection";
|
import type {RoomConnection} from "../Connexion/RoomConnection";
|
||||||
import {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels";
|
import type {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels";
|
||||||
import {AdminMessageEventTypes} from "../Connexion/AdminMessagesService";
|
import {AdminMessageEventTypes} from "../Connexion/AdminMessagesService";
|
||||||
|
|
||||||
export const CLASS_CONSOLE_MESSAGE = 'main-console';
|
export const CLASS_CONSOLE_MESSAGE = 'main-console';
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {HtmlUtils} from "./../WebRtc/HtmlUtils";
|
import {HtmlUtils} from "./../WebRtc/HtmlUtils";
|
||||||
import {AUDIO_TYPE, MESSAGE_TYPE} from "./ConsoleGlobalMessageManager";
|
import {AUDIO_TYPE, MESSAGE_TYPE} from "./ConsoleGlobalMessageManager";
|
||||||
import {PUSHER_URL, UPLOADER_URL} from "../Enum/EnvironmentVariable";
|
import {PUSHER_URL, UPLOADER_URL} from "../Enum/EnvironmentVariable";
|
||||||
import {RoomConnection} from "../Connexion/RoomConnection";
|
import type {RoomConnection} from "../Connexion/RoomConnection";
|
||||||
import {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels";
|
import type {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels";
|
||||||
|
|
||||||
export class GlobalMessageManager {
|
export class GlobalMessageManager {
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {TypeMessageInterface} from "./UserMessageManager";
|
import type {TypeMessageInterface} from "./UserMessageManager";
|
||||||
import {HtmlUtils} from "../WebRtc/HtmlUtils";
|
import {HtmlUtils} from "../WebRtc/HtmlUtils";
|
||||||
|
|
||||||
let modalTimeOut : NodeJS.Timeout;
|
let modalTimeOut : NodeJS.Timeout;
|
||||||
|
@ -1,7 +1,58 @@
|
|||||||
export interface IframeEvent {
|
|
||||||
type: string;
|
|
||||||
data: unknown;
|
import type { ButtonClickedEvent } from './ButtonClickedEvent';
|
||||||
|
import type { ChatEvent } from './ChatEvent';
|
||||||
|
import type { ClosePopupEvent } from './ClosePopupEvent';
|
||||||
|
import type { EnterLeaveEvent } from './EnterLeaveEvent';
|
||||||
|
import type { GoToPageEvent } from './GoToPageEvent';
|
||||||
|
import type { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent';
|
||||||
|
import type { OpenPopupEvent } from './OpenPopupEvent';
|
||||||
|
import type { OpenTabEvent } from './OpenTabEvent';
|
||||||
|
import type { UserInputChatEvent } from './UserInputChatEvent';
|
||||||
|
import type { ExitUrlEvent } from './ExitUrlEvent';
|
||||||
|
|
||||||
|
|
||||||
|
export interface TypedMessageEvent<T> extends MessageEvent {
|
||||||
|
data: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IframeEventMap = {
|
||||||
|
//getState: GameStateEvent,
|
||||||
|
// updateTile: UpdateTileEvent
|
||||||
|
chat: ChatEvent,
|
||||||
|
openPopup: OpenPopupEvent
|
||||||
|
closePopup: ClosePopupEvent
|
||||||
|
openTab: OpenTabEvent
|
||||||
|
goToPage: GoToPageEvent
|
||||||
|
openCoWebSite: OpenCoWebSiteEvent
|
||||||
|
closeCoWebSite: null
|
||||||
|
disablePlayerControls: null
|
||||||
|
restorePlayerControls: null
|
||||||
|
displayBubble: null
|
||||||
|
removeBubble: null
|
||||||
|
exitUrl : ExitUrlEvent
|
||||||
|
closeChatMessage : null
|
||||||
|
}
|
||||||
|
export interface IframeEvent<T extends keyof IframeEventMap> {
|
||||||
|
type: T;
|
||||||
|
data: IframeEventMap[T];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export const isIframeEventWrapper = (event: any): event is IframeEvent<keyof IframeEventMap> => typeof event.type === 'string';
|
||||||
|
|
||||||
|
export interface IframeResponseEventMap {
|
||||||
|
userInputChat: UserInputChatEvent
|
||||||
|
enterEvent: EnterLeaveEvent
|
||||||
|
leaveEvent: EnterLeaveEvent
|
||||||
|
buttonClickedEvent: ButtonClickedEvent
|
||||||
|
// gameState: GameStateEvent
|
||||||
|
}
|
||||||
|
export interface IframeResponseEvent<T extends keyof IframeResponseEventMap> {
|
||||||
|
type: T;
|
||||||
|
data: IframeResponseEventMap[T];
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export const isIframeEventWrapper = (event: any): event is IframeEvent => typeof event.type === 'string';
|
export const isIframeResponseEventWrapper = (event: { type?: string }): event is IframeResponseEvent<keyof IframeResponseEventMap> => typeof event.type === 'string';
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
import { Subject } from "rxjs";
|
import { Subject } from "rxjs";
|
||||||
import { ChatEvent, isChatEvent } from "./Events/ChatEvent";
|
import { ChatEvent, isChatEvent } from "./Events/ChatEvent";
|
||||||
import {IframeEvent, isIframeEventWrapper} from "./Events/IframeEvent";
|
|
||||||
import {UserInputChatEvent} from "./Events/UserInputChatEvent";
|
|
||||||
import * as crypto from "crypto";
|
|
||||||
import { HtmlUtils } from "../WebRtc/HtmlUtils";
|
import { HtmlUtils } from "../WebRtc/HtmlUtils";
|
||||||
import {EnterLeaveEvent} from "./Events/EnterLeaveEvent";
|
import type { EnterLeaveEvent } from "./Events/EnterLeaveEvent";
|
||||||
import { isOpenPopupEvent, OpenPopupEvent } from "./Events/OpenPopupEvent";
|
import { isOpenPopupEvent, OpenPopupEvent } from "./Events/OpenPopupEvent";
|
||||||
import { isOpenTabEvent, OpenTabEvent } from "./Events/OpenTabEvent";
|
import { isOpenTabEvent, OpenTabEvent } from "./Events/OpenTabEvent";
|
||||||
import {ButtonClickedEvent} from "./Events/ButtonClickedEvent";
|
import type { ButtonClickedEvent } from "./Events/ButtonClickedEvent";
|
||||||
import { ClosePopupEvent, isClosePopupEvent } from "./Events/ClosePopupEvent";
|
import { ClosePopupEvent, isClosePopupEvent } from "./Events/ClosePopupEvent";
|
||||||
import { scriptUtils } from "./ScriptUtils";
|
import { scriptUtils } from "./ScriptUtils";
|
||||||
import { GoToPageEvent, isGoToPageEvent } from "./Events/GoToPageEvent";
|
import { GoToPageEvent, isGoToPageEvent } from "./Events/GoToPageEvent";
|
||||||
import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent";
|
import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent";
|
||||||
|
import { IframeEventMap, IframeEvent, IframeResponseEvent, IframeResponseEventMap, isIframeEventWrapper, TypedMessageEvent } from "./Events/IframeEvent";
|
||||||
|
import type { UserInputChatEvent } from "./Events/UserInputChatEvent";
|
||||||
import { isExitUrlEvent } from './Events/ExitUrlEvent';
|
import { isExitUrlEvent } from './Events/ExitUrlEvent';
|
||||||
|
|
||||||
|
|
||||||
@ -63,18 +62,18 @@ class IframeListener {
|
|||||||
private readonly scripts = new Map<string, HTMLIFrameElement>();
|
private readonly scripts = new Map<string, HTMLIFrameElement>();
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
window.addEventListener("message", (message) => {
|
window.addEventListener("message", (message: TypedMessageEvent<IframeEvent<keyof IframeEventMap>>) => {
|
||||||
// Do we trust the sender of this message?
|
// Do we trust the sender of this message?
|
||||||
// Let's only accept messages from the iframe that are allowed.
|
// Let's only accept messages from the iframe that are allowed.
|
||||||
// Note: maybe we could restrict on the domain too for additional security (in case the iframe goes to another domain).
|
// Note: maybe we could restrict on the domain too for additional security (in case the iframe goes to another domain).
|
||||||
let found = false;
|
let foundSrc: string | null = null;
|
||||||
for (const iframe of this.iframes) {
|
for (const iframe of this.iframes) {
|
||||||
if (iframe.contentWindow === message.source) {
|
if (iframe.contentWindow === message.source) {
|
||||||
found = true;
|
foundSrc = iframe.src;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!foundSrc) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +93,11 @@ class IframeListener {
|
|||||||
scriptUtils.goToPage(payload.data.url);
|
scriptUtils.goToPage(payload.data.url);
|
||||||
}
|
}
|
||||||
else if (payload.type === 'openCoWebSite' && isOpenCoWebsite(payload.data)) {
|
else if (payload.type === 'openCoWebSite' && isOpenCoWebsite(payload.data)) {
|
||||||
scriptUtils.openCoWebsite(payload.data.url);
|
const scriptUrl = [...this.scripts.keys()].find(key => {
|
||||||
|
return this.scripts.get(key)?.contentWindow == message.source
|
||||||
|
})
|
||||||
|
|
||||||
|
scriptUtils.openCoWebsite(payload.data.url, scriptUrl || foundSrc);
|
||||||
}
|
}
|
||||||
else if (payload.type === 'closeCoWebSite') {
|
else if (payload.type === 'closeCoWebSite') {
|
||||||
scriptUtils.closeCoWebSite();
|
scriptUtils.closeCoWebSite();
|
||||||
@ -102,10 +105,10 @@ class IframeListener {
|
|||||||
else if(payload.type === 'closeChatMessage') {
|
else if(payload.type === 'closeChatMessage') {
|
||||||
scriptUtils.closeChatMessage();
|
scriptUtils.closeChatMessage();
|
||||||
}
|
}
|
||||||
else if (payload.type === 'disablePlayerControl'){
|
else if (payload.type === 'disablePlayerControls') {
|
||||||
this._disablePlayerControlStream.next();
|
this._disablePlayerControlStream.next();
|
||||||
}
|
}
|
||||||
else if (payload.type === 'restorePlayerControl'){
|
else if (payload.type === 'restorePlayerControls') {
|
||||||
this._enablePlayerControlStream.next();
|
this._enablePlayerControlStream.next();
|
||||||
}
|
}
|
||||||
else if (payload.type === 'displayBubble') {
|
else if (payload.type === 'displayBubble') {
|
||||||
@ -187,7 +190,7 @@ class IframeListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getIFrameId(scriptUrl: string): string {
|
private getIFrameId(scriptUrl: string): string {
|
||||||
return 'script'+crypto.createHash('md5').update(scriptUrl).digest("hex");
|
return 'script' + btoa(scriptUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
unregisterScript(scriptUrl: string): void {
|
unregisterScript(scriptUrl: string): void {
|
||||||
@ -244,7 +247,7 @@ class IframeListener {
|
|||||||
/**
|
/**
|
||||||
* Sends the message... to all allowed iframes.
|
* Sends the message... to all allowed iframes.
|
||||||
*/
|
*/
|
||||||
private postMessage(message: IframeEvent) {
|
private postMessage(message: IframeResponseEvent<keyof IframeResponseEventMap>) {
|
||||||
for (const iframe of this.iframes) {
|
for (const iframe of this.iframes) {
|
||||||
iframe.contentWindow?.postMessage(message, '*');
|
iframe.contentWindow?.postMessage(message, '*');
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {coWebsiteManager} from "../WebRtc/CoWebsiteManager";
|
import {coWebsiteManager} from "../WebRtc/CoWebsiteManager";
|
||||||
import {discussionManager} from "../WebRtc/DiscussionManager";
|
// import {discussionManager} from "../WebRtc/DiscussionManager";
|
||||||
|
|
||||||
|
|
||||||
class ScriptUtils {
|
class ScriptUtils {
|
||||||
@ -13,16 +13,16 @@ class ScriptUtils {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public openCoWebsite(url: string, base: string) {
|
||||||
|
coWebsiteManager.loadCoWebsite(url, base);
|
||||||
|
}
|
||||||
|
|
||||||
public closeCoWebSite(){
|
public closeCoWebSite(){
|
||||||
coWebsiteManager.closeCoWebsite();
|
coWebsiteManager.closeCoWebsite();
|
||||||
}
|
}
|
||||||
|
|
||||||
public openCoWebsite(url : string){
|
|
||||||
coWebsiteManager.loadCoWebsite(url,url);
|
|
||||||
}
|
|
||||||
|
|
||||||
public closeChatMessage(){
|
public closeChatMessage(){
|
||||||
discussionManager.hideDiscussion();
|
// discussionManager.hideDiscussion();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {Subject} from "rxjs";
|
import {Subject} from "rxjs";
|
||||||
import {BanUserMessage, SendUserMessage} from "../Messages/generated/messages_pb";
|
import type {BanUserMessage, SendUserMessage} from "../Messages/generated/messages_pb";
|
||||||
|
|
||||||
export enum AdminMessageEventTypes {
|
export enum AdminMessageEventTypes {
|
||||||
admin = 'message',
|
admin = 'message',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
import {PUSHER_URL, START_ROOM_URL} from "../Enum/EnvironmentVariable";
|
import {PUSHER_URL, START_ROOM_URL} from "../Enum/EnvironmentVariable";
|
||||||
import {RoomConnection} from "./RoomConnection";
|
import {RoomConnection} from "./RoomConnection";
|
||||||
import {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels";
|
import type {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels";
|
||||||
import {GameConnexionTypes, urlManager} from "../Url/UrlManager";
|
import {GameConnexionTypes, urlManager} from "../Url/UrlManager";
|
||||||
import {localUserStore} from "./LocalUserStore";
|
import {localUserStore} from "./LocalUserStore";
|
||||||
import {LocalUser} from "./LocalUser";
|
import {LocalUser} from "./LocalUser";
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {PlayerAnimationDirections} from "../Phaser/Player/Animation";
|
import {PlayerAnimationDirections} from "../Phaser/Player/Animation";
|
||||||
import {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
|
import {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
|
||||||
import {SignalData} from "simple-peer";
|
import type {SignalData} from "simple-peer";
|
||||||
import {RoomConnection} from "./RoomConnection";
|
import type {RoomConnection} from "./RoomConnection";
|
||||||
import {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures";
|
import type {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures";
|
||||||
|
|
||||||
export enum EventMessage{
|
export enum EventMessage{
|
||||||
CONNECT = "connect",
|
CONNECT = "connect",
|
||||||
|
@ -10,7 +10,7 @@ export interface CharacterTexture {
|
|||||||
export const maxUserNameLength: number = MAX_USERNAME_LENGTH;
|
export const maxUserNameLength: number = MAX_USERNAME_LENGTH;
|
||||||
|
|
||||||
export function isUserNameValid(value: string): boolean {
|
export function isUserNameValid(value: string): boolean {
|
||||||
const regexp = new RegExp('^[A-Za-z]{1,'+maxUserNameLength+'}$');
|
const regexp = new RegExp('^[A-Za-z0-9]{1,'+maxUserNameLength+'}$');
|
||||||
return regexp.test(value);
|
return regexp.test(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ import {
|
|||||||
BanUserMessage
|
BanUserMessage
|
||||||
} from "../Messages/generated/messages_pb"
|
} from "../Messages/generated/messages_pb"
|
||||||
|
|
||||||
import {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
|
import type {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
|
||||||
import Direction = PositionMessage.Direction;
|
import Direction = PositionMessage.Direction;
|
||||||
import {ProtobufClientUtils} from "../Network/ProtobufClientUtils";
|
import {ProtobufClientUtils} from "../Network/ProtobufClientUtils";
|
||||||
import {
|
import {
|
||||||
@ -42,7 +42,7 @@ import {
|
|||||||
ViewportInterface, WebRtcDisconnectMessageInterface,
|
ViewportInterface, WebRtcDisconnectMessageInterface,
|
||||||
WebRtcSignalReceivedMessageInterface,
|
WebRtcSignalReceivedMessageInterface,
|
||||||
} from "./ConnexionModels";
|
} from "./ConnexionModels";
|
||||||
import {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures";
|
import type {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures";
|
||||||
import {adminMessagesService} from "./AdminMessagesService";
|
import {adminMessagesService} from "./AdminMessagesService";
|
||||||
import {worldFullMessageStream} from "./WorldFullMessageStream";
|
import {worldFullMessageStream} from "./WorldFullMessageStream";
|
||||||
import {worldFullWarningStream} from "./WorldFullWarningStream";
|
import {worldFullWarningStream} from "./WorldFullWarningStream";
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true";
|
const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true";
|
||||||
const START_ROOM_URL : string = process.env.START_ROOM_URL || '/_/global/maps.workadventure.localhost/Floor0/floor0.json';
|
const START_ROOM_URL : string = process.env.START_ROOM_URL || '/_/global/maps.workadventure.localhost/Floor0/floor0.json';
|
||||||
// For compatibility reasons with older versions, API_URL is the old host name of PUSHER_URL
|
const PUSHER_URL = process.env.PUSHER_URL || '//pusher.workadventure.localhost';
|
||||||
const PUSHER_URL = process.env.PUSHER_URL || (process.env.API_URL ? '//'+process.env.API_URL : "//pusher.workadventure.localhost");
|
|
||||||
const UPLOADER_URL = process.env.UPLOADER_URL || '//uploader.workadventure.localhost';
|
const UPLOADER_URL = process.env.UPLOADER_URL || '//uploader.workadventure.localhost';
|
||||||
const ADMIN_URL = process.env.ADMIN_URL || "//workadventure.localhost";
|
|
||||||
const STUN_SERVER: string = process.env.STUN_SERVER || "stun:stun.l.google.com:19302";
|
const STUN_SERVER: string = process.env.STUN_SERVER || "stun:stun.l.google.com:19302";
|
||||||
const TURN_SERVER: string = process.env.TURN_SERVER || "";
|
const TURN_SERVER: string = process.env.TURN_SERVER || "";
|
||||||
|
const SKIP_RENDER_OPTIMIZATIONS: boolean = process.env.SKIP_RENDER_OPTIMIZATIONS == "true";
|
||||||
|
const DISABLE_NOTIFICATIONS: boolean = process.env.DISABLE_NOTIFICATIONS == "true";
|
||||||
const TURN_USER: string = process.env.TURN_USER || '';
|
const TURN_USER: string = process.env.TURN_USER || '';
|
||||||
const TURN_PASSWORD: string = process.env.TURN_PASSWORD || '';
|
const TURN_PASSWORD: string = process.env.TURN_PASSWORD || '';
|
||||||
const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL;
|
const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL;
|
||||||
const JITSI_PRIVATE_MODE : boolean = process.env.JITSI_PRIVATE_MODE == "true";
|
const JITSI_PRIVATE_MODE : boolean = process.env.JITSI_PRIVATE_MODE == "true";
|
||||||
const RESOLUTION = 2;
|
|
||||||
const ZOOM_LEVEL = 1/*3/4*/;
|
|
||||||
const POSITION_DELAY = 200; // Wait 200ms between sending position events
|
const POSITION_DELAY = 200; // Wait 200ms between sending position events
|
||||||
const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new movement is sent by the player
|
const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new movement is sent by the player
|
||||||
export const MAX_USERNAME_LENGTH = parseInt(process.env.MAX_USERNAME_LENGTH || '') || 8;
|
export const MAX_USERNAME_LENGTH = parseInt(process.env.MAX_USERNAME_LENGTH || '') || 8;
|
||||||
@ -22,11 +20,10 @@ export const isMobile = ():boolean => ( ( window.innerWidth <= 800 ) || ( window
|
|||||||
export {
|
export {
|
||||||
DEBUG_MODE,
|
DEBUG_MODE,
|
||||||
START_ROOM_URL,
|
START_ROOM_URL,
|
||||||
|
SKIP_RENDER_OPTIMIZATIONS,
|
||||||
|
DISABLE_NOTIFICATIONS,
|
||||||
PUSHER_URL,
|
PUSHER_URL,
|
||||||
UPLOADER_URL,
|
UPLOADER_URL,
|
||||||
ADMIN_URL,
|
|
||||||
RESOLUTION,
|
|
||||||
ZOOM_LEVEL,
|
|
||||||
POSITION_DELAY,
|
POSITION_DELAY,
|
||||||
MAX_EXTRAPOLATION_TIME,
|
MAX_EXTRAPOLATION_TIME,
|
||||||
STUN_SERVER,
|
STUN_SERVER,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {PositionMessage} from "../Messages/generated/messages_pb";
|
import {PositionMessage} from "../Messages/generated/messages_pb";
|
||||||
import Direction = PositionMessage.Direction;
|
import Direction = PositionMessage.Direction;
|
||||||
import {PointInterface} from "../Connexion/ConnexionModels";
|
import type {PointInterface} from "../Connexion/ConnexionModels";
|
||||||
|
|
||||||
export class ProtobufClientUtils {
|
export class ProtobufClientUtils {
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import Sprite = Phaser.GameObjects.Sprite;
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
import Container = Phaser.GameObjects.Container;
|
import Container = Phaser.GameObjects.Container;
|
||||||
import { lazyLoadCompanionResource } from "./CompanionTexturesLoadingManager";
|
|
||||||
import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation";
|
import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation";
|
||||||
|
|
||||||
export interface CompanionStatus {
|
export interface CompanionStatus {
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js';
|
import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js';
|
||||||
|
import {waScaleManager} from "../Services/WaScaleManager";
|
||||||
const outOfScreenX = -1000;
|
|
||||||
const outOfScreenY = -1000;
|
|
||||||
|
|
||||||
|
|
||||||
//the assets were found here: https://hannemann.itch.io/virtual-joystick-pack-free
|
//the assets were found here: https://hannemann.itch.io/virtual-joystick-pack-free
|
||||||
export const joystickBaseKey = 'joystickBase';
|
export const joystickBaseKey = 'joystickBase';
|
||||||
@ -10,26 +7,58 @@ export const joystickBaseImg = 'resources/objects/joystickSplitted.png';
|
|||||||
export const joystickThumbKey = 'joystickThumb';
|
export const joystickThumbKey = 'joystickThumb';
|
||||||
export const joystickThumbImg = 'resources/objects/smallHandleFilledGrey.png';
|
export const joystickThumbImg = 'resources/objects/smallHandleFilledGrey.png';
|
||||||
|
|
||||||
|
const baseSize = 50;
|
||||||
|
const thumbSize = 25;
|
||||||
|
const radius = 17.5;
|
||||||
|
|
||||||
export class MobileJoystick extends VirtualJoystick {
|
export class MobileJoystick extends VirtualJoystick {
|
||||||
|
private resizeCallback: () => void;
|
||||||
|
|
||||||
constructor(scene: Phaser.Scene) {
|
constructor(scene: Phaser.Scene) {
|
||||||
super(scene, {
|
super(scene, {
|
||||||
x: outOfScreenX,
|
x: -1000,
|
||||||
y: outOfScreenY,
|
y: -1000,
|
||||||
radius: 20,
|
radius: radius * window.devicePixelRatio,
|
||||||
base: scene.add.image(0, 0, joystickBaseKey).setDisplaySize(60, 60).setDepth(99999),
|
base: scene.add.image(0, 0, joystickBaseKey).setDisplaySize(baseSize * window.devicePixelRatio, baseSize * window.devicePixelRatio).setDepth(99999),
|
||||||
thumb: scene.add.image(0, 0, joystickThumbKey).setDisplaySize(30, 30).setDepth(99999),
|
thumb: scene.add.image(0, 0, joystickThumbKey).setDisplaySize(thumbSize * window.devicePixelRatio, thumbSize * window.devicePixelRatio).setDepth(99999),
|
||||||
enable: true,
|
enable: true,
|
||||||
dir: "8dir",
|
dir: "8dir",
|
||||||
});
|
});
|
||||||
|
this.visible = false;
|
||||||
|
this.enable = false;
|
||||||
|
|
||||||
this.scene.input.on('pointerdown', (pointer: { x: number; y: number; }) => {
|
this.scene.input.on('pointerdown', (pointer: { x: number; y: number; wasTouch: boolean; event: TouchEvent }) => {
|
||||||
|
if (!pointer.wasTouch) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's only display the joystick if there is one finger on the screen
|
||||||
|
if (pointer.event.touches.length === 1) {
|
||||||
this.x = pointer.x;
|
this.x = pointer.x;
|
||||||
this.y = pointer.y;
|
this.y = pointer.y;
|
||||||
|
this.visible = true;
|
||||||
|
this.enable = true;
|
||||||
|
} else {
|
||||||
|
this.visible = false;
|
||||||
|
this.enable = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.scene.input.on('pointerup', () => {
|
this.scene.input.on('pointerup', () => {
|
||||||
this.x = outOfScreenX;
|
this.visible = false;
|
||||||
this.y = outOfScreenY;
|
this.enable = false;
|
||||||
});
|
});
|
||||||
|
this.resizeCallback = this.resize.bind(this);
|
||||||
|
this.scene.scale.on(Phaser.Scale.Events.RESIZE, this.resizeCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private resize() {
|
||||||
|
this.base.setDisplaySize(baseSize / waScaleManager.zoomModifier * window.devicePixelRatio, baseSize / waScaleManager.zoomModifier * window.devicePixelRatio);
|
||||||
|
this.thumb.setDisplaySize(thumbSize / waScaleManager.zoomModifier * window.devicePixelRatio, thumbSize / waScaleManager.zoomModifier * window.devicePixelRatio);
|
||||||
|
this.setRadius(radius / waScaleManager.zoomModifier * window.devicePixelRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy() {
|
||||||
|
super.destroy();
|
||||||
|
this.scene.scale.removeListener(Phaser.Scale.Events.RESIZE, this.resizeCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,7 +17,6 @@ export class SoundMeter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private init(context: AudioContext) {
|
private init(context: AudioContext) {
|
||||||
if (this.context === undefined) {
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.analyser = this.context.createAnalyser();
|
this.analyser = this.context.createAnalyser();
|
||||||
|
|
||||||
@ -25,7 +24,6 @@ export class SoundMeter {
|
|||||||
const bufferLength = this.analyser.fftSize;
|
const bufferLength = this.analyser.fftSize;
|
||||||
this.dataArray = new Uint8Array(bufferLength);
|
this.dataArray = new Uint8Array(bufferLength);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public connectToSource(stream: MediaStream, context: AudioContext): void
|
public connectToSource(stream: MediaStream, context: AudioContext): void
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import Container = Phaser.GameObjects.Container;
|
import Container = Phaser.GameObjects.Container;
|
||||||
import {Scene} from "phaser";
|
import type {Scene} from "phaser";
|
||||||
import GameObject = Phaser.GameObjects.GameObject;
|
import GameObject = Phaser.GameObjects.GameObject;
|
||||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import {ITiledMapObject} from "../Map/ITiledMap";
|
import type {ITiledMapObject} from "../Map/ITiledMap";
|
||||||
import Text = Phaser.GameObjects.Text;
|
import type {GameScene} from "../Game/GameScene";
|
||||||
import {GameScene} from "../Game/GameScene";
|
|
||||||
import TextStyle = Phaser.GameObjects.TextStyle;
|
|
||||||
|
|
||||||
export class TextUtils {
|
export class TextUtils {
|
||||||
public static createTextFromITiledMapObject(scene: GameScene, object: ITiledMapObject): void {
|
public static createTextFromITiledMapObject(scene: GameScene, object: ITiledMapObject): void {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
||||||
import TextureManager = Phaser.Textures.TextureManager;
|
import type {CharacterTexture} from "../../Connexion/LocalUser";
|
||||||
import {CharacterTexture} from "../../Connexion/LocalUser";
|
|
||||||
import {BodyResourceDescriptionInterface, LAYERS, PLAYER_RESOURCES} from "./PlayerTextures";
|
import {BodyResourceDescriptionInterface, LAYERS, PLAYER_RESOURCES} from "./PlayerTextures";
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {GameScene} from "../Game/GameScene";
|
import type {GameScene} from "../Game/GameScene";
|
||||||
import {PointInterface} from "../../Connexion/ConnexionModels";
|
import type {PointInterface} from "../../Connexion/ConnexionModels";
|
||||||
import {Character} from "../Entity/Character";
|
import {Character} from "../Entity/Character";
|
||||||
import {PlayerAnimationDirections} from "../Player/Animation";
|
import type {PlayerAnimationDirections} from "../Player/Animation";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class representing the sprite of a remote player (a player that plays on another computer)
|
* Class representing the sprite of a remote player (a player that plays on another computer)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import Scene = Phaser.Scene;
|
import Scene = Phaser.Scene;
|
||||||
import {Character} from "./Character";
|
import type {Character} from "./Character";
|
||||||
|
|
||||||
//todo: improve this WIP
|
//todo: improve this WIP
|
||||||
export class SpeechBubble {
|
export class SpeechBubble {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {PointInterface} from "../../Connexion/ConnexionModels";
|
import type {PointInterface} from "../../Connexion/ConnexionModels";
|
||||||
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||||
|
|
||||||
export interface AddPlayerInterface {
|
export interface AddPlayerInterface {
|
||||||
userId: number;
|
userId: number;
|
||||||
|
53
front/src/Phaser/Game/DirtyScene.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import {ResizableScene} from "../Login/ResizableScene";
|
||||||
|
import GameObject = Phaser.GameObjects.GameObject;
|
||||||
|
import Events = Phaser.Scenes.Events;
|
||||||
|
import AnimationEvents = Phaser.Animations.Events;
|
||||||
|
import StructEvents = Phaser.Structs.Events;
|
||||||
|
import {SKIP_RENDER_OPTIMIZATIONS} from "../../Enum/EnvironmentVariable";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A scene that can track its dirty/pristine state.
|
||||||
|
*/
|
||||||
|
export abstract class DirtyScene extends ResizableScene {
|
||||||
|
private isAlreadyTracking: boolean = false;
|
||||||
|
protected dirty:boolean = true;
|
||||||
|
private objectListChanged:boolean = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track all objects added to the scene and adds a callback each time an animation is added.
|
||||||
|
* Whenever an object is added, removed, or when an animation is played, the dirty state is set to true.
|
||||||
|
*
|
||||||
|
* Note: this does not work with animations from sprites inside containers.
|
||||||
|
*/
|
||||||
|
protected trackDirtyAnims(): void {
|
||||||
|
if (this.isAlreadyTracking || SKIP_RENDER_OPTIMIZATIONS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isAlreadyTracking = true;
|
||||||
|
const trackAnimationFunction = this.trackAnimation.bind(this);
|
||||||
|
this.sys.updateList.on(StructEvents.PROCESS_QUEUE_ADD, (gameObject: GameObject) => {
|
||||||
|
this.objectListChanged = true;
|
||||||
|
gameObject.on(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction);
|
||||||
|
});
|
||||||
|
this.sys.updateList.on(StructEvents.PROCESS_QUEUE_REMOVE, (gameObject: GameObject) => {
|
||||||
|
this.objectListChanged = true;
|
||||||
|
gameObject.removeListener(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.events.on(Events.RENDER, () => {
|
||||||
|
this.objectListChanged = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private trackAnimation(): void {
|
||||||
|
this.objectListChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isDirty(): boolean {
|
||||||
|
return this.dirty || this.objectListChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onResize(ev: UIEvent): void {
|
||||||
|
this.objectListChanged = true;
|
||||||
|
}
|
||||||
|
}
|
90
front/src/Phaser/Game/Game.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import {SKIP_RENDER_OPTIMIZATIONS} from "../../Enum/EnvironmentVariable";
|
||||||
|
|
||||||
|
const Events = Phaser.Core.Events;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A specialization of the main Phaser Game scene.
|
||||||
|
* It comes with an optimization to skip rendering.
|
||||||
|
*
|
||||||
|
* Beware, the "step" function might vary in future versions of Phaser.
|
||||||
|
*/
|
||||||
|
export class Game extends Phaser.Game {
|
||||||
|
public step(time: number, delta: number)
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
if (this.pendingDestroy)
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
return this.runDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventEmitter = this.events;
|
||||||
|
|
||||||
|
// Global Managers like Input and Sound update in the prestep
|
||||||
|
|
||||||
|
eventEmitter.emit(Events.PRE_STEP, time, delta);
|
||||||
|
|
||||||
|
// This is mostly meant for user-land code and plugins
|
||||||
|
|
||||||
|
eventEmitter.emit(Events.STEP, time, delta);
|
||||||
|
|
||||||
|
// Update the Scene Manager and all active Scenes
|
||||||
|
|
||||||
|
this.scene.update(time, delta);
|
||||||
|
|
||||||
|
// Our final event before rendering starts
|
||||||
|
|
||||||
|
eventEmitter.emit(Events.POST_STEP, time, delta);
|
||||||
|
|
||||||
|
// This "if" is the changed introduced by the new "Game" class to avoid rendering unnecessarily.
|
||||||
|
if (SKIP_RENDER_OPTIMIZATIONS || this.isDirty()) {
|
||||||
|
const renderer = this.renderer;
|
||||||
|
|
||||||
|
// Run the Pre-render (clearing the canvas, setting background colors, etc)
|
||||||
|
|
||||||
|
renderer.preRender();
|
||||||
|
|
||||||
|
eventEmitter.emit(Events.PRE_RENDER, renderer, time, delta);
|
||||||
|
|
||||||
|
// The main render loop. Iterates all Scenes and all Cameras in those scenes, rendering to the renderer instance.
|
||||||
|
|
||||||
|
this.scene.render(renderer);
|
||||||
|
|
||||||
|
// The Post-Render call. Tidies up loose end, takes snapshots, etc.
|
||||||
|
|
||||||
|
renderer.postRender();
|
||||||
|
|
||||||
|
// The final event before the step repeats. Your last chance to do anything to the canvas before it all starts again.
|
||||||
|
|
||||||
|
eventEmitter.emit(Events.POST_RENDER, renderer, time, delta);
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
this.scene.isProcessing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private isDirty(): boolean {
|
||||||
|
// Loop through the scenes in forward order
|
||||||
|
for (let i = 0; i < this.scene.scenes.length; i++)
|
||||||
|
{
|
||||||
|
const scene = this.scene.scenes[i];
|
||||||
|
const sys = scene.sys;
|
||||||
|
|
||||||
|
if (sys.settings.visible && sys.settings.status >= Phaser.Scenes.LOADING && sys.settings.status < Phaser.Scenes.SLEEPING)
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
if(typeof scene.isDirty === 'function') {
|
||||||
|
// @ts-ignore
|
||||||
|
const isDirty = scene.isDirty() || scene.tweens.getAllTweens().length > 0;
|
||||||
|
if (isDirty) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import {GameScene} from "./GameScene";
|
import {GameScene} from "./GameScene";
|
||||||
import {connectionManager} from "../../Connexion/ConnectionManager";
|
import {connectionManager} from "../../Connexion/ConnectionManager";
|
||||||
import {Room} from "../../Connexion/Room";
|
import type {Room} from "../../Connexion/Room";
|
||||||
import {MenuScene, MenuSceneName} from "../Menu/MenuScene";
|
import {MenuScene, MenuSceneName} from "../Menu/MenuScene";
|
||||||
import {HelpCameraSettingsScene, HelpCameraSettingsSceneName} from "../Menu/HelpCameraSettingsScene";
|
import {HelpCameraSettingsScene, HelpCameraSettingsSceneName} from "../Menu/HelpCameraSettingsScene";
|
||||||
import {LoginSceneName} from "../Login/LoginScene";
|
import {LoginSceneName} from "../Login/LoginScene";
|
||||||
@ -95,11 +95,8 @@ export class GameManager {
|
|||||||
console.log('starting '+ (this.currentGameSceneName || this.startRoom.id))
|
console.log('starting '+ (this.currentGameSceneName || this.startRoom.id))
|
||||||
scenePlugin.start(this.currentGameSceneName || this.startRoom.id);
|
scenePlugin.start(this.currentGameSceneName || this.startRoom.id);
|
||||||
scenePlugin.launch(MenuSceneName);
|
scenePlugin.launch(MenuSceneName);
|
||||||
|
|
||||||
if (!localUserStore.getHelpCameraSettingsShown()) {
|
|
||||||
scenePlugin.launch(HelpCameraSettingsSceneName);//700
|
scenePlugin.launch(HelpCameraSettingsSceneName);//700
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public gameSceneIsCreated(scene: GameScene) {
|
public gameSceneIsCreated(scene: GameScene) {
|
||||||
this.currentGameSceneName = scene.scene.key;
|
this.currentGameSceneName = scene.scene.key;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {ITiledMap, ITiledMapLayer} from "../Map/ITiledMap";
|
import type {ITiledMap, ITiledMapLayer} from "../Map/ITiledMap";
|
||||||
import {LayersIterator} from "../Map/LayersIterator";
|
import {LayersIterator} from "../Map/LayersIterator";
|
||||||
|
|
||||||
export type PropertyChangeCallback = (newValue: string | number | boolean | undefined, oldValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) => void;
|
export type PropertyChangeCallback = (newValue: string | number | boolean | undefined, oldValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) => void;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {gameManager, HasMovedEvent} from "./GameManager";
|
import {gameManager, HasMovedEvent} from "./GameManager";
|
||||||
import {
|
import type {
|
||||||
GroupCreatedUpdatedMessageInterface,
|
GroupCreatedUpdatedMessageInterface,
|
||||||
MessageUserJoined,
|
MessageUserJoined,
|
||||||
MessageUserMovedInterface,
|
MessageUserMovedInterface,
|
||||||
@ -15,10 +15,8 @@ import {
|
|||||||
JITSI_PRIVATE_MODE,
|
JITSI_PRIVATE_MODE,
|
||||||
MAX_PER_GROUP,
|
MAX_PER_GROUP,
|
||||||
POSITION_DELAY,
|
POSITION_DELAY,
|
||||||
RESOLUTION,
|
|
||||||
ZOOM_LEVEL
|
|
||||||
} from "../../Enum/EnvironmentVariable";
|
} from "../../Enum/EnvironmentVariable";
|
||||||
import {
|
import type {
|
||||||
ITiledMap,
|
ITiledMap,
|
||||||
ITiledMapLayer,
|
ITiledMapLayer,
|
||||||
ITiledMapLayerProperty,
|
ITiledMapLayerProperty,
|
||||||
@ -27,7 +25,7 @@ import {
|
|||||||
ITiledMapTileLayer,
|
ITiledMapTileLayer,
|
||||||
ITiledTileSet
|
ITiledTileSet
|
||||||
} from "../Map/ITiledMap";
|
} from "../Map/ITiledMap";
|
||||||
import {AddPlayerInterface} from "./AddPlayerInterface";
|
import type {AddPlayerInterface} from "./AddPlayerInterface";
|
||||||
import {PlayerAnimationDirections} from "../Player/Animation";
|
import {PlayerAnimationDirections} from "../Player/Animation";
|
||||||
import {PlayerMovement} from "./PlayerMovement";
|
import {PlayerMovement} from "./PlayerMovement";
|
||||||
import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator";
|
import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator";
|
||||||
@ -51,13 +49,13 @@ import {
|
|||||||
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 {ItemFactoryInterface} from "../Items/ItemFactoryInterface";
|
import type {ItemFactoryInterface} from "../Items/ItemFactoryInterface";
|
||||||
import {ActionableItem} from "../Items/ActionableItem";
|
import type {ActionableItem} from "../Items/ActionableItem";
|
||||||
import {UserInputManager} from "../UserInput/UserInputManager";
|
import {UserInputManager} from "../UserInput/UserInputManager";
|
||||||
import {UserMovedMessage} from "../../Messages/generated/messages_pb";
|
import type {UserMovedMessage} from "../../Messages/generated/messages_pb";
|
||||||
import {ProtobufClientUtils} from "../../Network/ProtobufClientUtils";
|
import {ProtobufClientUtils} from "../../Network/ProtobufClientUtils";
|
||||||
import {connectionManager} from "../../Connexion/ConnectionManager";
|
import {connectionManager} from "../../Connexion/ConnectionManager";
|
||||||
import {RoomConnection} from "../../Connexion/RoomConnection";
|
import type {RoomConnection} from "../../Connexion/RoomConnection";
|
||||||
import {GlobalMessageManager} from "../../Administration/GlobalMessageManager";
|
import {GlobalMessageManager} from "../../Administration/GlobalMessageManager";
|
||||||
import {userMessageManager} from "../../Administration/UserMessageManager";
|
import {userMessageManager} from "../../Administration/UserMessageManager";
|
||||||
import {ConsoleGlobalMessageManager} from "../../Administration/ConsoleGlobalMessageManager";
|
import {ConsoleGlobalMessageManager} from "../../Administration/ConsoleGlobalMessageManager";
|
||||||
@ -83,13 +81,17 @@ import GameObject = Phaser.GameObjects.GameObject;
|
|||||||
import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
|
import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
|
||||||
import DOMElement = Phaser.GameObjects.DOMElement;
|
import DOMElement = Phaser.GameObjects.DOMElement;
|
||||||
import EVENT_TYPE =Phaser.Scenes.Events;
|
import EVENT_TYPE =Phaser.Scenes.Events;
|
||||||
import {Subscription} from "rxjs";
|
import type {Subscription} from "rxjs";
|
||||||
import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream";
|
import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream";
|
||||||
import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager";
|
import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager";
|
||||||
|
import RenderTexture = Phaser.GameObjects.RenderTexture;
|
||||||
|
import Tilemap = Phaser.Tilemaps.Tilemap;
|
||||||
|
import {DirtyScene} from "./DirtyScene";
|
||||||
import {TextUtils} from "../Components/TextUtils";
|
import {TextUtils} from "../Components/TextUtils";
|
||||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||||
import {PinchManager} from "../UserInput/PinchManager";
|
import {PinchManager} from "../UserInput/PinchManager";
|
||||||
import {joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey} from "../Components/MobileJoystick";
|
import {joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey} from "../Components/MobileJoystick";
|
||||||
|
import {waScaleManager} from "../Services/WaScaleManager";
|
||||||
import AnimatedTiles from "phaser-animated-tiles";
|
import AnimatedTiles from "phaser-animated-tiles";
|
||||||
|
|
||||||
export interface GameSceneInitInterface {
|
export interface GameSceneInitInterface {
|
||||||
@ -129,7 +131,7 @@ interface DeleteGroupEventInterface {
|
|||||||
|
|
||||||
const defaultStartLayerName = 'start';
|
const defaultStartLayerName = 'start';
|
||||||
|
|
||||||
export class GameScene extends ResizableScene implements CenterListener {
|
export class GameScene extends DirtyScene implements CenterListener {
|
||||||
Terrains : Array<Phaser.Tilemaps.Tileset>;
|
Terrains : Array<Phaser.Tilemaps.Tileset>;
|
||||||
CurrentPlayer!: CurrentGamerInterface;
|
CurrentPlayer!: CurrentGamerInterface;
|
||||||
MapPlayers!: Phaser.Physics.Arcade.Group;
|
MapPlayers!: Phaser.Physics.Arcade.Group;
|
||||||
@ -152,7 +154,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
private GlobalMessageManager!: GlobalMessageManager;
|
private GlobalMessageManager!: GlobalMessageManager;
|
||||||
public ConsoleGlobalMessageManager!: ConsoleGlobalMessageManager;
|
public ConsoleGlobalMessageManager!: ConsoleGlobalMessageManager;
|
||||||
private connectionAnswerPromise: Promise<RoomJoinedMessageInterface>;
|
private connectionAnswerPromise: Promise<RoomJoinedMessageInterface>;
|
||||||
private connectionAnswerPromiseResolve!: (value?: RoomJoinedMessageInterface | PromiseLike<RoomJoinedMessageInterface>) => void;
|
private connectionAnswerPromiseResolve!: (value: RoomJoinedMessageInterface | PromiseLike<RoomJoinedMessageInterface>) => void;
|
||||||
// A promise that will resolve when the "create" method is called (signaling loading is ended)
|
// A promise that will resolve when the "create" method is called (signaling loading is ended)
|
||||||
private createPromise: Promise<void>;
|
private createPromise: Promise<void>;
|
||||||
private createPromiseResolve!: (value?: void | PromiseLike<void>) => void;
|
private createPromiseResolve!: (value?: void | PromiseLike<void>) => void;
|
||||||
@ -186,6 +188,9 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
private messageSubscription: Subscription|null = null;
|
private messageSubscription: Subscription|null = null;
|
||||||
private popUpElements : Map<number, DOMElement> = new Map<number, Phaser.GameObjects.DOMElement>();
|
private popUpElements : Map<number, DOMElement> = new Map<number, Phaser.GameObjects.DOMElement>();
|
||||||
private originalMapUrl: string|undefined;
|
private originalMapUrl: string|undefined;
|
||||||
|
private pinchManager: PinchManager|undefined;
|
||||||
|
private mapTransitioning: boolean = false; //used to prevent transitions happenning at the same time.
|
||||||
|
private onVisibilityChangeCallback: () => void;
|
||||||
|
|
||||||
constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) {
|
constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) {
|
||||||
super({
|
super({
|
||||||
@ -201,15 +206,15 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
|
|
||||||
this.createPromise = new Promise<void>((resolve, reject): void => {
|
this.createPromise = new Promise<void>((resolve, reject): void => {
|
||||||
this.createPromiseResolve = resolve;
|
this.createPromiseResolve = resolve;
|
||||||
})
|
});
|
||||||
this.connectionAnswerPromise = new Promise<RoomJoinedMessageInterface>((resolve, reject): void => {
|
this.connectionAnswerPromise = new Promise<RoomJoinedMessageInterface>((resolve, reject): void => {
|
||||||
this.connectionAnswerPromiseResolve = resolve;
|
this.connectionAnswerPromiseResolve = resolve;
|
||||||
})
|
});
|
||||||
|
this.onVisibilityChangeCallback = this.onVisibilityChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
//hook preload scene
|
//hook preload scene
|
||||||
preload(): void {
|
preload(): void {
|
||||||
addLoader(this);
|
|
||||||
const localUser = localUserStore.getLocalUser();
|
const localUser = localUserStore.getLocalUser();
|
||||||
const textures = localUser?.textures;
|
const textures = localUser?.textures;
|
||||||
if (textures) {
|
if (textures) {
|
||||||
@ -270,6 +275,9 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
|
|
||||||
this.load.spritesheet('layout_modes', 'resources/objects/layout_modes.png', {frameWidth: 32, frameHeight: 32});
|
this.load.spritesheet('layout_modes', 'resources/objects/layout_modes.png', {frameWidth: 32, frameHeight: 32});
|
||||||
this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||||
|
|
||||||
|
//this function must stay at the end of preload function
|
||||||
|
addLoader(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
@ -368,12 +376,14 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
|
|
||||||
//hook create scene
|
//hook create scene
|
||||||
create(): void {
|
create(): void {
|
||||||
|
this.trackDirtyAnims();
|
||||||
|
|
||||||
gameManager.gameSceneIsCreated(this);
|
gameManager.gameSceneIsCreated(this);
|
||||||
urlManager.pushRoomIdToUrl(this.room);
|
urlManager.pushRoomIdToUrl(this.room);
|
||||||
this.startLayerName = urlManager.getStartLayerNameFromUrl();
|
this.startLayerName = urlManager.getStartLayerNameFromUrl();
|
||||||
|
|
||||||
if (touchScreenManager.supportTouchScreen) {
|
if (touchScreenManager.supportTouchScreen) {
|
||||||
new PinchManager(this);
|
this.pinchManager = new PinchManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.messageSubscription = worldFullMessageStream.stream.subscribe((message) => this.showWorldFullError(message))
|
this.messageSubscription = worldFullMessageStream.stream.subscribe((message) => this.showWorldFullError(message))
|
||||||
@ -399,7 +409,6 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
|
|
||||||
//add layer on map
|
//add layer on map
|
||||||
this.Layers = new Array<Phaser.Tilemaps.TilemapLayer>();
|
this.Layers = new Array<Phaser.Tilemaps.TilemapLayer>();
|
||||||
|
|
||||||
let depth = -2;
|
let depth = -2;
|
||||||
for (const layer of this.gameMap.layersIterator) {
|
for (const layer of this.gameMap.layersIterator) {
|
||||||
if (layer.type === 'tilelayer') {
|
if (layer.type === 'tilelayer') {
|
||||||
@ -502,6 +511,8 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
if (!this.room.isDisconnected()) {
|
if (!this.room.isDisconnected()) {
|
||||||
this.connect();
|
this.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener('visibilitychange', this.onVisibilityChangeCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -623,6 +634,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
self.chatModeSprite.setVisible(false);
|
self.chatModeSprite.setVisible(false);
|
||||||
self.openChatIcon.setVisible(false);
|
self.openChatIcon.setVisible(false);
|
||||||
audioManager.restoreVolume();
|
audioManager.restoreVolume();
|
||||||
|
self.onVisibilityChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -914,6 +926,8 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private onMapExit(exitKey: string) {
|
private onMapExit(exitKey: string) {
|
||||||
|
if (this.mapTransitioning) return;
|
||||||
|
this.mapTransitioning = true;
|
||||||
const {roomId, hash} = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance);
|
const {roomId, hash} = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance);
|
||||||
if (!roomId) throw new Error('Could not find the room from its exit key: '+exitKey);
|
if (!roomId) throw new Error('Could not find the room from its exit key: '+exitKey);
|
||||||
urlManager.pushStartLayerNameToUrl(hash);
|
urlManager.pushStartLayerNameToUrl(hash);
|
||||||
@ -931,6 +945,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
this.initPositionFromLayerName(hash || defaultStartLayerName);
|
this.initPositionFromLayerName(hash || defaultStartLayerName);
|
||||||
this.CurrentPlayer.x = this.startX;
|
this.CurrentPlayer.x = this.startX;
|
||||||
this.CurrentPlayer.y = this.startY;
|
this.CurrentPlayer.y = this.startY;
|
||||||
|
setTimeout(() => this.mapTransitioning = false, 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -950,10 +965,14 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
this.simplePeer?.closeAllConnections();
|
this.simplePeer?.closeAllConnections();
|
||||||
this.simplePeer?.unregister();
|
this.simplePeer?.unregister();
|
||||||
this.messageSubscription?.unsubscribe();
|
this.messageSubscription?.unsubscribe();
|
||||||
|
this.userInputManager.destroy();
|
||||||
|
this.pinchManager?.destroy();
|
||||||
|
|
||||||
for(const iframeEvents of this.iframeSubscriptionList){
|
for(const iframeEvents of this.iframeSubscriptionList){
|
||||||
iframeEvents.unsubscribe();
|
iframeEvents.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.removeEventListener('visibilitychange', this.onVisibilityChangeCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeAllRemotePlayers(): void {
|
private removeAllRemotePlayers(): void {
|
||||||
@ -1097,8 +1116,8 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
//todo: in a dedicated class/function?
|
//todo: in a dedicated class/function?
|
||||||
initCamera() {
|
initCamera() {
|
||||||
this.cameras.main.setBounds(0,0, this.Map.widthInPixels, this.Map.heightInPixels);
|
this.cameras.main.setBounds(0,0, this.Map.widthInPixels, this.Map.heightInPixels);
|
||||||
|
this.cameras.main.startFollow(this.CurrentPlayer, true);
|
||||||
this.updateCameraOffset();
|
this.updateCameraOffset();
|
||||||
this.cameras.main.setZoom(ZOOM_LEVEL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addLayer(Layer : Phaser.Tilemaps.TilemapLayer){
|
addLayer(Layer : Phaser.Tilemaps.TilemapLayer){
|
||||||
@ -1106,6 +1125,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createCollisionWithPlayer() {
|
createCollisionWithPlayer() {
|
||||||
|
this.physics.disableUpdate();
|
||||||
//add collision layer
|
//add collision layer
|
||||||
this.Layers.forEach((Layer: Phaser.Tilemaps.TilemapLayer) => {
|
this.Layers.forEach((Layer: Phaser.Tilemaps.TilemapLayer) => {
|
||||||
this.physics.add.collider(this.CurrentPlayer, Layer, (object1: GameObject, object2: GameObject) => {
|
this.physics.add.collider(this.CurrentPlayer, Layer, (object1: GameObject, object2: GameObject) => {
|
||||||
@ -1236,12 +1256,24 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
* @param delta The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate.
|
* @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 {
|
update(time: number, delta: number) : void {
|
||||||
mediaManager.setLastUpdateScene();
|
this.dirty = false;
|
||||||
|
mediaManager.updateScene();
|
||||||
this.currentTick = time;
|
this.currentTick = time;
|
||||||
|
if (this.CurrentPlayer.isMoving()) {
|
||||||
|
this.dirty = true;
|
||||||
|
}
|
||||||
this.CurrentPlayer.moveUser(delta);
|
this.CurrentPlayer.moveUser(delta);
|
||||||
|
if (this.CurrentPlayer.isMoving()) {
|
||||||
|
this.dirty = true;
|
||||||
|
this.physics.enableUpdate();
|
||||||
|
} else {
|
||||||
|
this.physics.disableUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Let's handle all events
|
// Let's handle all events
|
||||||
while (this.pendingEvents.length !== 0) {
|
while (this.pendingEvents.length !== 0) {
|
||||||
|
this.dirty = true;
|
||||||
const event = this.pendingEvents.dequeue();
|
const event = this.pendingEvents.dequeue();
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case "InitUserPositionEvent":
|
case "InitUserPositionEvent":
|
||||||
@ -1267,6 +1299,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
// Let's move all users
|
// Let's move all users
|
||||||
const updatedPlayersPositions = this.playersPositionInterpolator.getUpdatedPositions(time);
|
const updatedPlayersPositions = this.playersPositionInterpolator.getUpdatedPositions(time);
|
||||||
updatedPlayersPositions.forEach((moveEvent: HasMovedEvent, userId: number) => {
|
updatedPlayersPositions.forEach((moveEvent: HasMovedEvent, userId: number) => {
|
||||||
|
this.dirty = true;
|
||||||
const player: RemotePlayer | undefined = this.MapPlayersByKey.get(userId);
|
const player: RemotePlayer | undefined = this.MapPlayersByKey.get(userId);
|
||||||
if (player === undefined) {
|
if (player === undefined) {
|
||||||
throw new Error('Cannot find player with ID "' + userId + '"');
|
throw new Error('Cannot find player with ID "' + userId + '"');
|
||||||
@ -1436,7 +1469,8 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
this.connection?.emitActionableEvent(itemId, eventName, state, parameters);
|
this.connection?.emitActionableEvent(itemId, eventName, state, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onResize(): void {
|
public onResize(ev: UIEvent): void {
|
||||||
|
super.onResize(ev);
|
||||||
this.reposition();
|
this.reposition();
|
||||||
|
|
||||||
// Send new viewport to server
|
// Send new viewport to server
|
||||||
@ -1471,19 +1505,18 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the offset of the character compared to the center of the screen according to the layout mananger
|
* Updates the offset of the character compared to the center of the screen according to the layout manager
|
||||||
* (tries to put the character in the center of the reamining space if there is a discussion going on.
|
* (tries to put the character in the center of the remaining space if there is a discussion going on.
|
||||||
*/
|
*/
|
||||||
private updateCameraOffset(): void {
|
private updateCameraOffset(): void {
|
||||||
const array = layoutManager.findBiggestAvailableArray();
|
const array = layoutManager.findBiggestAvailableArray();
|
||||||
let xCenter = (array.xEnd - array.xStart) / 2 + array.xStart;
|
const xCenter = (array.xEnd - array.xStart) / 2 + array.xStart;
|
||||||
let yCenter = (array.yEnd - array.yStart) / 2 + array.yStart;
|
const yCenter = (array.yEnd - array.yStart) / 2 + array.yStart;
|
||||||
|
|
||||||
|
const game = HtmlUtils.querySelectorOrFail<HTMLCanvasElement>('#game canvas');
|
||||||
// Let's put this in Game coordinates by applying the zoom level:
|
// Let's put this in Game coordinates by applying the zoom level:
|
||||||
xCenter /= ZOOM_LEVEL * RESOLUTION;
|
|
||||||
yCenter /= ZOOM_LEVEL * RESOLUTION;
|
|
||||||
|
|
||||||
this.cameras.main.startFollow(this.CurrentPlayer, true, 1, 1, xCenter - this.game.renderer.width / 2, yCenter - this.game.renderer.height / 2);
|
this.cameras.main.setFollowOffset((xCenter - game.offsetWidth/2) * window.devicePixelRatio / this.scale.zoom , (yCenter - game.offsetHeight/2) * window.devicePixelRatio / this.scale.zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onCenterChange(): void {
|
public onCenterChange(): void {
|
||||||
@ -1504,6 +1537,8 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
mediaManager.addTriggerCloseJitsiFrameButton('close-jisi',() => {
|
mediaManager.addTriggerCloseJitsiFrameButton('close-jisi',() => {
|
||||||
this.stopJitsi();
|
this.stopJitsi();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.onVisibilityChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
public stopJitsi(): void {
|
public stopJitsi(): void {
|
||||||
@ -1539,6 +1574,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
openJitsiRoomFunction();
|
openJitsiRoomFunction();
|
||||||
}, this.userInputManager);
|
}, this.userInputManager);
|
||||||
}
|
}
|
||||||
|
this.onVisibilityChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo: put this into an 'orchestrator' scene (EntryScene?)
|
//todo: put this into an 'orchestrator' scene (EntryScene?)
|
||||||
@ -1573,4 +1609,25 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zoomByFactor(zoomFactor: number) {
|
||||||
|
waScaleManager.zoomModifier *= zoomFactor;
|
||||||
|
this.updateCameraOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
private onVisibilityChange(): void {
|
||||||
|
// If the overlay is not displayed, we are in Jitsi. We don't need the webcam.
|
||||||
|
if (!mediaManager.isGameOverlayVisible()) {
|
||||||
|
mediaManager.blurCamera();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.visibilityState === 'visible') {
|
||||||
|
mediaManager.focusCamera();
|
||||||
|
} else {
|
||||||
|
if (this.simplePeer.getNbConnections() === 0) {
|
||||||
|
mediaManager.blurCamera();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {HasMovedEvent} from "./GameManager";
|
import type {HasMovedEvent} from "./GameManager";
|
||||||
import {MAX_EXTRAPOLATION_TIME} from "../../Enum/EnvironmentVariable";
|
import {MAX_EXTRAPOLATION_TIME} from "../../Enum/EnvironmentVariable";
|
||||||
import {PositionInterface} from "../../Connexion/ConnexionModels";
|
import type {PositionInterface} from "../../Connexion/ConnexionModels";
|
||||||
|
|
||||||
export class PlayerMovement {
|
export class PlayerMovement {
|
||||||
public constructor(private startPosition: PositionInterface, private startTick: number, private endPosition: HasMovedEvent, private endTick: number) {
|
public constructor(private startPosition: PositionInterface, private startTick: number, private endPosition: HasMovedEvent, private endTick: number) {
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
* This class is in charge of computing the position of all players.
|
* This class is in charge of computing the position of all players.
|
||||||
* Player movement is delayed by 200ms so position depends on ticks.
|
* Player movement is delayed by 200ms so position depends on ticks.
|
||||||
*/
|
*/
|
||||||
import {PlayerMovement} from "./PlayerMovement";
|
import type {PlayerMovement} from "./PlayerMovement";
|
||||||
import {HasMovedEvent} from "./GameManager";
|
import type {HasMovedEvent} from "./GameManager";
|
||||||
|
|
||||||
export class PlayersPositionInterpolator {
|
export class PlayersPositionInterpolator {
|
||||||
playerMovements: Map<number, PlayerMovement> = new Map<number, PlayerMovement>();
|
playerMovements: Map<number, PlayerMovement> = new Map<number, PlayerMovement>();
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import Sprite = Phaser.GameObjects.Sprite;
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
import {OutlinePipeline} from "../Shaders/OutlinePipeline";
|
import {OutlinePipeline} from "../Shaders/OutlinePipeline";
|
||||||
import {GameScene} from "../Game/GameScene";
|
import type {GameScene} from "../Game/GameScene";
|
||||||
|
|
||||||
type EventCallback = (state: unknown, parameters: unknown) => void;
|
type EventCallback = (state: unknown, parameters: unknown) => void;
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import * as Phaser from 'phaser';
|
import * as Phaser from 'phaser';
|
||||||
import {Scene} from "phaser";
|
import {Scene} from "phaser";
|
||||||
import Sprite = Phaser.GameObjects.Sprite;
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
import {ITiledMapObject} from "../../Map/ITiledMap";
|
import type {ITiledMapObject} from "../../Map/ITiledMap";
|
||||||
import {ItemFactoryInterface} from "../ItemFactoryInterface";
|
import type {ItemFactoryInterface} from "../ItemFactoryInterface";
|
||||||
import {GameScene} from "../../Game/GameScene";
|
import type {GameScene} from "../../Game/GameScene";
|
||||||
import {ActionableItem} from "../ActionableItem";
|
import {ActionableItem} from "../ActionableItem";
|
||||||
import * as tg from "generic-type-guard";
|
import * as tg from "generic-type-guard";
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
import type {GameScene} from "../Game/GameScene";
|
||||||
|
import type {ITiledMapObject} from "../Map/ITiledMap";
|
||||||
|
import type {ActionableItem} from "./ActionableItem";
|
||||||
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
||||||
import {GameScene} from "../Game/GameScene";
|
|
||||||
import {ITiledMapObject} from "../Map/ITiledMap";
|
|
||||||
import {ActionableItem} from "./ActionableItem";
|
|
||||||
|
|
||||||
export interface ItemFactoryInterface {
|
export interface ItemFactoryInterface {
|
||||||
preload: (loader: LoaderPlugin) => void;
|
preload: (loader: LoaderPlugin) => void;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {ResizableScene} from "./ResizableScene";
|
import {ResizableScene} from "./ResizableScene";
|
||||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||||
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||||
import {loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager";
|
import {loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager";
|
||||||
import {CharacterTexture} from "../../Connexion/LocalUser";
|
import type {CharacterTexture} from "../../Connexion/LocalUser";
|
||||||
|
|
||||||
export abstract class AbstractCharacterScene extends ResizableScene {
|
export abstract class AbstractCharacterScene extends ResizableScene {
|
||||||
|
|
||||||
|
@ -6,12 +6,11 @@ import Container = Phaser.GameObjects.Container;
|
|||||||
import {gameManager} from "../Game/GameManager";
|
import {gameManager} from "../Game/GameManager";
|
||||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||||
import {addLoader} from "../Components/Loader";
|
import {addLoader} from "../Components/Loader";
|
||||||
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||||
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
||||||
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
|
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
|
||||||
import { MenuScene } from "../Menu/MenuScene";
|
import { MenuScene } from "../Menu/MenuScene";
|
||||||
import { SelectCharacterSceneName } from "./SelectCharacterScene";
|
import { SelectCharacterSceneName } from "./SelectCharacterScene";
|
||||||
import { RESOLUTION } from "../../Enum/EnvironmentVariable";
|
|
||||||
|
|
||||||
export const CustomizeSceneName = "CustomizeScene";
|
export const CustomizeSceneName = "CustomizeScene";
|
||||||
|
|
||||||
@ -35,24 +34,25 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
addLoader(this);
|
|
||||||
|
|
||||||
this.load.html(customizeSceneKey, 'resources/html/CustomCharacterScene.html');
|
this.load.html(customizeSceneKey, 'resources/html/CustomCharacterScene.html');
|
||||||
|
|
||||||
this.layers = loadAllLayers(this.load);
|
this.layers = loadAllLayers(this.load);
|
||||||
this.loadCustomSceneSelectCharacters().then((bodyResourceDescriptions) => {
|
this.loadCustomSceneSelectCharacters().then((bodyResourceDescriptions) => {
|
||||||
bodyResourceDescriptions.forEach((bodyResourceDescription) => {
|
bodyResourceDescriptions.forEach((bodyResourceDescription) => {
|
||||||
if(!bodyResourceDescription.level){
|
if(bodyResourceDescription.level == undefined || bodyResourceDescription.level < 0 || bodyResourceDescription.level > 5 ){
|
||||||
throw 'Texture level is null';
|
throw 'Texture level is null';
|
||||||
}
|
}
|
||||||
this.layers[bodyResourceDescription.level].unshift(bodyResourceDescription);
|
this.layers[bodyResourceDescription.level].unshift(bodyResourceDescription);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//this function must stay at the end of preload function
|
||||||
|
addLoader(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
const middleX = this.getMiddleX();
|
this.customizeSceneElement = this.add.dom(-1000, 0).createFromCache(customizeSceneKey);
|
||||||
this.customizeSceneElement = this.add.dom(middleX, 0).createFromCache(customizeSceneKey);
|
this.centerXDomElement(this.customizeSceneElement, 150);
|
||||||
MenuScene.revealMenusAfterInit(this.customizeSceneElement, customizeSceneKey);
|
MenuScene.revealMenusAfterInit(this.customizeSceneElement, customizeSceneKey);
|
||||||
|
|
||||||
this.customizeSceneElement.addListener('click');
|
this.customizeSceneElement.addListener('click');
|
||||||
@ -112,6 +112,8 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
this.moveLayers();
|
this.moveLayers();
|
||||||
this.updateSelectedLayer();
|
this.updateSelectedLayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.onResize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private moveCursorHorizontally(index: number): void {
|
private moveCursorHorizontally(index: number): void {
|
||||||
@ -259,13 +261,7 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update(time: number, delta: number): void {
|
update(time: number, delta: number): void {
|
||||||
const middleX = this.getMiddleX();
|
|
||||||
this.tweens.add({
|
|
||||||
targets: this.customizeSceneElement,
|
|
||||||
x: middleX,
|
|
||||||
duration: 1000,
|
|
||||||
ease: 'Power3'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onResize(): void {
|
public onResize(): void {
|
||||||
@ -274,24 +270,7 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2;
|
this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2;
|
||||||
this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 3;
|
this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 3;
|
||||||
|
|
||||||
const middleX = this.getMiddleX();
|
this.centerXDomElement(this.customizeSceneElement, 150);
|
||||||
this.tweens.add({
|
|
||||||
targets: this.customizeSceneElement,
|
|
||||||
x: middleX,
|
|
||||||
duration: 1000,
|
|
||||||
ease: 'Power3'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getMiddleX() : number{
|
|
||||||
return (this.game.renderer.width / RESOLUTION) -
|
|
||||||
(
|
|
||||||
this.customizeSceneElement
|
|
||||||
&& this.customizeSceneElement.node
|
|
||||||
&& this.customizeSceneElement.node.getBoundingClientRect().width > 0
|
|
||||||
? (this.customizeSceneElement.node.getBoundingClientRect().width / (2*RESOLUTION))
|
|
||||||
: 150
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private nextSceneToCamera(){
|
private nextSceneToCamera(){
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import {gameManager} from "../Game/GameManager";
|
import {gameManager} from "../Game/GameManager";
|
||||||
import {TextField} from "../Components/TextField";
|
import {TextField} from "../Components/TextField";
|
||||||
import Image = Phaser.GameObjects.Image;
|
import Image = Phaser.GameObjects.Image;
|
||||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
|
||||||
import {mediaManager} from "../../WebRtc/MediaManager";
|
import {mediaManager} from "../../WebRtc/MediaManager";
|
||||||
import {RESOLUTION} from "../../Enum/EnvironmentVariable";
|
|
||||||
import {SoundMeter} from "../Components/SoundMeter";
|
import {SoundMeter} from "../Components/SoundMeter";
|
||||||
import {SoundMeterSprite} from "../Components/SoundMeterSprite";
|
import {SoundMeterSprite} from "../Components/SoundMeterSprite";
|
||||||
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
|
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
|
||||||
@ -11,6 +9,7 @@ import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
|||||||
import {PinchManager} from "../UserInput/PinchManager";
|
import {PinchManager} from "../UserInput/PinchManager";
|
||||||
import Zone = Phaser.GameObjects.Zone;
|
import Zone = Phaser.GameObjects.Zone;
|
||||||
import { MenuScene } from "../Menu/MenuScene";
|
import { MenuScene } from "../Menu/MenuScene";
|
||||||
|
import {ResizableScene} from "./ResizableScene";
|
||||||
|
|
||||||
export const EnableCameraSceneName = "EnableCameraScene";
|
export const EnableCameraSceneName = "EnableCameraScene";
|
||||||
enum LoginTextures {
|
enum LoginTextures {
|
||||||
@ -23,7 +22,7 @@ enum LoginTextures {
|
|||||||
|
|
||||||
const enableCameraSceneKey = 'enableCameraScene';
|
const enableCameraSceneKey = 'enableCameraScene';
|
||||||
|
|
||||||
export class EnableCameraScene extends Phaser.Scene {
|
export class EnableCameraScene extends ResizableScene {
|
||||||
private textField!: TextField;
|
private textField!: TextField;
|
||||||
private cameraNameField!: TextField;
|
private cameraNameField!: TextField;
|
||||||
private arrowLeft!: Image;
|
private arrowLeft!: Image;
|
||||||
@ -37,11 +36,11 @@ export class EnableCameraScene extends Phaser.Scene {
|
|||||||
private soundMeter: SoundMeter;
|
private soundMeter: SoundMeter;
|
||||||
private soundMeterSprite!: SoundMeterSprite;
|
private soundMeterSprite!: SoundMeterSprite;
|
||||||
private microphoneNameField!: TextField;
|
private microphoneNameField!: TextField;
|
||||||
private repositionCallback!: (this: Window, ev: UIEvent) => void;
|
|
||||||
|
|
||||||
private enableCameraSceneElement!: Phaser.GameObjects.DOMElement;
|
private enableCameraSceneElement!: Phaser.GameObjects.DOMElement;
|
||||||
|
|
||||||
private mobileTapZone!: Zone;
|
private mobileTapZone!: Zone;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
key: EnableCameraSceneName
|
key: EnableCameraSceneName
|
||||||
@ -62,8 +61,9 @@ export class EnableCameraScene extends Phaser.Scene {
|
|||||||
|
|
||||||
create() {
|
create() {
|
||||||
|
|
||||||
const middleX = this.getMiddleX();
|
this.enableCameraSceneElement = this.add.dom(-1000, 0).createFromCache(enableCameraSceneKey);
|
||||||
this.enableCameraSceneElement = this.add.dom(middleX, 0).createFromCache(enableCameraSceneKey);
|
this.centerXDomElement(this.enableCameraSceneElement, 300);
|
||||||
|
|
||||||
MenuScene.revealMenusAfterInit(this.enableCameraSceneElement, enableCameraSceneKey);
|
MenuScene.revealMenusAfterInit(this.enableCameraSceneElement, enableCameraSceneKey);
|
||||||
|
|
||||||
const continuingButton = this.enableCameraSceneElement.getChildByID('enableCameraSceneFormSubmit') as HTMLButtonElement;
|
const continuingButton = this.enableCameraSceneElement.getChildByID('enableCameraSceneFormSubmit') as HTMLButtonElement;
|
||||||
@ -75,12 +75,14 @@ export class EnableCameraScene extends Phaser.Scene {
|
|||||||
if (touchScreenManager.supportTouchScreen) {
|
if (touchScreenManager.supportTouchScreen) {
|
||||||
new PinchManager(this);
|
new PinchManager(this);
|
||||||
}
|
}
|
||||||
|
//this.scale.setZoom(ZOOM_LEVEL);
|
||||||
|
//Phaser.Display.Align.In.BottomCenter(this.pressReturnField, zone);
|
||||||
|
|
||||||
/* FIX ME */
|
/* FIX ME */
|
||||||
this.textField = new TextField(this, this.game.renderer.width / 2, 20, '');
|
this.textField = new TextField(this, this.scale.width / 2, 20, '');
|
||||||
|
|
||||||
// For mobile purposes - we need a big enough touchable area.
|
// For mobile purposes - we need a big enough touchable area.
|
||||||
this.mobileTapZone = this.add.zone(this.game.renderer.width / 2,this.game.renderer.height - 30,200,50)
|
this.mobileTapZone = this.add.zone(this.scale.width / 2,this.scale.height - 30,200,50)
|
||||||
.setInteractive().on("pointerdown", () => {
|
.setInteractive().on("pointerdown", () => {
|
||||||
this.login();
|
this.login();
|
||||||
});
|
});
|
||||||
@ -130,8 +132,7 @@ export class EnableCameraScene extends Phaser.Scene {
|
|||||||
this.soundMeterSprite.setVisible(false);
|
this.soundMeterSprite.setVisible(false);
|
||||||
this.add.existing(this.soundMeterSprite);
|
this.add.existing(this.soundMeterSprite);
|
||||||
|
|
||||||
this.repositionCallback = this.reposition.bind(this);
|
this.onResize();
|
||||||
window.addEventListener('resize', this.repositionCallback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private previousCam(): void {
|
private previousCam(): void {
|
||||||
@ -209,10 +210,9 @@ export class EnableCameraScene extends Phaser.Scene {
|
|||||||
this.arrowUp.setVisible(this.microphoneSelected > 0);
|
this.arrowUp.setVisible(this.microphoneSelected > 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
this.reposition();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private reposition(): void {
|
public onResize(): void {
|
||||||
let div = HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('myCamVideoSetup');
|
let div = HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('myCamVideoSetup');
|
||||||
let bounds = div.getBoundingClientRect();
|
let bounds = div.getBoundingClientRect();
|
||||||
if (!div.srcObject) {
|
if (!div.srcObject) {
|
||||||
@ -225,44 +225,40 @@ export class EnableCameraScene extends Phaser.Scene {
|
|||||||
this.cameraNameField.x = this.game.renderer.width / 2;
|
this.cameraNameField.x = this.game.renderer.width / 2;
|
||||||
this.microphoneNameField.x = this.game.renderer.width / 2;
|
this.microphoneNameField.x = this.game.renderer.width / 2;
|
||||||
|
|
||||||
this.cameraNameField.y = bounds.top / RESOLUTION - 8;
|
this.cameraNameField.y = bounds.top / this.scale.zoom - 8;
|
||||||
|
|
||||||
this.soundMeterSprite.x = this.game.renderer.width / 2 - this.soundMeterSprite.getWidth() / 2;
|
this.soundMeterSprite.x = this.game.renderer.width / 2 - this.soundMeterSprite.getWidth() / 2;
|
||||||
this.soundMeterSprite.y = bounds.bottom / RESOLUTION + 16;
|
this.soundMeterSprite.y = bounds.bottom / this.scale.zoom + 16;
|
||||||
|
|
||||||
this.microphoneNameField.y = this.soundMeterSprite.y + 22;
|
this.microphoneNameField.y = this.soundMeterSprite.y + 22;
|
||||||
|
|
||||||
this.arrowRight.x = bounds.right / RESOLUTION + 16;
|
this.arrowRight.x = bounds.right / this.scale.zoom + 16;
|
||||||
this.arrowRight.y = (bounds.top + bounds.height / 2) / RESOLUTION;
|
this.arrowRight.y = (bounds.top + bounds.height / 2) / this.scale.zoom;
|
||||||
|
|
||||||
this.arrowLeft.x = bounds.left / RESOLUTION - 16;
|
this.arrowLeft.x = bounds.left / this.scale.zoom - 16;
|
||||||
this.arrowLeft.y = (bounds.top + bounds.height / 2) / RESOLUTION;
|
this.arrowLeft.y = (bounds.top + bounds.height / 2) / this.scale.zoom;
|
||||||
|
|
||||||
this.arrowDown.x = this.microphoneNameField.x + this.microphoneNameField.width / 2 + 16;
|
this.arrowDown.x = this.microphoneNameField.x + this.microphoneNameField.width / 2 + 16;
|
||||||
this.arrowDown.y = this.microphoneNameField.y;
|
this.arrowDown.y = this.microphoneNameField.y;
|
||||||
|
|
||||||
this.arrowUp.x = this.microphoneNameField.x - this.microphoneNameField.width / 2 - 16;
|
this.arrowUp.x = this.microphoneNameField.x - this.microphoneNameField.width / 2 - 16;
|
||||||
this.arrowUp.y = this.microphoneNameField.y;
|
this.arrowUp.y = this.microphoneNameField.y;
|
||||||
|
|
||||||
|
const actionBtn = document.querySelector<HTMLDivElement>('#enableCameraScene .action');
|
||||||
|
if (actionBtn !== null) {
|
||||||
|
actionBtn.style.top = (this.scale.height - 65) + 'px';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update(time: number, delta: number): void {
|
update(time: number, delta: number): void {
|
||||||
this.soundMeterSprite.setVolume(this.soundMeter.getVolume());
|
this.soundMeterSprite.setVolume(this.soundMeter.getVolume());
|
||||||
|
|
||||||
mediaManager.setLastUpdateScene();
|
this.centerXDomElement(this.enableCameraSceneElement, 300);
|
||||||
|
|
||||||
const middleX = this.getMiddleX();
|
|
||||||
this.tweens.add({
|
|
||||||
targets: this.enableCameraSceneElement,
|
|
||||||
x: middleX,
|
|
||||||
duration: 1000,
|
|
||||||
ease: 'Power3'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private login(): void {
|
private login(): void {
|
||||||
HtmlUtils.getElementByIdOrFail<HTMLDivElement>('webRtcSetup').style.display = 'none';
|
HtmlUtils.getElementByIdOrFail<HTMLDivElement>('webRtcSetup').style.display = 'none';
|
||||||
this.soundMeter.stop();
|
this.soundMeter.stop();
|
||||||
window.removeEventListener('resize', this.repositionCallback);
|
|
||||||
|
|
||||||
mediaManager.stopCamera();
|
mediaManager.stopCamera();
|
||||||
mediaManager.stopMicrophone();
|
mediaManager.stopMicrophone();
|
||||||
@ -282,15 +278,4 @@ export class EnableCameraScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
this.updateWebCamName();
|
this.updateWebCamName();
|
||||||
}
|
}
|
||||||
|
|
||||||
private getMiddleX() : number{
|
|
||||||
return (this.game.renderer.width / RESOLUTION) -
|
|
||||||
(
|
|
||||||
this.enableCameraSceneElement
|
|
||||||
&& this.enableCameraSceneElement.node
|
|
||||||
&& this.enableCameraSceneElement.node.getBoundingClientRect().width > 0
|
|
||||||
? (this.enableCameraSceneElement.node.getBoundingClientRect().width / (2*RESOLUTION))
|
|
||||||
: (300 / RESOLUTION)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import {gameManager} from "../Game/GameManager";
|
|||||||
import {Scene} from "phaser";
|
import {Scene} from "phaser";
|
||||||
import {ErrorScene} from "../Reconnecting/ErrorScene";
|
import {ErrorScene} from "../Reconnecting/ErrorScene";
|
||||||
import {WAError} from "../Reconnecting/WAError";
|
import {WAError} from "../Reconnecting/WAError";
|
||||||
|
import {waScaleManager} from "../Services/WaScaleManager";
|
||||||
|
|
||||||
export const EntrySceneName = "EntryScene";
|
export const EntrySceneName = "EntryScene";
|
||||||
|
|
||||||
@ -17,7 +18,11 @@ export class EntryScene extends Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
|
|
||||||
gameManager.init(this.scene).then((nextSceneName) => {
|
gameManager.init(this.scene).then((nextSceneName) => {
|
||||||
|
// Let's rescale before starting the game
|
||||||
|
// We can do it at this stage.
|
||||||
|
waScaleManager.applyNewSize();
|
||||||
this.scene.start(nextSceneName);
|
this.scene.start(nextSceneName);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
if (err.response && err.response.status == 404) {
|
if (err.response && err.response.status == 404) {
|
||||||
|
@ -4,7 +4,6 @@ import {ResizableScene} from "./ResizableScene";
|
|||||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||||
import {MenuScene} from "../Menu/MenuScene";
|
import {MenuScene} from "../Menu/MenuScene";
|
||||||
import { isUserNameValid } from "../../Connexion/LocalUser";
|
import { isUserNameValid } from "../../Connexion/LocalUser";
|
||||||
import { RESOLUTION } from "../../Enum/EnvironmentVariable";
|
|
||||||
|
|
||||||
export const LoginSceneName = "LoginScene";
|
export const LoginSceneName = "LoginScene";
|
||||||
|
|
||||||
@ -27,8 +26,8 @@ export class LoginScene extends ResizableScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
const middleX = this.getMiddleX();
|
this.loginSceneElement = this.add.dom(-1000, 0).createFromCache(loginSceneKey);
|
||||||
this.loginSceneElement = this.add.dom(middleX, 0).createFromCache(loginSceneKey);
|
this.centerXDomElement(this.loginSceneElement, 200);
|
||||||
MenuScene.revealMenusAfterInit(this.loginSceneElement, loginSceneKey);
|
MenuScene.revealMenusAfterInit(this.loginSceneElement, loginSceneKey);
|
||||||
|
|
||||||
const pErrorElement = this.loginSceneElement.getChildByID('errorLoginScene') as HTMLInputElement;
|
const pErrorElement = this.loginSceneElement.getChildByID('errorLoginScene') as HTMLInputElement;
|
||||||
@ -78,27 +77,10 @@ export class LoginScene extends ResizableScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update(time: number, delta: number): void {
|
update(time: number, delta: number): void {
|
||||||
const middleX = this.getMiddleX();
|
|
||||||
this.tweens.add({
|
|
||||||
targets: this.loginSceneElement,
|
|
||||||
x: middleX,
|
|
||||||
duration: 1000,
|
|
||||||
ease: 'Power3'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onResize(ev: UIEvent): void {
|
public onResize(ev: UIEvent): void {
|
||||||
const middleX = this.getMiddleX();
|
this.centerXDomElement(this.loginSceneElement, 200);
|
||||||
this.tweens.add({
|
|
||||||
targets: this.loginSceneElement,
|
|
||||||
x: middleX,
|
|
||||||
duration: 1000,
|
|
||||||
ease: 'Power3'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private getMiddleX() : number{
|
|
||||||
const middleX = ((window.innerWidth) - ((this.loginSceneElement && this.loginSceneElement.width > 0 ? this.loginSceneElement.width : 200 /*FIXME to use a const will be injected in HTMLElement*/)*2)) / 2;
|
|
||||||
return (middleX > 0 ? (middleX / 2) : 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,23 @@
|
|||||||
import {Scene} from "phaser";
|
import {Scene} from "phaser";
|
||||||
|
import DOMElement = Phaser.GameObjects.DOMElement;
|
||||||
|
|
||||||
export abstract class ResizableScene extends Scene {
|
export abstract class ResizableScene extends Scene {
|
||||||
public abstract onResize(ev: UIEvent): void;
|
public abstract onResize(ev: UIEvent): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Centers the DOM element on the X axis.
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* @param defaultWidth The width of the DOM element. We try to compute it but it may not be available if called from "create".
|
||||||
|
*/
|
||||||
|
public centerXDomElement(object: DOMElement, defaultWidth: number): void {
|
||||||
|
object.x = (this.scale.width / 2) -
|
||||||
|
(
|
||||||
|
object
|
||||||
|
&& object.node
|
||||||
|
&& object.node.getBoundingClientRect().width > 0
|
||||||
|
? (object.node.getBoundingClientRect().width / 2 / this.scale.zoom)
|
||||||
|
: (300 / this.scale.zoom)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
62
front/src/Phaser/Login/SelectCharacterMobileScene.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { SelectCharacterScene } from "./SelectCharacterScene";
|
||||||
|
|
||||||
|
export class SelectCharacterMobileScene extends SelectCharacterScene {
|
||||||
|
|
||||||
|
create(){
|
||||||
|
super.create();
|
||||||
|
this.selectedRectangle.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected defineSetupPlayer(numero: number){
|
||||||
|
const deltaX = 30;
|
||||||
|
const deltaY = 2;
|
||||||
|
let [playerX, playerY] = this.getCharacterPosition();
|
||||||
|
let playerVisible = true;
|
||||||
|
let playerScale = 1.5;
|
||||||
|
let playserOpactity = 1;
|
||||||
|
|
||||||
|
if( this.currentSelectUser !== numero ){
|
||||||
|
playerVisible = false;
|
||||||
|
}
|
||||||
|
if( numero === (this.currentSelectUser + 1) ){
|
||||||
|
playerY -= deltaY;
|
||||||
|
playerX += deltaX;
|
||||||
|
playerScale = 0.8;
|
||||||
|
playserOpactity = 0.6;
|
||||||
|
playerVisible = true;
|
||||||
|
}
|
||||||
|
if( numero === (this.currentSelectUser + 2) ){
|
||||||
|
playerY -= deltaY;
|
||||||
|
playerX += (deltaX * 2);
|
||||||
|
playerScale = 0.8;
|
||||||
|
playserOpactity = 0.6;
|
||||||
|
playerVisible = true;
|
||||||
|
}
|
||||||
|
if( numero === (this.currentSelectUser - 1) ){
|
||||||
|
playerY -= deltaY;
|
||||||
|
playerX -= deltaX;
|
||||||
|
playerScale = 0.8;
|
||||||
|
playserOpactity = 0.6;
|
||||||
|
playerVisible = true;
|
||||||
|
}
|
||||||
|
if( numero === (this.currentSelectUser - 2) ){
|
||||||
|
playerY -= deltaY;
|
||||||
|
playerX -= (deltaX * 2);
|
||||||
|
playerScale = 0.8;
|
||||||
|
playserOpactity = 0.6;
|
||||||
|
playerVisible = true;
|
||||||
|
}
|
||||||
|
return {playerX, playerY, playerScale, playserOpactity, playerVisible}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns pixel position by on column and row number
|
||||||
|
*/
|
||||||
|
protected getCharacterPosition(): [number, number] {
|
||||||
|
return [
|
||||||
|
this.game.renderer.width / 2,
|
||||||
|
this.game.renderer.height / 3
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,13 +1,12 @@
|
|||||||
import {isMobile} from "../../Enum/EnvironmentVariable";
|
import {isMobile} from "../../Enum/EnvironmentVariable";
|
||||||
import {gameManager} from "../Game/GameManager";
|
import {gameManager} from "../Game/GameManager";
|
||||||
import Image = Phaser.GameObjects.Image;
|
|
||||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||||
import {EnableCameraSceneName} from "./EnableCameraScene";
|
import {EnableCameraSceneName} from "./EnableCameraScene";
|
||||||
import {CustomizeSceneName} from "./CustomizeScene";
|
import {CustomizeSceneName} from "./CustomizeScene";
|
||||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||||
import {loadAllDefaultModels} from "../Entity/PlayerTexturesLoadingManager";
|
import {loadAllDefaultModels} from "../Entity/PlayerTexturesLoadingManager";
|
||||||
import {addLoader} from "../Components/Loader";
|
import {addLoader} from "../Components/Loader";
|
||||||
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||||
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
||||||
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
|
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
|
||||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||||
@ -37,8 +36,6 @@ export class SelectCharacterScene extends AbstractCharacterScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
addLoader(this);
|
|
||||||
|
|
||||||
this.load.html(selectCharacterKey, 'resources/html/selectCharacterScene.html');
|
this.load.html(selectCharacterKey, 'resources/html/selectCharacterScene.html');
|
||||||
|
|
||||||
this.loadSelectSceneCharacters().then((bodyResourceDescriptions) => {
|
this.loadSelectSceneCharacters().then((bodyResourceDescriptions) => {
|
||||||
@ -47,13 +44,15 @@ export class SelectCharacterScene extends AbstractCharacterScene {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
this.playerModels = loadAllDefaultModels(this.load);
|
this.playerModels = loadAllDefaultModels(this.load);
|
||||||
|
|
||||||
|
//this function must stay at the end of preload function
|
||||||
addLoader(this);
|
addLoader(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
|
|
||||||
const middleX = this.getMiddleX();
|
this.selectCharacterSceneElement = this.add.dom(-1000, 0).createFromCache(selectCharacterKey);
|
||||||
this.selectCharacterSceneElement = this.add.dom(middleX, 0).createFromCache(selectCharacterKey);
|
this.centerXDomElement(this.selectCharacterSceneElement, 150);
|
||||||
MenuScene.revealMenusAfterInit(this.selectCharacterSceneElement, selectCharacterKey);
|
MenuScene.revealMenusAfterInit(this.selectCharacterSceneElement, selectCharacterKey);
|
||||||
|
|
||||||
this.selectCharacterSceneElement.addListener('click');
|
this.selectCharacterSceneElement.addListener('click');
|
||||||
@ -279,36 +278,12 @@ export class SelectCharacterScene extends AbstractCharacterScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update(time: number, delta: number): void {
|
update(time: number, delta: number): void {
|
||||||
const middleX = this.getMiddleX();
|
|
||||||
this.tweens.add({
|
|
||||||
targets: this.selectCharacterSceneElement,
|
|
||||||
x: middleX,
|
|
||||||
duration: 1000,
|
|
||||||
ease: 'Power3'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onResize(ev: UIEvent): void {
|
public onResize(ev: UIEvent): void {
|
||||||
//move position of user
|
//move position of user
|
||||||
this.moveUser();
|
this.moveUser();
|
||||||
|
|
||||||
const middleX = this.getMiddleX();
|
this.centerXDomElement(this.selectCharacterSceneElement, 150);
|
||||||
this.tweens.add({
|
|
||||||
targets: this.selectCharacterSceneElement,
|
|
||||||
x: middleX,
|
|
||||||
duration: 1000,
|
|
||||||
ease: 'Power3'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getMiddleX() : number{
|
|
||||||
return (this.game.renderer.width / 2) -
|
|
||||||
(
|
|
||||||
this.selectCharacterSceneElement
|
|
||||||
&& this.selectCharacterSceneElement.node
|
|
||||||
&& this.selectCharacterSceneElement.node.getBoundingClientRect().width > 0
|
|
||||||
? (this.selectCharacterSceneElement.node.getBoundingClientRect().width / 4)
|
|
||||||
: 150
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,11 @@ import { gameManager} from "../Game/GameManager";
|
|||||||
import { ResizableScene } from "./ResizableScene";
|
import { ResizableScene } from "./ResizableScene";
|
||||||
import { EnableCameraSceneName } from "./EnableCameraScene";
|
import { EnableCameraSceneName } from "./EnableCameraScene";
|
||||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||||
import { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures";
|
import type { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures";
|
||||||
import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingManager";
|
import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingManager";
|
||||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||||
import {PinchManager} from "../UserInput/PinchManager";
|
import {PinchManager} from "../UserInput/PinchManager";
|
||||||
import { MenuScene } from "../Menu/MenuScene";
|
import { MenuScene } from "../Menu/MenuScene";
|
||||||
import { RESOLUTION } from "../../Enum/EnvironmentVariable";
|
|
||||||
|
|
||||||
export const SelectCompanionSceneName = "SelectCompanionScene";
|
export const SelectCompanionSceneName = "SelectCompanionScene";
|
||||||
|
|
||||||
@ -31,21 +30,20 @@ export class SelectCompanionScene extends ResizableScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
addLoader(this);
|
|
||||||
|
|
||||||
this.load.html(selectCompanionSceneKey, 'resources/html/SelectCompanionScene.html');
|
this.load.html(selectCompanionSceneKey, 'resources/html/SelectCompanionScene.html');
|
||||||
|
|
||||||
getAllCompanionResources(this.load).forEach(model => {
|
getAllCompanionResources(this.load).forEach(model => {
|
||||||
this.companionModels.push(model);
|
this.companionModels.push(model);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//this function must stay at the end of preload function
|
||||||
addLoader(this);
|
addLoader(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
|
|
||||||
const middleX = this.getMiddleX();
|
this.selectCompanionSceneElement = this.add.dom(-1000, 0).createFromCache(selectCompanionSceneKey);
|
||||||
this.selectCompanionSceneElement = this.add.dom(middleX, 0).createFromCache(selectCompanionSceneKey);
|
this.centerXDomElement(this.selectCompanionSceneElement, 150);
|
||||||
MenuScene.revealMenusAfterInit(this.selectCompanionSceneElement, selectCompanionSceneKey);
|
MenuScene.revealMenusAfterInit(this.selectCompanionSceneElement, selectCompanionSceneKey);
|
||||||
|
|
||||||
this.selectCompanionSceneElement.addListener('click');
|
this.selectCompanionSceneElement.addListener('click');
|
||||||
@ -88,13 +86,7 @@ export class SelectCompanionScene extends ResizableScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update(time: number, delta: number): void {
|
update(time: number, delta: number): void {
|
||||||
const middleX = this.getMiddleX();
|
|
||||||
this.tweens.add({
|
|
||||||
targets: this.selectCompanionSceneElement,
|
|
||||||
x: middleX,
|
|
||||||
duration: 1000,
|
|
||||||
ease: 'Power3'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private nextScene(): void {
|
private nextScene(): void {
|
||||||
@ -137,13 +129,7 @@ export class SelectCompanionScene extends ResizableScene {
|
|||||||
public onResize(ev: UIEvent): void {
|
public onResize(ev: UIEvent): void {
|
||||||
this.moveCompanion();
|
this.moveCompanion();
|
||||||
|
|
||||||
const middleX = this.getMiddleX();
|
this.centerXDomElement(this.selectCompanionSceneElement, 150);
|
||||||
this.tweens.add({
|
|
||||||
targets: this.selectCompanionSceneElement,
|
|
||||||
x: middleX,
|
|
||||||
duration: 1000,
|
|
||||||
ease: 'Power3'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateSelectedCompanion(): void {
|
private updateSelectedCompanion(): void {
|
||||||
@ -239,15 +225,4 @@ export class SelectCompanionScene extends ResizableScene {
|
|||||||
companion.setX(companionX);
|
companion.setX(companionX);
|
||||||
companion.setY(companionY);
|
companion.setY(companionY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getMiddleX() : number{
|
|
||||||
return (this.game.renderer.width / RESOLUTION) -
|
|
||||||
(
|
|
||||||
this.selectCompanionSceneElement
|
|
||||||
&& this.selectCompanionSceneElement.node
|
|
||||||
&& this.selectCompanionSceneElement.node.getBoundingClientRect().width > 0
|
|
||||||
? (this.selectCompanionSceneElement.node.getBoundingClientRect().width / (2*RESOLUTION))
|
|
||||||
: 150
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {ITiledMap, ITiledMapLayer} from "./ITiledMap";
|
import type {ITiledMap, ITiledMapLayer} from "./ITiledMap";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates over the layers of a map, flattening the grouped layers
|
* Iterates over the layers of a map, flattening the grouped layers
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import {mediaManager} from "../../WebRtc/MediaManager";
|
import {mediaManager} from "../../WebRtc/MediaManager";
|
||||||
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
|
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
|
||||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||||
import {RESOLUTION} from "../../Enum/EnvironmentVariable";
|
import {DirtyScene} from "../Game/DirtyScene";
|
||||||
|
|
||||||
export const HelpCameraSettingsSceneName = 'HelpCameraSettingsScene';
|
export const HelpCameraSettingsSceneName = 'HelpCameraSettingsScene';
|
||||||
const helpCameraSettings = 'helpCameraSettings';
|
const helpCameraSettings = 'helpCameraSettings';
|
||||||
/**
|
/**
|
||||||
* The scene that show how to permit Camera and Microphone access if there are not already allowed
|
* The scene that show how to permit Camera and Microphone access if there are not already allowed
|
||||||
*/
|
*/
|
||||||
export class HelpCameraSettingsScene extends Phaser.Scene {
|
export class HelpCameraSettingsScene extends DirtyScene {
|
||||||
private helpCameraSettingsElement!: Phaser.GameObjects.DOMElement;
|
private helpCameraSettingsElement!: Phaser.GameObjects.DOMElement;
|
||||||
private helpCameraSettingsOpened: boolean = false;
|
private helpCameraSettingsOpened: boolean = false;
|
||||||
|
|
||||||
@ -30,6 +30,9 @@ export class HelpCameraSettingsScene extends Phaser.Scene {
|
|||||||
this.revealMenusAfterInit(this.helpCameraSettingsElement, helpCameraSettings);
|
this.revealMenusAfterInit(this.helpCameraSettingsElement, helpCameraSettings);
|
||||||
this.helpCameraSettingsElement.addListener('click');
|
this.helpCameraSettingsElement.addListener('click');
|
||||||
this.helpCameraSettingsElement.on('click', (event:MouseEvent) => {
|
this.helpCameraSettingsElement.on('click', (event:MouseEvent) => {
|
||||||
|
if((event?.target as HTMLInputElement).id === 'mailto') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if((event?.target as HTMLInputElement).id === 'helpCameraSettingsFormRefresh') {
|
if((event?.target as HTMLInputElement).id === 'helpCameraSettingsFormRefresh') {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
@ -38,21 +41,28 @@ export class HelpCameraSettingsScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!mediaManager.constraintsMedia.audio || !mediaManager.constraintsMedia.video) {
|
if(!localUserStore.getHelpCameraSettingsShown() && (!mediaManager.constraintsMedia.audio || !mediaManager.constraintsMedia.video)){
|
||||||
this.openHelpCameraSettingsOpened();
|
this.openHelpCameraSettingsOpened();
|
||||||
} else {
|
localUserStore.setHelpCameraSettingsShown();
|
||||||
this.closeHelpCameraSettingsOpened();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mediaManager.setHelpCameraSettingsCallBack(() => {
|
||||||
|
this.openHelpCameraSettingsOpened();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private openHelpCameraSettingsOpened(): void{
|
private openHelpCameraSettingsOpened(): void{
|
||||||
HtmlUtils.getElementByIdOrFail<HTMLDivElement>('webRtcSetup').style.display = 'none';
|
HtmlUtils.getElementByIdOrFail<HTMLDivElement>('webRtcSetup').style.display = 'none';
|
||||||
this.helpCameraSettingsOpened = true;
|
this.helpCameraSettingsOpened = true;
|
||||||
|
try{
|
||||||
if(window.navigator.userAgent.includes('Firefox')){
|
if(window.navigator.userAgent.includes('Firefox')){
|
||||||
HtmlUtils.getElementByIdOrFail<HTMLParagraphElement>('browserHelpSetting').innerHTML ='<img src="/resources/objects/help-setting-camera-permission-firefox.png"/>';
|
HtmlUtils.getElementByIdOrFail<HTMLParagraphElement>('browserHelpSetting').innerHTML ='<img src="/resources/objects/help-setting-camera-permission-firefox.png"/>';
|
||||||
}else if(window.navigator.userAgent.includes('Chrome')){
|
}else if(window.navigator.userAgent.includes('Chrome')){
|
||||||
HtmlUtils.getElementByIdOrFail<HTMLParagraphElement>('browserHelpSetting').innerHTML ='<img src="/resources/objects/help-setting-camera-permission-chrome.png"/>';
|
HtmlUtils.getElementByIdOrFail<HTMLParagraphElement>('browserHelpSetting').innerHTML ='<img src="/resources/objects/help-setting-camera-permission-chrome.png"/>';
|
||||||
}
|
}
|
||||||
|
}catch(err) {
|
||||||
|
console.error('openHelpCameraSettingsOpened => getElementByIdOrFail => error', err);
|
||||||
|
}
|
||||||
const middleY = this.getMiddleY();
|
const middleY = this.getMiddleY();
|
||||||
const middleX = this.getMiddleX();
|
const middleX = this.getMiddleX();
|
||||||
this.tweens.add({
|
this.tweens.add({
|
||||||
@ -63,23 +73,26 @@ export class HelpCameraSettingsScene extends Phaser.Scene {
|
|||||||
ease: 'Power3',
|
ease: 'Power3',
|
||||||
overflow: 'scroll'
|
overflow: 'scroll'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private closeHelpCameraSettingsOpened(): void{
|
private closeHelpCameraSettingsOpened(): void{
|
||||||
const middleX = this.getMiddleX();
|
const middleX = this.getMiddleX();
|
||||||
const helpCameraSettingsInfo = this.helpCameraSettingsElement.getChildByID('helpCameraSettings') as HTMLParagraphElement;
|
/*const helpCameraSettingsInfo = this.helpCameraSettingsElement.getChildByID('helpCameraSettings') as HTMLParagraphElement;
|
||||||
helpCameraSettingsInfo.innerText = '';
|
helpCameraSettingsInfo.innerText = '';
|
||||||
helpCameraSettingsInfo.style.display = 'none';
|
helpCameraSettingsInfo.style.display = 'none';*/
|
||||||
this.helpCameraSettingsOpened = false;
|
this.helpCameraSettingsOpened = false;
|
||||||
this.tweens.add({
|
this.tweens.add({
|
||||||
targets: this.helpCameraSettingsElement,
|
targets: this.helpCameraSettingsElement,
|
||||||
y: -400,
|
y: -1000,
|
||||||
x: middleX,
|
x: middleX,
|
||||||
duration: 1000,
|
duration: 1000,
|
||||||
ease: 'Power3',
|
ease: 'Power3',
|
||||||
overflow: 'scroll'
|
overflow: 'scroll'
|
||||||
});
|
});
|
||||||
localUserStore.setHelpCameraSettingsShown();
|
|
||||||
|
this.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private revealMenusAfterInit(menuElement: Phaser.GameObjects.DOMElement, rootDomId: string) {
|
private revealMenusAfterInit(menuElement: Phaser.GameObjects.DOMElement, rootDomId: string) {
|
||||||
@ -91,18 +104,12 @@ export class HelpCameraSettingsScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update(time: number, delta: number): void {
|
update(time: number, delta: number): void {
|
||||||
const middleX = this.getMiddleX();
|
this.dirty = false;
|
||||||
const middleY = this.getMiddleY();
|
|
||||||
this.tweens.add({
|
|
||||||
targets: this.helpCameraSettingsElement,
|
|
||||||
x: middleX,
|
|
||||||
y: middleY,
|
|
||||||
duration: 1000,
|
|
||||||
ease: 'Power3'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onResize(ev: UIEvent): void {
|
public onResize(ev: UIEvent): void {
|
||||||
|
super.onResize(ev);
|
||||||
|
if (this.helpCameraSettingsOpened) {
|
||||||
const middleX = this.getMiddleX();
|
const middleX = this.getMiddleX();
|
||||||
const middleY = this.getMiddleY();
|
const middleY = this.getMiddleY();
|
||||||
this.tweens.add({
|
this.tweens.add({
|
||||||
@ -112,27 +119,32 @@ export class HelpCameraSettingsScene extends Phaser.Scene {
|
|||||||
duration: 1000,
|
duration: 1000,
|
||||||
ease: 'Power3'
|
ease: 'Power3'
|
||||||
});
|
});
|
||||||
|
this.dirty = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getMiddleX() : number{
|
private getMiddleX() : number{
|
||||||
return (this.game.renderer.width / RESOLUTION) -
|
return (this.scale.width / 2) -
|
||||||
(
|
(
|
||||||
this.helpCameraSettingsElement
|
this.helpCameraSettingsElement
|
||||||
&& this.helpCameraSettingsElement.node
|
&& this.helpCameraSettingsElement.node
|
||||||
&& this.helpCameraSettingsElement.node.getBoundingClientRect().width > 0
|
&& this.helpCameraSettingsElement.node.getBoundingClientRect().width > 0
|
||||||
? (this.helpCameraSettingsElement.node.getBoundingClientRect().width / 4)
|
? (this.helpCameraSettingsElement.node.getBoundingClientRect().width / (2 * this.scale.zoom))
|
||||||
: (400 / 2)
|
: (400 / 2)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getMiddleY() : number{
|
private getMiddleY() : number{
|
||||||
const middleY = ((window.innerHeight) - (
|
const middleY = ((this.scale.height) - (
|
||||||
(this.helpCameraSettingsElement
|
(this.helpCameraSettingsElement
|
||||||
&& this.helpCameraSettingsElement.node
|
&& this.helpCameraSettingsElement.node
|
||||||
&& this.helpCameraSettingsElement.node.getBoundingClientRect().height > 0
|
&& this.helpCameraSettingsElement.node.getBoundingClientRect().height > 0
|
||||||
? this.helpCameraSettingsElement.node.getBoundingClientRect().height : 400 /*FIXME to use a const will be injected in HTMLElement*/) * 2)) / 2;
|
? this.helpCameraSettingsElement.node.getBoundingClientRect().height : 400 /*FIXME to use a const will be injected in HTMLElement*/)/this.scale.zoom)) / 2;
|
||||||
return (middleY > 0 ? middleY / RESOLUTION : 0);
|
return (middleY > 0 ? middleY : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isDirty(): boolean {
|
||||||
|
return this.dirty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,11 +192,11 @@ export class MenuScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let middleY = (window.innerHeight / 3) - (257);
|
let middleY = this.scale.height / 2 - 392/2;
|
||||||
if(middleY < 0){
|
if(middleY < 0){
|
||||||
middleY = 0;
|
middleY = 0;
|
||||||
}
|
}
|
||||||
let middleX = (window.innerWidth / 3) - 298;
|
let middleX = this.scale.width / 2 - 457/2;
|
||||||
if(middleX < 0){
|
if(middleX < 0){
|
||||||
middleX = 0;
|
middleX = 0;
|
||||||
}
|
}
|
||||||
@ -236,11 +236,11 @@ export class MenuScene extends Phaser.Scene {
|
|||||||
|
|
||||||
this.gameShareOpened = true;
|
this.gameShareOpened = true;
|
||||||
|
|
||||||
let middleY = (window.innerHeight / 3) - (257);
|
let middleY = this.scale.height / 2 - 85;
|
||||||
if(middleY < 0){
|
if(middleY < 0){
|
||||||
middleY = 0;
|
middleY = 0;
|
||||||
}
|
}
|
||||||
let middleX = (window.innerWidth / 3) - 298;
|
let middleX = this.scale.width / 2 - 200;
|
||||||
if(middleX < 0){
|
if(middleX < 0){
|
||||||
middleX = 0;
|
middleX = 0;
|
||||||
}
|
}
|
||||||
@ -350,4 +350,8 @@ export class MenuScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isDirty(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {PlayerAnimationDirections} from "./Animation";
|
import {PlayerAnimationDirections} from "./Animation";
|
||||||
import {GameScene} from "../Game/GameScene";
|
import type {GameScene} from "../Game/GameScene";
|
||||||
import {UserInputEvent, UserInputManager} from "../UserInput/UserInputManager";
|
import {UserInputEvent, UserInputManager} from "../UserInput/UserInputManager";
|
||||||
import {Character} from "../Entity/Character";
|
import {Character} from "../Entity/Character";
|
||||||
|
|
||||||
@ -7,6 +7,7 @@ export const hasMovedEventName = "hasMoved";
|
|||||||
export interface CurrentGamerInterface extends Character{
|
export interface CurrentGamerInterface extends Character{
|
||||||
moveUser(delta: number) : void;
|
moveUser(delta: number) : void;
|
||||||
say(text : string) : void;
|
say(text : string) : void;
|
||||||
|
isMoving(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Player extends Character implements CurrentGamerInterface {
|
export class Player extends Character implements CurrentGamerInterface {
|
||||||
@ -83,4 +84,8 @@ export class Player extends Character implements CurrentGamerInterface {
|
|||||||
}
|
}
|
||||||
this.wasMoving = moving;
|
this.wasMoving = moving;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isMoving(): boolean {
|
||||||
|
return this.wasMoving;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
111
front/src/Phaser/Services/HdpiManager.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
|
||||||
|
interface Size {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HdpiManager {
|
||||||
|
private _zoomModifier: number = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param minRecommendedGamePixelsNumber The minimum number of pixels we want to display "by default" to the user
|
||||||
|
* @param absoluteMinPixelNumber The very minimum of game pixels to display. Below, we forbid zooming more
|
||||||
|
*/
|
||||||
|
public constructor(private minRecommendedGamePixelsNumber: number, private absoluteMinPixelNumber: number) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the optimal size in "game pixels" based on the screen size in "real pixels".
|
||||||
|
*
|
||||||
|
* Note: the function is returning the optimal size in "game pixels" in the "game" property,
|
||||||
|
* but also recommends resizing the "real" pixel screen size of the canvas.
|
||||||
|
* The proposed new real size is a few pixels bigger than the real size available (if the size is not a multiple of the pixel size) and should overflow.
|
||||||
|
*
|
||||||
|
* @param realPixelScreenSize
|
||||||
|
*/
|
||||||
|
public getOptimalGameSize(realPixelScreenSize: Size): { game: Size, real: Size } {
|
||||||
|
const realPixelNumber = realPixelScreenSize.width * realPixelScreenSize.height;
|
||||||
|
// If the screen has not a definition small enough to match the minimum number of pixels we want to display,
|
||||||
|
// let's make the canvas the size of the screen (in real pixels)
|
||||||
|
if (realPixelNumber <= this.minRecommendedGamePixelsNumber) {
|
||||||
|
return {
|
||||||
|
game: realPixelScreenSize,
|
||||||
|
real: realPixelScreenSize
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const optimalZoomLevel = this.getOptimalZoomLevel(realPixelNumber);
|
||||||
|
|
||||||
|
// Has the canvas more pixels than the screen? This is forbidden
|
||||||
|
if (optimalZoomLevel * this._zoomModifier < 1) {
|
||||||
|
// Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter)
|
||||||
|
this._zoomModifier = 1 / optimalZoomLevel;
|
||||||
|
|
||||||
|
return {
|
||||||
|
game: {
|
||||||
|
width: realPixelScreenSize.width,
|
||||||
|
height: realPixelScreenSize.height,
|
||||||
|
},
|
||||||
|
real: {
|
||||||
|
width: realPixelScreenSize.width,
|
||||||
|
height: realPixelScreenSize.height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const gameWidth = Math.ceil(realPixelScreenSize.width / optimalZoomLevel / this._zoomModifier);
|
||||||
|
const gameHeight = Math.ceil(realPixelScreenSize.height / optimalZoomLevel / this._zoomModifier);
|
||||||
|
|
||||||
|
// Let's ensure we display a minimum of pixels, even if crazily zoomed in.
|
||||||
|
if (gameWidth * gameHeight < this.absoluteMinPixelNumber) {
|
||||||
|
const minGameHeight = Math.sqrt(this.absoluteMinPixelNumber * realPixelScreenSize.height / realPixelScreenSize.width);
|
||||||
|
const minGameWidth = Math.sqrt(this.absoluteMinPixelNumber * realPixelScreenSize.width / realPixelScreenSize.height);
|
||||||
|
|
||||||
|
// Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter)
|
||||||
|
this._zoomModifier = realPixelScreenSize.width / minGameWidth / optimalZoomLevel;
|
||||||
|
|
||||||
|
return {
|
||||||
|
game: {
|
||||||
|
width: minGameWidth,
|
||||||
|
height: minGameHeight,
|
||||||
|
},
|
||||||
|
real: {
|
||||||
|
width: realPixelScreenSize.width,
|
||||||
|
height: realPixelScreenSize.height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
game: {
|
||||||
|
width: gameWidth,
|
||||||
|
height: gameHeight,
|
||||||
|
},
|
||||||
|
real: {
|
||||||
|
width: Math.ceil(realPixelScreenSize.width / optimalZoomLevel) * optimalZoomLevel,
|
||||||
|
height: Math.ceil(realPixelScreenSize.height / optimalZoomLevel) * optimalZoomLevel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We only accept integer but we make an exception for 1.5
|
||||||
|
*/
|
||||||
|
private getOptimalZoomLevel(realPixelNumber: number): number {
|
||||||
|
const result = Math.sqrt(realPixelNumber / this.minRecommendedGamePixelsNumber);
|
||||||
|
if (1.5 <= result && result < 2) {
|
||||||
|
return 1.5
|
||||||
|
} else {
|
||||||
|
return Math.floor(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get zoomModifier(): number {
|
||||||
|
return this._zoomModifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set zoomModifier(zoomModifier: number) {
|
||||||
|
this._zoomModifier = zoomModifier;
|
||||||
|
}
|
||||||
|
}
|
47
front/src/Phaser/Services/WaScaleManager.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import {HdpiManager} from "./HdpiManager";
|
||||||
|
import ScaleManager = Phaser.Scale.ScaleManager;
|
||||||
|
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
|
||||||
|
|
||||||
|
|
||||||
|
class WaScaleManager {
|
||||||
|
private hdpiManager: HdpiManager;
|
||||||
|
private scaleManager!: ScaleManager;
|
||||||
|
|
||||||
|
public constructor(private minGamePixelsNumber: number, private absoluteMinPixelNumber: number) {
|
||||||
|
this.hdpiManager = new HdpiManager(minGamePixelsNumber, absoluteMinPixelNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setScaleManager(scaleManager: ScaleManager) {
|
||||||
|
this.scaleManager = scaleManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public applyNewSize() {
|
||||||
|
const {width, height} = coWebsiteManager.getGameSize();
|
||||||
|
|
||||||
|
let devicePixelRatio = 1;
|
||||||
|
if (window.devicePixelRatio) {
|
||||||
|
devicePixelRatio = window.devicePixelRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { game: gameSize, real: realSize } = this.hdpiManager.getOptimalGameSize({width: width * devicePixelRatio, height: height * devicePixelRatio});
|
||||||
|
|
||||||
|
this.scaleManager.setZoom(realSize.width / gameSize.width / devicePixelRatio);
|
||||||
|
this.scaleManager.resize(gameSize.width, gameSize.height);
|
||||||
|
|
||||||
|
// Override bug in canvas resizing in Phaser. Let's resize the canvas ourselves
|
||||||
|
const style = this.scaleManager.canvas.style;
|
||||||
|
style.width = Math.ceil(realSize.width / devicePixelRatio) + 'px';
|
||||||
|
style.height = Math.ceil(realSize.height / devicePixelRatio) + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
public get zoomModifier(): number {
|
||||||
|
return this.hdpiManager.zoomModifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set zoomModifier(zoomModifier: number) {
|
||||||
|
this.hdpiManager.zoomModifier = zoomModifier;
|
||||||
|
this.applyNewSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const waScaleManager = new WaScaleManager(640*480, 196*196);
|
@ -1,4 +1,6 @@
|
|||||||
import {Pinch} from "phaser3-rex-plugins/plugins/gestures.js";
|
import {Pinch} from "phaser3-rex-plugins/plugins/gestures.js";
|
||||||
|
import {waScaleManager} from "../Services/WaScaleManager";
|
||||||
|
import {GameScene} from "../Game/GameScene";
|
||||||
|
|
||||||
export class PinchManager {
|
export class PinchManager {
|
||||||
private scene: Phaser.Scene;
|
private scene: Phaser.Scene;
|
||||||
@ -7,16 +9,33 @@ export class PinchManager {
|
|||||||
constructor(scene: Phaser.Scene) {
|
constructor(scene: Phaser.Scene) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.pinch = new Pinch(scene);
|
this.pinch = new Pinch(scene);
|
||||||
|
this.pinch.setDragThreshold(10);
|
||||||
|
|
||||||
|
// The "pinch.scaleFactor" value is very sensitive and causes the screen to flicker.
|
||||||
|
// We are smoothing its value with previous values to prevent the flicking.
|
||||||
|
let smoothPinch = 1;
|
||||||
|
|
||||||
|
this.pinch.on('pinchstart', () => {
|
||||||
|
smoothPinch = 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
this.pinch.on('pinch', (pinch:any) => { // eslint-disable-line
|
this.pinch.on('pinch', (pinch:any) => { // eslint-disable-line
|
||||||
let newZoom = this.scene.cameras.main.zoom * pinch.scaleFactor;
|
if (pinch.scaleFactor > 1.2 || pinch.scaleFactor < 0.8) {
|
||||||
if (newZoom < 0.25) {
|
// Pinch too fast! Probably a bad measure.
|
||||||
newZoom = 0.25;
|
return;
|
||||||
} else if(newZoom > 2) {
|
}
|
||||||
newZoom = 2;
|
|
||||||
|
smoothPinch = 3/5*smoothPinch + 2/5*pinch.scaleFactor;
|
||||||
|
if (this.scene instanceof GameScene) {
|
||||||
|
this.scene.zoomByFactor(smoothPinch);
|
||||||
|
} else {
|
||||||
|
waScaleManager.zoomModifier *= smoothPinch;
|
||||||
}
|
}
|
||||||
this.scene.cameras.main.setZoom(newZoom);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.pinch.removeAllListeners();
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import { Direction } from "../../types";
|
import type { Direction } from "../../types";
|
||||||
import {GameScene} from "../Game/GameScene";
|
import type {GameScene} from "../Game/GameScene";
|
||||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||||
import {MobileJoystick} from "../Components/MobileJoystick";
|
import {MobileJoystick} from "../Components/MobileJoystick";
|
||||||
|
|
||||||
@ -54,6 +54,7 @@ export class UserInputManager {
|
|||||||
this.Scene = Scene;
|
this.Scene = Scene;
|
||||||
this.isInputDisabled = false;
|
this.isInputDisabled = false;
|
||||||
this.initKeyBoardEvent();
|
this.initKeyBoardEvent();
|
||||||
|
this.initMouseWheel();
|
||||||
if (touchScreenManager.supportTouchScreen) {
|
if (touchScreenManager.supportTouchScreen) {
|
||||||
this.initVirtualJoystick();
|
this.initVirtualJoystick();
|
||||||
}
|
}
|
||||||
@ -170,4 +171,14 @@ export class UserInputManager {
|
|||||||
removeSpaceEventListner(callback : Function){
|
removeSpaceEventListner(callback : Function){
|
||||||
this.Scene.input.keyboard.removeListener('keyup-SPACE', callback);
|
this.Scene.input.keyboard.removeListener('keyup-SPACE', callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
this.joystick?.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private initMouseWheel() {
|
||||||
|
this.Scene.input.on('wheel', (pointer: unknown, gameObjects: unknown, deltaX: number, deltaY: number, deltaZ: number) => {
|
||||||
|
this.Scene.zoomByFactor(1 - deltaY / 53 * 0.1);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {Room} from "../Connexion/Room";
|
import type {Room} from "../Connexion/Room";
|
||||||
|
|
||||||
export enum GameConnexionTypes {
|
export enum GameConnexionTypes {
|
||||||
anonymous=1,
|
anonymous=1,
|
||||||
|
@ -162,13 +162,14 @@ class CoWebsiteManager {
|
|||||||
if (allowPolicy) {
|
if (allowPolicy) {
|
||||||
iframe.allow = allowPolicy;
|
iframe.allow = allowPolicy;
|
||||||
}
|
}
|
||||||
const onloadPromise = new Promise((resolve) => {
|
const onloadPromise = new Promise<void>((resolve) => {
|
||||||
iframe.onload = () => resolve();
|
iframe.onload = () => resolve();
|
||||||
});
|
});
|
||||||
if (allowApi) {
|
if (allowApi) {
|
||||||
iframeListener.registerIframe(iframe);
|
iframeListener.registerIframe(iframe);
|
||||||
}
|
}
|
||||||
const onTimeoutPromise = new Promise((resolve) => {
|
this.cowebsiteMainDom.appendChild(iframe);
|
||||||
|
const onTimeoutPromise = new Promise<void>((resolve) => {
|
||||||
setTimeout(() => resolve(), 2000);
|
setTimeout(() => resolve(), 2000);
|
||||||
});
|
});
|
||||||
this.currentOperationPromise = this.currentOperationPromise.then(() =>Promise.race([onloadPromise, onTimeoutPromise])).then(() => {
|
this.currentOperationPromise = this.currentOperationPromise.then(() =>Promise.race([onloadPromise, onTimeoutPromise])).then(() => {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {HtmlUtils} from "./HtmlUtils";
|
import {HtmlUtils} from "./HtmlUtils";
|
||||||
import {mediaManager, ReportCallback, ShowReportCallBack} from "./MediaManager";
|
import type {ShowReportCallBack} from "./MediaManager";
|
||||||
import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
import type {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
||||||
import {connectionManager} from "../Connexion/ConnectionManager";
|
import {connectionManager} from "../Connexion/ConnectionManager";
|
||||||
import {GameConnexionTypes} from "../Url/UrlManager";
|
import {GameConnexionTypes} from "../Url/UrlManager";
|
||||||
import {iframeListener} from "../Api/IframeListener";
|
import {iframeListener} from "../Api/IframeListener";
|
||||||
|
@ -10,9 +10,10 @@ interface jitsiConfigInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getDefaultConfig = () : jitsiConfigInterface => {
|
const getDefaultConfig = () : jitsiConfigInterface => {
|
||||||
|
const constraints = mediaManager.getConstraintRequestedByUser();
|
||||||
return {
|
return {
|
||||||
startWithAudioMuted: !mediaManager.constraintsMedia.audio,
|
startWithAudioMuted: !constraints.audio,
|
||||||
startWithVideoMuted: mediaManager.constraintsMedia.video === false,
|
startWithVideoMuted: constraints.video === false,
|
||||||
prejoinPageEnabled: false
|
prejoinPageEnabled: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,7 +72,7 @@ class JitsiFactory {
|
|||||||
private jitsiApi: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
private jitsiApi: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
private audioCallback = this.onAudioChange.bind(this);
|
private audioCallback = this.onAudioChange.bind(this);
|
||||||
private videoCallback = this.onVideoChange.bind(this);
|
private videoCallback = this.onVideoChange.bind(this);
|
||||||
private previousConfigMeet? : jitsiConfigInterface;
|
private previousConfigMeet! : jitsiConfigInterface;
|
||||||
private jitsiScriptLoaded: boolean = false;
|
private jitsiScriptLoaded: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -136,32 +137,24 @@ class JitsiFactory {
|
|||||||
|
|
||||||
//restore previous config
|
//restore previous config
|
||||||
if(this.previousConfigMeet?.startWithAudioMuted){
|
if(this.previousConfigMeet?.startWithAudioMuted){
|
||||||
mediaManager.disableMicrophone();
|
await mediaManager.disableMicrophone();
|
||||||
}else{
|
}else{
|
||||||
mediaManager.enableMicrophone();
|
await mediaManager.enableMicrophone();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.previousConfigMeet?.startWithVideoMuted){
|
if(this.previousConfigMeet?.startWithVideoMuted){
|
||||||
mediaManager.disableCamera();
|
await mediaManager.disableCamera();
|
||||||
}else{
|
}else{
|
||||||
mediaManager.enableCamera();
|
await mediaManager.enableCamera();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onAudioChange({muted}: {muted: boolean}): void {
|
private onAudioChange({muted}: {muted: boolean}): void {
|
||||||
if (muted && mediaManager.constraintsMedia.audio === true) {
|
this.previousConfigMeet.startWithAudioMuted = muted;
|
||||||
mediaManager.disableMicrophone();
|
|
||||||
} else if(!muted && mediaManager.constraintsMedia.audio === false) {
|
|
||||||
mediaManager.enableMicrophone();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onVideoChange({muted}: {muted: boolean}): void {
|
private onVideoChange({muted}: {muted: boolean}): void {
|
||||||
if (muted && mediaManager.constraintsMedia.video !== false) {
|
this.previousConfigMeet.startWithVideoMuted = muted;
|
||||||
mediaManager.disableCamera();
|
|
||||||
} else if(!muted && mediaManager.constraintsMedia.video === false) {
|
|
||||||
mediaManager.enableCamera();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadJitsiScript(domain: string): Promise<void> {
|
private async loadJitsiScript(domain: string): Promise<void> {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { UserInputManager } from "../Phaser/UserInput/UserInputManager";
|
import type { UserInputManager } from "../Phaser/UserInput/UserInputManager";
|
||||||
import {HtmlUtils} from "./HtmlUtils";
|
import {HtmlUtils} from "./HtmlUtils";
|
||||||
|
|
||||||
export enum LayoutMode {
|
export enum LayoutMode {
|
||||||
@ -349,7 +349,7 @@ class LayoutManager {
|
|||||||
userInputManager.addSpaceEventListner(callBack);
|
userInputManager.addSpaceEventListner(callBack);
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeActionButton(id: string, userInputManager: UserInputManager){
|
public removeActionButton(id: string, userInputManager?: UserInputManager){
|
||||||
//delete previous element
|
//delete previous element
|
||||||
const previousDiv = this.actionButtonInformation.get(id);
|
const previousDiv = this.actionButtonInformation.get(id);
|
||||||
if(previousDiv){
|
if(previousDiv){
|
||||||
@ -357,10 +357,45 @@ class LayoutManager {
|
|||||||
this.actionButtonInformation.delete(id);
|
this.actionButtonInformation.delete(id);
|
||||||
}
|
}
|
||||||
const previousEventCallback = this.actionButtonTrigger.get(id);
|
const previousEventCallback = this.actionButtonTrigger.get(id);
|
||||||
if(previousEventCallback){
|
if(previousEventCallback && userInputManager){
|
||||||
userInputManager.removeSpaceEventListner(previousEventCallback);
|
userInputManager.removeSpaceEventListner(previousEventCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addInformation(id: string, text: string, callBack?: Function, userInputManager?: UserInputManager){
|
||||||
|
//delete previous element
|
||||||
|
for ( const [key, value] of this.actionButtonInformation ) {
|
||||||
|
this.removeActionButton(key, userInputManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
//create div and text html component
|
||||||
|
const p = document.createElement('p');
|
||||||
|
p.classList.add('action-body');
|
||||||
|
p.innerText = text;
|
||||||
|
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.classList.add('action');
|
||||||
|
div.classList.add(id);
|
||||||
|
div.id = id;
|
||||||
|
div.appendChild(p);
|
||||||
|
|
||||||
|
this.actionButtonInformation.set(id, div);
|
||||||
|
|
||||||
|
const mainContainer = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('main-container');
|
||||||
|
mainContainer.appendChild(div);
|
||||||
|
//add trigger action
|
||||||
|
if(callBack){
|
||||||
|
div.onpointerdown = () => {
|
||||||
|
callBack();
|
||||||
|
this.removeActionButton(id, userInputManager);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove it after 10 sec
|
||||||
|
setTimeout(() => {
|
||||||
|
this.removeActionButton(id, userInputManager);
|
||||||
|
}, 10000)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const layoutManager = new LayoutManager();
|
const layoutManager = new LayoutManager();
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import {DivImportance, layoutManager} from "./LayoutManager";
|
import {DivImportance, layoutManager} from "./LayoutManager";
|
||||||
import {HtmlUtils} from "./HtmlUtils";
|
import {HtmlUtils} from "./HtmlUtils";
|
||||||
import {discussionManager, SendMessageCallback} from "./DiscussionManager";
|
import {discussionManager, SendMessageCallback} from "./DiscussionManager";
|
||||||
import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
import type {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
||||||
import {localUserStore} from "../Connexion/LocalUserStore";
|
import {localUserStore} from "../Connexion/LocalUserStore";
|
||||||
import {UserSimplePeerInterface} from "./SimplePeer";
|
import type {UserSimplePeerInterface} from "./SimplePeer";
|
||||||
|
import {SoundMeter} from "../Phaser/Components/SoundMeter";
|
||||||
|
import {DISABLE_NOTIFICATIONS} from "../Enum/EnvironmentVariable";
|
||||||
|
|
||||||
declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
|
|
||||||
let videoConstraint: boolean|MediaTrackConstraints = {
|
let videoConstraint: boolean|MediaTrackConstraints = {
|
||||||
@ -26,6 +29,7 @@ export type StartScreenSharingCallback = (media: MediaStream) => void;
|
|||||||
export type StopScreenSharingCallback = (media: MediaStream) => void;
|
export type StopScreenSharingCallback = (media: MediaStream) => void;
|
||||||
export type ReportCallback = (message: string) => void;
|
export type ReportCallback = (message: string) => void;
|
||||||
export type ShowReportCallBack = (userId: string, userName: string|undefined) => void;
|
export type ShowReportCallBack = (userId: string, userName: string|undefined) => void;
|
||||||
|
export type HelpCameraSettingsCallBack = () => void;
|
||||||
|
|
||||||
// TODO: Split MediaManager in 2 classes: MediaManagerUI (in charge of HTML) and MediaManager (singleton in charge of the camera only)
|
// TODO: Split MediaManager in 2 classes: MediaManagerUI (in charge of HTML) and MediaManager (singleton in charge of the camera only)
|
||||||
export class MediaManager {
|
export class MediaManager {
|
||||||
@ -40,6 +44,8 @@ export class MediaManager {
|
|||||||
microphoneClose: HTMLImageElement;
|
microphoneClose: HTMLImageElement;
|
||||||
microphone: HTMLImageElement;
|
microphone: HTMLImageElement;
|
||||||
webrtcInAudio: HTMLAudioElement;
|
webrtcInAudio: HTMLAudioElement;
|
||||||
|
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||||
|
//mySoundMeterElement: HTMLDivElement;
|
||||||
private webrtcOutAudio: HTMLAudioElement;
|
private webrtcOutAudio: HTMLAudioElement;
|
||||||
constraintsMedia : MediaStreamConstraints = {
|
constraintsMedia : MediaStreamConstraints = {
|
||||||
audio: audioConstraint,
|
audio: audioConstraint,
|
||||||
@ -49,6 +55,8 @@ export class MediaManager {
|
|||||||
startScreenSharingCallBacks : Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>();
|
startScreenSharingCallBacks : Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>();
|
||||||
stopScreenSharingCallBacks : Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>();
|
stopScreenSharingCallBacks : Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>();
|
||||||
showReportModalCallBacks : Set<ShowReportCallBack> = new Set<ShowReportCallBack>();
|
showReportModalCallBacks : Set<ShowReportCallBack> = new Set<ShowReportCallBack>();
|
||||||
|
helpCameraSettingsCallBacks : Set<HelpCameraSettingsCallBack> = new Set<HelpCameraSettingsCallBack>();
|
||||||
|
|
||||||
private microphoneBtn: HTMLDivElement;
|
private microphoneBtn: HTMLDivElement;
|
||||||
private cinemaBtn: HTMLDivElement;
|
private cinemaBtn: HTMLDivElement;
|
||||||
private monitorBtn: HTMLDivElement;
|
private monitorBtn: HTMLDivElement;
|
||||||
@ -56,13 +64,17 @@ export class MediaManager {
|
|||||||
private previousConstraint : MediaStreamConstraints;
|
private previousConstraint : MediaStreamConstraints;
|
||||||
private focused : boolean = true;
|
private focused : boolean = true;
|
||||||
|
|
||||||
private lastUpdateScene : Date = new Date();
|
|
||||||
private setTimeOutlastUpdateScene? : NodeJS.Timeout;
|
|
||||||
|
|
||||||
private hasCamera = true;
|
private hasCamera = true;
|
||||||
|
|
||||||
private triggerCloseJistiFrame : Map<String, Function> = new Map<String, Function>();
|
private triggerCloseJistiFrame : Map<String, Function> = new Map<String, Function>();
|
||||||
|
|
||||||
|
private userInputManager?: UserInputManager;
|
||||||
|
|
||||||
|
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||||
|
/*private mySoundMeter?: SoundMeter|null;
|
||||||
|
private soundMeters: Map<string, SoundMeter> = new Map<string, SoundMeter>();
|
||||||
|
private soundMeterElements: Map<string, HTMLDivElement> = new Map<string, HTMLDivElement>();*/
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
||||||
this.myCamVideo = HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('myCamVideo');
|
this.myCamVideo = HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('myCamVideo');
|
||||||
@ -120,11 +132,19 @@ export class MediaManager {
|
|||||||
this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia));
|
this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia));
|
||||||
this.pingCameraStatus();
|
this.pingCameraStatus();
|
||||||
|
|
||||||
this.checkActiveUser(); //todo: desactivated in case of bug
|
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||||
|
/*this.mySoundMeterElement = (HtmlUtils.getElementByIdOrFail('mySoundMeter'));
|
||||||
|
this.mySoundMeterElement.childNodes.forEach((value: ChildNode, index) => {
|
||||||
|
this.mySoundMeterElement.children.item(index)?.classList.remove('active');
|
||||||
|
});*/
|
||||||
|
|
||||||
|
//Check of ask notification navigator permission
|
||||||
|
this.getNotification();
|
||||||
}
|
}
|
||||||
|
|
||||||
public setLastUpdateScene(){
|
public updateScene(){
|
||||||
this.lastUpdateScene = new Date();
|
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||||
|
//this.updateSoudMeter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public blurCamera() {
|
public blurCamera() {
|
||||||
@ -136,6 +156,13 @@ export class MediaManager {
|
|||||||
this.disableCamera();
|
this.disableCamera();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the constraint that the user wants (independently of the visibility / jitsi state...)
|
||||||
|
*/
|
||||||
|
public getConstraintRequestedByUser(): MediaStreamConstraints {
|
||||||
|
return this.previousConstraint ?? this.constraintsMedia;
|
||||||
|
}
|
||||||
|
|
||||||
public focusCamera() {
|
public focusCamera() {
|
||||||
if(this.focused){
|
if(this.focused){
|
||||||
return;
|
return;
|
||||||
@ -178,7 +205,7 @@ export class MediaManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public showGameOverlay(){
|
public showGameOverlay(): void {
|
||||||
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
|
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
|
||||||
gameOverlay.classList.add('active');
|
gameOverlay.classList.add('active');
|
||||||
|
|
||||||
@ -189,7 +216,7 @@ export class MediaManager {
|
|||||||
buttonCloseFrame.removeEventListener('click', functionTrigger);
|
buttonCloseFrame.removeEventListener('click', functionTrigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public hideGameOverlay(){
|
public hideGameOverlay(): void {
|
||||||
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
|
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
|
||||||
gameOverlay.classList.remove('active');
|
gameOverlay.classList.remove('active');
|
||||||
|
|
||||||
@ -200,6 +227,11 @@ export class MediaManager {
|
|||||||
buttonCloseFrame.addEventListener('click', functionTrigger);
|
buttonCloseFrame.addEventListener('click', functionTrigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isGameOverlayVisible(): boolean {
|
||||||
|
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
|
||||||
|
return gameOverlay.classList.contains('active');
|
||||||
|
}
|
||||||
|
|
||||||
public updateCameraQuality(value: number) {
|
public updateCameraQuality(value: number) {
|
||||||
this.enableCameraStyle();
|
this.enableCameraStyle();
|
||||||
const newVideoConstraint = JSON.parse(JSON.stringify(videoConstraint));
|
const newVideoConstraint = JSON.parse(JSON.stringify(videoConstraint));
|
||||||
@ -211,25 +243,32 @@ export class MediaManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public enableCamera() {
|
public async enableCamera() {
|
||||||
this.constraintsMedia.video = videoConstraint;
|
this.constraintsMedia.video = videoConstraint;
|
||||||
|
|
||||||
this.getCamera().then((stream: MediaStream) => {
|
try {
|
||||||
|
const stream = await this.getCamera()
|
||||||
//TODO show error message tooltip upper of camera button
|
//TODO show error message tooltip upper of camera button
|
||||||
//TODO message : please check camera permission of your navigator
|
//TODO message : please check camera permission of your navigator
|
||||||
if(stream.getVideoTracks().length === 0) {
|
if(stream.getVideoTracks().length === 0) {
|
||||||
throw Error('Video track is empty, please check camera permission of your navigator')
|
throw new Error('Video track is empty, please check camera permission of your navigator')
|
||||||
}
|
}
|
||||||
this.enableCameraStyle();
|
this.enableCameraStyle();
|
||||||
this.triggerUpdatedLocalStreamCallbacks(stream);
|
this.triggerUpdatedLocalStreamCallbacks(stream);
|
||||||
}).catch((err) => {
|
} catch(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
this.disableCameraStyle();
|
this.disableCameraStyle();
|
||||||
});
|
this.stopCamera();
|
||||||
|
|
||||||
|
layoutManager.addInformation('warning', 'Camera access denied. Click here and check navigators permissions.', () => {
|
||||||
|
this.showHelpCameraSettingsCallBack();
|
||||||
|
}, this.userInputManager);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async disableCamera() {
|
public async disableCamera() {
|
||||||
this.disableCameraStyle();
|
this.disableCameraStyle();
|
||||||
|
this.stopCamera();
|
||||||
|
|
||||||
if (this.constraintsMedia.audio !== false) {
|
if (this.constraintsMedia.audio !== false) {
|
||||||
const stream = await this.getCamera();
|
const stream = await this.getCamera();
|
||||||
@ -239,10 +278,12 @@ export class MediaManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enableMicrophone() {
|
public async enableMicrophone() {
|
||||||
this.constraintsMedia.audio = audioConstraint;
|
this.constraintsMedia.audio = audioConstraint;
|
||||||
|
|
||||||
this.getCamera().then((stream) => {
|
try {
|
||||||
|
const stream = await this.getCamera();
|
||||||
|
|
||||||
//TODO show error message tooltip upper of camera button
|
//TODO show error message tooltip upper of camera button
|
||||||
//TODO message : please check microphone permission of your navigator
|
//TODO message : please check microphone permission of your navigator
|
||||||
if (stream.getAudioTracks().length === 0) {
|
if (stream.getAudioTracks().length === 0) {
|
||||||
@ -250,10 +291,14 @@ export class MediaManager {
|
|||||||
}
|
}
|
||||||
this.enableMicrophoneStyle();
|
this.enableMicrophoneStyle();
|
||||||
this.triggerUpdatedLocalStreamCallbacks(stream);
|
this.triggerUpdatedLocalStreamCallbacks(stream);
|
||||||
}).catch((err) => {
|
} catch(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
this.disableMicrophoneStyle();
|
this.disableMicrophoneStyle();
|
||||||
});
|
|
||||||
|
layoutManager.addInformation('warning', 'Microphone access denied. Click here and check navigators permissions.', () => {
|
||||||
|
this.showHelpCameraSettingsCallBack();
|
||||||
|
}, this.userInputManager);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async disableMicrophone() {
|
public async disableMicrophone() {
|
||||||
@ -298,7 +343,6 @@ export class MediaManager {
|
|||||||
this.cinemaBtn.classList.add("disabled");
|
this.cinemaBtn.classList.add("disabled");
|
||||||
this.constraintsMedia.video = false;
|
this.constraintsMedia.video = false;
|
||||||
this.myCamVideo.srcObject = null;
|
this.myCamVideo.srcObject = null;
|
||||||
this.stopCamera();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enableMicrophoneStyle(){
|
private enableMicrophoneStyle(){
|
||||||
@ -324,6 +368,10 @@ export class MediaManager {
|
|||||||
this.monitorClose.style.display = "block";
|
this.monitorClose.style.display = "block";
|
||||||
this.monitor.style.display = "none";
|
this.monitor.style.display = "none";
|
||||||
this.monitorBtn.classList.remove("enabled");
|
this.monitorBtn.classList.remove("enabled");
|
||||||
|
|
||||||
|
layoutManager.addInformation('warning', 'Screen sharing access denied. Click here and check navigators permissions.', () => {
|
||||||
|
this.showHelpCameraSettingsCallBack();
|
||||||
|
}, this.userInputManager);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -402,13 +450,16 @@ export class MediaManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.getLocalStream().catch(() => {
|
return this.getLocalStream().catch((err) => {
|
||||||
console.info('Error get camera, trying with video option at null');
|
console.info('Error get camera, trying with video option at null =>', err);
|
||||||
this.disableCameraStyle();
|
this.disableCameraStyle();
|
||||||
|
this.stopCamera();
|
||||||
|
|
||||||
return this.getLocalStream().then((stream : MediaStream) => {
|
return this.getLocalStream().then((stream : MediaStream) => {
|
||||||
this.hasCamera = false;
|
this.hasCamera = false;
|
||||||
return stream;
|
return stream;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
this.disableMicrophoneStyle();
|
||||||
console.info("error get media ", this.constraintsMedia.video, this.constraintsMedia.audio, err);
|
console.info("error get media ", this.constraintsMedia.video, this.constraintsMedia.audio, err);
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
@ -425,6 +476,13 @@ export class MediaManager {
|
|||||||
return navigator.mediaDevices.getUserMedia(this.constraintsMedia).then((stream : MediaStream) => {
|
return navigator.mediaDevices.getUserMedia(this.constraintsMedia).then((stream : MediaStream) => {
|
||||||
this.localStream = stream;
|
this.localStream = stream;
|
||||||
this.myCamVideo.srcObject = this.localStream;
|
this.myCamVideo.srcObject = this.localStream;
|
||||||
|
|
||||||
|
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||||
|
/*this.mySoundMeter = null;
|
||||||
|
if(this.constraintsMedia.audio){
|
||||||
|
this.mySoundMeter = new SoundMeter();
|
||||||
|
this.mySoundMeter.connectToSource(stream, new AudioContext());
|
||||||
|
}*/
|
||||||
return stream;
|
return stream;
|
||||||
}).catch((err: Error) => {
|
}).catch((err: Error) => {
|
||||||
throw err;
|
throw err;
|
||||||
@ -451,6 +509,7 @@ export class MediaManager {
|
|||||||
track.stop();
|
track.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//this.mySoundMeter?.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
setCamera(id: string): Promise<MediaStream> {
|
setCamera(id: string): Promise<MediaStream> {
|
||||||
@ -496,6 +555,13 @@ export class MediaManager {
|
|||||||
</button>
|
</button>
|
||||||
<video id="${userId}" autoplay></video>
|
<video id="${userId}" autoplay></video>
|
||||||
<img src="resources/logos/blockSign.svg" id="blocking-${userId}" class="block-logo">
|
<img src="resources/logos/blockSign.svg" id="blocking-${userId}" class="block-logo">
|
||||||
|
<div id="soundMeter-${userId}" class="sound-progress">
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -585,6 +651,13 @@ export class MediaManager {
|
|||||||
throw `Unable to find video for ${userId}`;
|
throw `Unable to find video for ${userId}`;
|
||||||
}
|
}
|
||||||
remoteVideo.srcObject = stream;
|
remoteVideo.srcObject = stream;
|
||||||
|
|
||||||
|
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||||
|
//sound metter
|
||||||
|
/*const soundMeter = new SoundMeter();
|
||||||
|
soundMeter.connectToSource(stream, new AudioContext());
|
||||||
|
this.soundMeters.set(userId, soundMeter);
|
||||||
|
this.soundMeterElements.set(userId, HtmlUtils.getElementByIdOrFail<HTMLImageElement>('soundMeter-'+userId));*/
|
||||||
}
|
}
|
||||||
addStreamRemoteScreenSharing(userId: string, stream : MediaStream){
|
addStreamRemoteScreenSharing(userId: string, stream : MediaStream){
|
||||||
// In the case of screen sharing (going both ways), we may need to create the HTML element if it does not exist yet
|
// In the case of screen sharing (going both ways), we may need to create the HTML element if it does not exist yet
|
||||||
@ -600,6 +673,11 @@ export class MediaManager {
|
|||||||
layoutManager.remove(userId);
|
layoutManager.remove(userId);
|
||||||
this.remoteVideo.delete(userId);
|
this.remoteVideo.delete(userId);
|
||||||
|
|
||||||
|
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||||
|
/*this.soundMeters.get(userId)?.stop();
|
||||||
|
this.soundMeters.delete(userId);
|
||||||
|
this.soundMeterElements.delete(userId);*/
|
||||||
|
|
||||||
//permit to remove user in discussion part
|
//permit to remove user in discussion part
|
||||||
this.removeParticipant(userId);
|
this.removeParticipant(userId);
|
||||||
}
|
}
|
||||||
@ -717,28 +795,91 @@ export class MediaManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setUserInputManager(userInputManager : UserInputManager){
|
public setUserInputManager(userInputManager : UserInputManager){
|
||||||
|
this.userInputManager = userInputManager;
|
||||||
discussionManager.setUserInputManager(userInputManager);
|
discussionManager.setUserInputManager(userInputManager);
|
||||||
}
|
}
|
||||||
//check if user is active
|
|
||||||
private checkActiveUser(){
|
|
||||||
if(this.setTimeOutlastUpdateScene){
|
|
||||||
clearTimeout(this.setTimeOutlastUpdateScene);
|
|
||||||
}
|
|
||||||
this.setTimeOutlastUpdateScene = setTimeout(() => {
|
|
||||||
const now = new Date();
|
|
||||||
//if last update is more of 10 sec
|
|
||||||
if( (now.getTime() - this.lastUpdateScene.getTime()) > 10000) {
|
|
||||||
this.blurCamera();
|
|
||||||
}else{
|
|
||||||
this.focusCamera();
|
|
||||||
}
|
|
||||||
this.checkActiveUser();
|
|
||||||
}, this.focused ? 10000 : 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
public setShowReportModalCallBacks(callback: ShowReportCallBack){
|
public setShowReportModalCallBacks(callback: ShowReportCallBack){
|
||||||
this.showReportModalCallBacks.add(callback);
|
this.showReportModalCallBacks.add(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setHelpCameraSettingsCallBack(callback: HelpCameraSettingsCallBack){
|
||||||
|
this.helpCameraSettingsCallBacks.add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private showHelpCameraSettingsCallBack(){
|
||||||
|
for(const callBack of this.helpCameraSettingsCallBacks){
|
||||||
|
callBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||||
|
/*updateSoudMeter(){
|
||||||
|
try{
|
||||||
|
const volume = parseInt(((this.mySoundMeter ? this.mySoundMeter.getVolume() : 0) / 10).toFixed(0));
|
||||||
|
this.setVolumeSoundMeter(volume, this.mySoundMeterElement);
|
||||||
|
|
||||||
|
for(const indexUserId of this.soundMeters.keys()){
|
||||||
|
const soundMeter = this.soundMeters.get(indexUserId);
|
||||||
|
const soundMeterElement = this.soundMeterElements.get(indexUserId);
|
||||||
|
if(!soundMeter || !soundMeterElement){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const volumeByUser = parseInt((soundMeter.getVolume() / 10).toFixed(0));
|
||||||
|
this.setVolumeSoundMeter(volumeByUser, soundMeterElement);
|
||||||
|
}
|
||||||
|
}catch(err){
|
||||||
|
//console.error(err);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
private setVolumeSoundMeter(volume: number, element: HTMLDivElement){
|
||||||
|
if(volume <= 0 && !element.classList.contains('active')){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
element.classList.remove('active');
|
||||||
|
if(volume <= 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
element.classList.add('active');
|
||||||
|
element.childNodes.forEach((value: ChildNode, index) => {
|
||||||
|
const elementChildre = element.children.item(index);
|
||||||
|
if(!elementChildre){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
elementChildre.classList.remove('active');
|
||||||
|
if((index +1) > volume){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
elementChildre.classList.add('active');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNotification(){
|
||||||
|
//Get notification
|
||||||
|
if (!DISABLE_NOTIFICATIONS && window.Notification && Notification.permission !== "granted") {
|
||||||
|
Notification.requestPermission().catch((err) => {
|
||||||
|
console.error(`Notification permission error`, err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public createNotification(userName: string){
|
||||||
|
if(this.focused){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (window.Notification && Notification.permission === "granted") {
|
||||||
|
const title = 'WorkAdventure';
|
||||||
|
const options = {
|
||||||
|
body: `Hi! ${userName} wants to discuss with you, don't be afraid!`,
|
||||||
|
icon: '/resources/logos/logo-WA-min.png',
|
||||||
|
image: '/resources/logos/logo-WA-min.png',
|
||||||
|
badge: '/resources/logos/logo-WA-min.png',
|
||||||
|
};
|
||||||
|
new Notification(title, options);
|
||||||
|
//new Notification(`Hi! ${userName} wants to discuss with you, don't be afraid!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mediaManager = new MediaManager();
|
export const mediaManager = new MediaManager();
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import * as SimplePeerNamespace from "simple-peer";
|
import type * as SimplePeerNamespace from "simple-peer";
|
||||||
import {mediaManager} from "./MediaManager";
|
import {mediaManager} from "./MediaManager";
|
||||||
import {STUN_SERVER, TURN_SERVER, TURN_USER, TURN_PASSWORD} from "../Enum/EnvironmentVariable";
|
import {STUN_SERVER, TURN_SERVER, TURN_USER, TURN_PASSWORD} from "../Enum/EnvironmentVariable";
|
||||||
import {RoomConnection} from "../Connexion/RoomConnection";
|
import type {RoomConnection} from "../Connexion/RoomConnection";
|
||||||
import {MESSAGE_TYPE_CONSTRAINT} from "./VideoPeer";
|
import {MESSAGE_TYPE_CONSTRAINT} from "./VideoPeer";
|
||||||
import {UserSimplePeerInterface} from "./SimplePeer";
|
import type {UserSimplePeerInterface} from "./SimplePeer";
|
||||||
|
|
||||||
const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');
|
const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ export class ScreenSharingPeer extends Peer {
|
|||||||
constructor(user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection) {
|
constructor(user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection) {
|
||||||
super({
|
super({
|
||||||
initiator: initiator ? initiator : false,
|
initiator: initiator ? initiator : false,
|
||||||
reconnectTimer: 10000,
|
//reconnectTimer: 10000,
|
||||||
config: {
|
config: {
|
||||||
iceServers: [
|
iceServers: [
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {
|
import type {
|
||||||
WebRtcDisconnectMessageInterface,
|
WebRtcDisconnectMessageInterface,
|
||||||
WebRtcSignalReceivedMessageInterface,
|
WebRtcSignalReceivedMessageInterface,
|
||||||
} from "../Connexion/ConnexionModels";
|
} from "../Connexion/ConnexionModels";
|
||||||
@ -10,7 +10,7 @@ import {
|
|||||||
} from "./MediaManager";
|
} from "./MediaManager";
|
||||||
import {ScreenSharingPeer} from "./ScreenSharingPeer";
|
import {ScreenSharingPeer} from "./ScreenSharingPeer";
|
||||||
import {MESSAGE_TYPE_BLOCKED, MESSAGE_TYPE_CONSTRAINT, MESSAGE_TYPE_MESSAGE, VideoPeer} from "./VideoPeer";
|
import {MESSAGE_TYPE_BLOCKED, MESSAGE_TYPE_CONSTRAINT, MESSAGE_TYPE_MESSAGE, VideoPeer} from "./VideoPeer";
|
||||||
import {RoomConnection} from "../Connexion/RoomConnection";
|
import type {RoomConnection} from "../Connexion/RoomConnection";
|
||||||
import {connectionManager} from "../Connexion/ConnectionManager";
|
import {connectionManager} from "../Connexion/ConnectionManager";
|
||||||
import {GameConnexionTypes} from "../Url/UrlManager";
|
import {GameConnexionTypes} from "../Url/UrlManager";
|
||||||
import {blackListManager} from "./BlackListManager";
|
import {blackListManager} from "./BlackListManager";
|
||||||
@ -82,15 +82,11 @@ export class SimplePeer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
mediaManager.showGameOverlay();
|
mediaManager.showGameOverlay();
|
||||||
mediaManager.getCamera().then(() => {
|
mediaManager.getCamera().finally(() => {
|
||||||
|
|
||||||
//receive message start
|
//receive message start
|
||||||
this.Connection.receiveWebrtcStart((message: UserSimplePeerInterface) => {
|
this.Connection.receiveWebrtcStart((message: UserSimplePeerInterface) => {
|
||||||
this.receiveWebrtcStart(message);
|
this.receiveWebrtcStart(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
}).catch((err) => {
|
|
||||||
console.error("err", err);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Connection.disconnectMessage((data: WebRtcDisconnectMessageInterface): void => {
|
this.Connection.disconnectMessage((data: WebRtcDisconnectMessageInterface): void => {
|
||||||
@ -162,6 +158,11 @@ export class SimplePeer {
|
|||||||
this.sendLocalScreenSharingStreamToUser(user.userId);
|
this.sendLocalScreenSharingStreamToUser(user.userId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Create a notification for first user in circle discussion
|
||||||
|
if(this.PeerConnectionArray.size === 0){
|
||||||
|
mediaManager.createNotification(user.name??'');
|
||||||
|
}
|
||||||
this.PeerConnectionArray.set(user.userId, peer);
|
this.PeerConnectionArray.set(user.userId, peer);
|
||||||
|
|
||||||
for (const peerConnectionListener of this.peerConnectionListeners) {
|
for (const peerConnectionListener of this.peerConnectionListeners) {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import * as SimplePeerNamespace from "simple-peer";
|
import type * as SimplePeerNamespace from "simple-peer";
|
||||||
import {mediaManager} from "./MediaManager";
|
import {mediaManager} from "./MediaManager";
|
||||||
import {STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER} from "../Enum/EnvironmentVariable";
|
import {STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER} from "../Enum/EnvironmentVariable";
|
||||||
import {RoomConnection} from "../Connexion/RoomConnection";
|
import type {RoomConnection} from "../Connexion/RoomConnection";
|
||||||
import {blackListManager} from "./BlackListManager";
|
import {blackListManager} from "./BlackListManager";
|
||||||
import {Subscription} from "rxjs";
|
import type {Subscription} from "rxjs";
|
||||||
import {UserSimplePeerInterface} from "./SimplePeer";
|
import type {UserSimplePeerInterface} from "./SimplePeer";
|
||||||
|
|
||||||
const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');
|
const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ export class VideoPeer extends Peer {
|
|||||||
constructor(public user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection) {
|
constructor(public user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection) {
|
||||||
super({
|
super({
|
||||||
initiator: initiator ? initiator : false,
|
initiator: initiator ? initiator : false,
|
||||||
reconnectTimer: 10000,
|
//reconnectTimer: 10000,
|
||||||
config: {
|
config: {
|
||||||
iceServers: [
|
iceServers: [
|
||||||
{
|
{
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import {ChatEvent, isChatEvent} from "./Api/Events/ChatEvent";
|
import type { ChatEvent } from "./Api/Events/ChatEvent";
|
||||||
import {isIframeEventWrapper} from "./Api/Events/IframeEvent";
|
import { isIframeResponseEventWrapper } from "./Api/Events/IframeEvent";
|
||||||
import { isUserInputChatEvent, UserInputChatEvent } from "./Api/Events/UserInputChatEvent";
|
import { isUserInputChatEvent, UserInputChatEvent } from "./Api/Events/UserInputChatEvent";
|
||||||
import { Subject } from "rxjs";
|
import { Subject } from "rxjs";
|
||||||
import { EnterLeaveEvent, isEnterLeaveEvent } from "./Api/Events/EnterLeaveEvent";
|
import { EnterLeaveEvent, isEnterLeaveEvent } from "./Api/Events/EnterLeaveEvent";
|
||||||
import {OpenPopupEvent} from "./Api/Events/OpenPopupEvent";
|
import type { OpenPopupEvent } from "./Api/Events/OpenPopupEvent";
|
||||||
import { isButtonClickedEvent } from "./Api/Events/ButtonClickedEvent";
|
import { isButtonClickedEvent } from "./Api/Events/ButtonClickedEvent";
|
||||||
import {ClosePopupEvent, isClosePopupEvent} from "./Api/Events/ClosePopupEvent";
|
import type { ClosePopupEvent } from "./Api/Events/ClosePopupEvent";
|
||||||
import {OpenTabEvent} from "./Api/Events/OpenTabEvent";
|
import type { OpenTabEvent } from "./Api/Events/OpenTabEvent";
|
||||||
import {GoToPageEvent} from "./Api/Events/GoToPageEvent";
|
import type { GoToPageEvent } from "./Api/Events/GoToPageEvent";
|
||||||
import {OpenCoWebSiteEvent} from "./Api/Events/OpenCoWebSiteEvent";
|
import type { OpenCoWebSiteEvent } from "./Api/Events/OpenCoWebSiteEvent";
|
||||||
import {ExitUrlEvent} from "./Api/Events/ExitUrlEvent";
|
import {ExitUrlEvent} from "./Api/Events/ExitUrlEvent";
|
||||||
|
|
||||||
interface WorkAdventureApi {
|
interface WorkAdventureApi {
|
||||||
@ -22,8 +22,8 @@ interface WorkAdventureApi {
|
|||||||
goToPage(url: string): void;
|
goToPage(url: string): void;
|
||||||
openCoWebSite(url: string): void;
|
openCoWebSite(url: string): void;
|
||||||
closeCoWebSite(): void;
|
closeCoWebSite(): void;
|
||||||
disablePlayerControl(): void;
|
disablePlayerControls(): void;
|
||||||
restorePlayerControl(): void;
|
restorePlayerControls(): void;
|
||||||
displayBubble(): void;
|
displayBubble(): void;
|
||||||
removeBubble(): void;
|
removeBubble(): void;
|
||||||
exitUrl(url : string) : void;
|
exitUrl(url : string) : void;
|
||||||
@ -100,12 +100,12 @@ window.WA = {
|
|||||||
"type": 'closeChatMessage'
|
"type": 'closeChatMessage'
|
||||||
}, '*');
|
}, '*');
|
||||||
},
|
},
|
||||||
disablePlayerControl(): void {
|
disablePlayerControls(): void {
|
||||||
window.parent.postMessage({'type': 'disablePlayerControl'}, '*');
|
window.parent.postMessage({ 'type': 'disablePlayerControls' }, '*');
|
||||||
},
|
},
|
||||||
|
|
||||||
restorePlayerControl(): void {
|
restorePlayerControls(): void {
|
||||||
window.parent.postMessage({'type': 'restorePlayerControl'}, '*');
|
window.parent.postMessage({ 'type': 'restorePlayerControls' }, '*');
|
||||||
},
|
},
|
||||||
|
|
||||||
displayBubble(): void {
|
displayBubble(): void {
|
||||||
@ -166,7 +166,7 @@ window.WA = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(input) {
|
if(input) {
|
||||||
this.disablePlayerControl();
|
this.disablePlayerControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.parent.postMessage({
|
window.parent.postMessage({
|
||||||
@ -229,9 +229,9 @@ window.addEventListener('message', message => {
|
|||||||
|
|
||||||
const payload = message.data;
|
const payload = message.data;
|
||||||
|
|
||||||
console.log(payload);
|
console.debug(payload);
|
||||||
|
|
||||||
if(isIframeEventWrapper(payload)) {
|
if (isIframeResponseEventWrapper(payload)) {
|
||||||
const payloadData = payload.data;
|
const payloadData = payload.data;
|
||||||
if (payload.type === 'userInputChat' && isUserInputChatEvent(payloadData)) {
|
if (payload.type === 'userInputChat' && isUserInputChatEvent(payloadData)) {
|
||||||
userInputChatStream.next(payloadData);
|
userInputChatStream.next(payloadData);
|
||||||
|
@ -2,7 +2,7 @@ import 'phaser';
|
|||||||
import GameConfig = Phaser.Types.Core.GameConfig;
|
import GameConfig = Phaser.Types.Core.GameConfig;
|
||||||
import "../dist/resources/style/index.scss";
|
import "../dist/resources/style/index.scss";
|
||||||
|
|
||||||
import {DEBUG_MODE, RESOLUTION} from "./Enum/EnvironmentVariable";
|
import {DEBUG_MODE, isMobile} from "./Enum/EnvironmentVariable";
|
||||||
import {LoginScene} from "./Phaser/Login/LoginScene";
|
import {LoginScene} from "./Phaser/Login/LoginScene";
|
||||||
import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene";
|
import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene";
|
||||||
import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene";
|
import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene";
|
||||||
@ -17,6 +17,10 @@ import {HelpCameraSettingsScene} from "./Phaser/Menu/HelpCameraSettingsScene";
|
|||||||
import {localUserStore} from "./Connexion/LocalUserStore";
|
import {localUserStore} from "./Connexion/LocalUserStore";
|
||||||
import {ErrorScene} from "./Phaser/Reconnecting/ErrorScene";
|
import {ErrorScene} from "./Phaser/Reconnecting/ErrorScene";
|
||||||
import {iframeListener} from "./Api/IframeListener";
|
import {iframeListener} from "./Api/IframeListener";
|
||||||
|
import { SelectCharacterMobileScene } from './Phaser/Login/SelectCharacterMobileScene';
|
||||||
|
import {HdpiManager} from "./Phaser/Services/HdpiManager";
|
||||||
|
import {waScaleManager} from "./Phaser/Services/WaScaleManager";
|
||||||
|
import {Game} from "./Phaser/Game/Game";
|
||||||
|
|
||||||
const {width, height} = coWebsiteManager.getGameSize();
|
const {width, height} = coWebsiteManager.getGameSize();
|
||||||
|
|
||||||
@ -67,15 +71,23 @@ switch (phaserMode) {
|
|||||||
throw new Error('phaserMode parameter must be one of "auto", "canvas" or "webgl"');
|
throw new Error('phaserMode parameter must be one of "auto", "canvas" or "webgl"');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hdpiManager = new HdpiManager(640*480, 196*196);
|
||||||
|
const { game: gameSize, real: realSize } = hdpiManager.getOptimalGameSize({width, height});
|
||||||
|
|
||||||
const config: GameConfig = {
|
const config: GameConfig = {
|
||||||
type: mode,
|
type: mode,
|
||||||
title: "WorkAdventure",
|
title: "WorkAdventure",
|
||||||
width: width / RESOLUTION,
|
scale: {
|
||||||
height: height / RESOLUTION,
|
|
||||||
parent: "game",
|
parent: "game",
|
||||||
|
width: gameSize.width,
|
||||||
|
height: gameSize.height,
|
||||||
|
zoom: realSize.width / gameSize.width,
|
||||||
|
autoRound: true,
|
||||||
|
resizeInterval: 999999999999
|
||||||
|
},
|
||||||
scene: [EntryScene,
|
scene: [EntryScene,
|
||||||
LoginScene,
|
LoginScene,
|
||||||
SelectCharacterScene,
|
isMobile() ? SelectCharacterMobileScene : SelectCharacterScene,
|
||||||
SelectCompanionScene,
|
SelectCompanionScene,
|
||||||
EnableCameraScene,
|
EnableCameraScene,
|
||||||
ReconnectingScene,
|
ReconnectingScene,
|
||||||
@ -83,7 +95,7 @@ const config: GameConfig = {
|
|||||||
CustomizeScene,
|
CustomizeScene,
|
||||||
MenuScene,
|
MenuScene,
|
||||||
HelpCameraSettingsScene],
|
HelpCameraSettingsScene],
|
||||||
zoom: RESOLUTION,
|
//resolution: window.devicePixelRatio / 2,
|
||||||
fps: fps,
|
fps: fps,
|
||||||
dom: {
|
dom: {
|
||||||
createContainer: true
|
createContainer: true
|
||||||
@ -99,6 +111,8 @@ const config: GameConfig = {
|
|||||||
debug: DEBUG_MODE,
|
debug: DEBUG_MODE,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// Instruct systems with 2 GPU to choose the low power one. We don't need that extra power and we want to save battery
|
||||||
|
powerPreference: "low-power",
|
||||||
callbacks: {
|
callbacks: {
|
||||||
postBoot: game => {
|
postBoot: game => {
|
||||||
// Commented out to try to fix MacOS bug
|
// Commented out to try to fix MacOS bug
|
||||||
@ -110,12 +124,15 @@ const config: GameConfig = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const game = new Phaser.Game(config);
|
//const game = new Phaser.Game(config);
|
||||||
|
const game = new Game(config);
|
||||||
|
|
||||||
|
waScaleManager.setScaleManager(game.scale);
|
||||||
|
|
||||||
window.addEventListener('resize', function (event) {
|
window.addEventListener('resize', function (event) {
|
||||||
coWebsiteManager.resetStyle();
|
coWebsiteManager.resetStyle();
|
||||||
const {width, height} = coWebsiteManager.getGameSize();
|
|
||||||
game.scale.resize(width / RESOLUTION, height / RESOLUTION);
|
waScaleManager.applyNewSize();
|
||||||
|
|
||||||
// Let's trigger the onResize method of any active scene that is a ResizableScene
|
// Let's trigger the onResize method of any active scene that is a ResizableScene
|
||||||
for (const scene of game.scene.getScenes(true)) {
|
for (const scene of game.scene.getScenes(true)) {
|
||||||
@ -126,8 +143,7 @@ window.addEventListener('resize', function (event) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
coWebsiteManager.onResize.subscribe(() => {
|
coWebsiteManager.onResize.subscribe(() => {
|
||||||
const {width, height} = coWebsiteManager.getGameSize();
|
waScaleManager.applyNewSize();
|
||||||
game.scale.resize(width / RESOLUTION, height / RESOLUTION);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
iframeListener.init();
|
iframeListener.init();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import Phaser from "phaser";
|
import type Phaser from "phaser";
|
||||||
|
|
||||||
export type CursorKey = {
|
export type CursorKey = {
|
||||||
isDown: boolean
|
isDown: boolean
|
||||||
|
@ -19,9 +19,6 @@ describe("isUserNameValid()", () => {
|
|||||||
it("should not validate spaces", () => {
|
it("should not validate spaces", () => {
|
||||||
expect(isUserNameValid(' ')).toBe(false);
|
expect(isUserNameValid(' ')).toBe(false);
|
||||||
});
|
});
|
||||||
it("should not validate numbers", () => {
|
|
||||||
expect(isUserNameValid('a12')).toBe(false);
|
|
||||||
});
|
|
||||||
it("should not validate special characters", () => {
|
it("should not validate special characters", () => {
|
||||||
expect(isUserNameValid('a&-')).toBe(false);
|
expect(isUserNameValid('a&-')).toBe(false);
|
||||||
});
|
});
|
||||||
|
55
front/tests/Phaser/Services/HdpiManagerTest.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import "jasmine";
|
||||||
|
import {HdpiManager} from "../../../src/Phaser/Services/HdpiManager";
|
||||||
|
|
||||||
|
describe("Test HdpiManager", () => {
|
||||||
|
it("should match screen size if size is too small.", () => {
|
||||||
|
const hdpiManager = new HdpiManager(640*480, 64*64);
|
||||||
|
|
||||||
|
const result = hdpiManager.getOptimalGameSize({ width: 320, height: 200 });
|
||||||
|
expect(result.game.width).toEqual(320);
|
||||||
|
expect(result.game.height).toEqual(200);
|
||||||
|
expect(result.real.width).toEqual(320);
|
||||||
|
expect(result.real.height).toEqual(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should match multiple just above.", () => {
|
||||||
|
const hdpiManager = new HdpiManager(640*480, 64*64);
|
||||||
|
|
||||||
|
let result = hdpiManager.getOptimalGameSize({ width: 960, height: 600 });
|
||||||
|
expect(result.game.width).toEqual(960);
|
||||||
|
expect(result.game.height).toEqual(600);
|
||||||
|
|
||||||
|
result = hdpiManager.getOptimalGameSize({ width: 640 * 2 + 50, height: 480 * 2 + 50 });
|
||||||
|
expect(result.game.width).toEqual(Math.ceil((640 * 2 + 50) / 2));
|
||||||
|
expect(result.game.height).toEqual((480 * 2 + 50) / 2);
|
||||||
|
|
||||||
|
result = hdpiManager.getOptimalGameSize({ width: 640 * 3 + 50, height: 480 * 3 + 50 });
|
||||||
|
expect(result.game.width).toEqual(Math.ceil((640 * 3 + 50) / 3));
|
||||||
|
expect(result.game.height).toEqual(Math.ceil((480 * 3 + 50) / 3));
|
||||||
|
expect(result.real.width).toEqual(result.game.width * 3);
|
||||||
|
expect(result.real.height).toEqual(result.game.height * 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not zoom in too much.", () => {
|
||||||
|
const hdpiManager = new HdpiManager(640*480, 64*64);
|
||||||
|
|
||||||
|
hdpiManager.zoomModifier = 11;
|
||||||
|
|
||||||
|
const result = hdpiManager.getOptimalGameSize({ width: 640, height: 640 });
|
||||||
|
expect(result.game.width).toEqual(64);
|
||||||
|
expect(result.game.height).toEqual(64);
|
||||||
|
expect(hdpiManager.zoomModifier).toEqual(10);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not zoom out too much.", () => {
|
||||||
|
const hdpiManager = new HdpiManager(640*480, 64*64);
|
||||||
|
|
||||||
|
hdpiManager.zoomModifier = 1/10;
|
||||||
|
|
||||||
|
const result = hdpiManager.getOptimalGameSize({ width: 1280, height: 768 });
|
||||||
|
expect(result.game.width).toEqual(1280);
|
||||||
|
expect(result.game.height).toEqual(768);
|
||||||
|
expect(hdpiManager.zoomModifier).toEqual(2 / 3);
|
||||||
|
});
|
||||||
|
});
|
@ -4,13 +4,15 @@
|
|||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"module": "CommonJS",
|
"module": "CommonJS",
|
||||||
"target": "ES2015",
|
"target": "ES2017",
|
||||||
"declaration": false,
|
"declaration": false,
|
||||||
"downlevelIteration": true,
|
"downlevelIteration": true,
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
|
|
||||||
|
"importsNotUsedAsValues": "preserve",
|
||||||
|
|
||||||
"strict": true, /* Enable all strict type-checking options. */
|
"strict": true, /* Enable all strict type-checking options. */
|
||||||
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||||
"strictNullChecks": true, /* Enable strict null checks. */
|
"strictNullChecks": true, /* Enable strict null checks. */
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
|
import type {Configuration} from "webpack";
|
||||||
|
import type WebpackDevServer from "webpack-dev-server";
|
||||||
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin")
|
||||||
|
|
||||||
|
const mode = process.env.NODE_ENV ?? 'development';
|
||||||
|
const isProduction = mode === 'production';
|
||||||
|
const isDevelopment = !isProduction;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: {
|
entry: {
|
||||||
'main': './src/index.ts',
|
'main': './src/index.ts',
|
||||||
'iframe_api': './src/iframe_api.ts'
|
'iframe_api': './src/iframe_api.ts'
|
||||||
},
|
},
|
||||||
devtool: 'inline-source-map',
|
mode: mode,
|
||||||
|
devtool: isDevelopment ? 'inline-source-map' : 'source-map',
|
||||||
devServer: {
|
devServer: {
|
||||||
contentBase: './dist',
|
contentBase: './dist',
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
@ -41,14 +50,14 @@ module.exports = {
|
|||||||
filename: (pathData) => {
|
filename: (pathData) => {
|
||||||
// Add a content hash only for the main bundle.
|
// Add a content hash only for the main bundle.
|
||||||
// We want the iframe_api.js file to keep its name as it will be referenced from outside iframes.
|
// We want the iframe_api.js file to keep its name as it will be referenced from outside iframes.
|
||||||
return pathData.chunk.name === 'main' ? 'js/[name].[contenthash].js': '[name].js';
|
return pathData.chunk?.name === 'main' ? 'js/[name].[contenthash].js': '[name].js';
|
||||||
},
|
},
|
||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'dist'),
|
||||||
publicPath: '/'
|
publicPath: '/'
|
||||||
},
|
},
|
||||||
externals:[
|
/*externals:[
|
||||||
require('webpack-require-http')
|
require('webpack-require-http')
|
||||||
],
|
],*/
|
||||||
plugins: [
|
plugins: [
|
||||||
new MiniCssExtractPlugin({filename: 'style.[contenthash].css'}),
|
new MiniCssExtractPlugin({filename: 'style.[contenthash].css'}),
|
||||||
new HtmlWebpackPlugin(
|
new HtmlWebpackPlugin(
|
||||||
@ -69,8 +78,11 @@ module.exports = {
|
|||||||
new webpack.ProvidePlugin({
|
new webpack.ProvidePlugin({
|
||||||
Phaser: 'phaser'
|
Phaser: 'phaser'
|
||||||
}),
|
}),
|
||||||
|
new NodePolyfillPlugin(),
|
||||||
new webpack.EnvironmentPlugin({
|
new webpack.EnvironmentPlugin({
|
||||||
'API_URL': null,
|
'API_URL': null,
|
||||||
|
'SKIP_RENDER_OPTIMIZATIONS': false,
|
||||||
|
'DISABLE_NOTIFICATIONS': false,
|
||||||
'PUSHER_URL': undefined,
|
'PUSHER_URL': undefined,
|
||||||
'UPLOADER_URL': null,
|
'UPLOADER_URL': null,
|
||||||
'ADMIN_URL': null,
|
'ADMIN_URL': null,
|
||||||
@ -81,8 +93,10 @@ module.exports = {
|
|||||||
'TURN_PASSWORD': null,
|
'TURN_PASSWORD': null,
|
||||||
'JITSI_URL': null,
|
'JITSI_URL': null,
|
||||||
'JITSI_PRIVATE_MODE': null,
|
'JITSI_PRIVATE_MODE': null,
|
||||||
'START_ROOM_URL': null
|
'START_ROOM_URL': null,
|
||||||
|
'MAX_USERNAME_LENGTH': 8,
|
||||||
|
'MAX_PER_GROUP': 4
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
|
|
||||||
};
|
} as Configuration & WebpackDevServer.Configuration;
|
@ -1,7 +0,0 @@
|
|||||||
const merge = require('webpack-merge');
|
|
||||||
const common = require('./webpack.config.js');
|
|
||||||
|
|
||||||
module.exports = merge(common, {
|
|
||||||
mode: 'production',
|
|
||||||
devtool: 'source-map'
|
|
||||||
});
|
|
3341
front/yarn.lock
@ -25,7 +25,7 @@ function launchTuto (){
|
|||||||
label: "Got it!",
|
label: "Got it!",
|
||||||
className : "success",callback:(popup2 => {
|
className : "success",callback:(popup2 => {
|
||||||
popup2.close();
|
popup2.close();
|
||||||
WA.restorePlayerControl();
|
WA.restorePlayerControls();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
@ -36,7 +36,7 @@ function launchTuto (){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
WA.disablePlayerControl();
|
WA.disablePlayerControls();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
{
|
{
|
||||||
"name":"exitUrl",
|
"name":"exitUrl",
|
||||||
"type":"string",
|
"type":"string",
|
||||||
"value":"\/@\/tcm\/workadventure\/floor0"
|
"value":"\/@\/tcm\/workadventure\/wa-village"
|
||||||
}],
|
}],
|
||||||
"type":"tilelayer",
|
"type":"tilelayer",
|
||||||
"visible":true,
|
"visible":true,
|
||||||
|
Before Width: | Height: | Size: 3.8 KiB |
BIN
maps/Village/logo-WA.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
BIN
maps/Village/sol_intérieur.png
Normal file
After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 3.3 KiB |