Merge branch 'develop' into load-page-api
25
.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:
|
||||||
|
release:
|
||||||
|
types: [created]
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [ labeled, synchronize ]
|
types: [ labeled, synchronize ]
|
||||||
|
|
||||||
@ -12,7 +14,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build-front:
|
build-front:
|
||||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
if: ${{ github.event.release || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -32,11 +34,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: ${{ env.GITHUB_HEAD_REF_SLUG }}
|
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||||
add_git_labels: true
|
add_git_labels: true
|
||||||
|
|
||||||
build-back:
|
build-back:
|
||||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
if: ${{ github.event.release || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -55,11 +57,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: ${{ env.GITHUB_HEAD_REF_SLUG }}
|
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||||
add_git_labels: true
|
add_git_labels: true
|
||||||
|
|
||||||
build-pusher:
|
build-pusher:
|
||||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
if: ${{ github.event.release || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -78,11 +80,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: ${{ env.GITHUB_HEAD_REF_SLUG }}
|
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||||
add_git_labels: true
|
add_git_labels: true
|
||||||
|
|
||||||
build-uploader:
|
build-uploader:
|
||||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
if: ${{ github.event.release || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -101,11 +103,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: ${{ env.GITHUB_HEAD_REF_SLUG }}
|
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||||
add_git_labels: true
|
add_git_labels: true
|
||||||
|
|
||||||
build-maps:
|
build-maps:
|
||||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
if: ${{ github.event.release || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -125,7 +127,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: ${{ env.GITHUB_HEAD_REF_SLUG }}
|
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||||
add_git_labels: true
|
add_git_labels: true
|
||||||
|
|
||||||
deeploy:
|
deeploy:
|
||||||
@ -136,6 +138,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') }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@ -153,12 +156,12 @@ 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 }}
|
||||||
with:
|
with:
|
||||||
namespace: workadventure-${{ env.GITHUB_HEAD_REF_SLUG }}
|
namespace: workadventure-${{ env.GITHUB_HEAD_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: ${{ env.GITHUB_HEAD_REF_SLUG != 'master' }}
|
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
|
1
.github/workflows/cleanup.yml
vendored
@ -18,6 +18,7 @@ jobs:
|
|||||||
- uses: rlespinasse/github-slug-action@3.1.0
|
- uses: rlespinasse/github-slug-action@3.1.0
|
||||||
|
|
||||||
- name: Cleanup
|
- name: Cleanup
|
||||||
|
continue-on-error: true
|
||||||
uses: thecodingmachine/deeployer-cleanup-action@master
|
uses: thecodingmachine/deeployer-cleanup-action@master
|
||||||
env:
|
env:
|
||||||
KUBE_CONFIG_FILE: ${{ secrets.KUBE_CONFIG_FILE }}
|
KUBE_CONFIG_FILE: ${{ secrets.KUBE_CONFIG_FILE }}
|
||||||
|
16
CHANGELOG.md
@ -1,4 +1,18 @@
|
|||||||
## Version 1.3.0 - in dev
|
## Version 1.3.9 - in dev
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
local env = std.extVar("env"),
|
local env = std.extVar("env"),
|
||||||
local namespace = env.GITHUB_HEAD_REF_SLUG,
|
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 = if namespace == "master" then "workadventu.re" else 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.
|
||||||
|
@ -153,23 +153,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
|
||||||
|
19
front/dist/index.tmpl.html
vendored
@ -48,20 +48,27 @@
|
|||||||
<div id="activeCam" class="activeCam">
|
<div id="activeCam" class="activeCam">
|
||||||
<div id="div-myCamVideo" class="video-container">
|
<div id="div-myCamVideo" class="video-container">
|
||||||
<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">
|
||||||
<div id="btn-micro" class="btn-micro">
|
<div id="btn-monitor" class="btn-monitor">
|
||||||
<img id="microphone" src="resources/logos/microphone.svg">
|
<img id="monitor" src="resources/logos/monitor.svg">
|
||||||
<img id="microphone-close" src="resources/logos/microphone-close.svg">
|
<img id="monitor-close" src="resources/logos/monitor-close.svg">
|
||||||
</div>
|
</div>
|
||||||
<div id="btn-video" class="btn-video">
|
<div id="btn-video" class="btn-video">
|
||||||
<img id="cinema" src="resources/logos/cinema.svg">
|
<img id="cinema" src="resources/logos/cinema.svg">
|
||||||
<img id="cinema-close" src="resources/logos/cinema-close.svg">
|
<img id="cinema-close" src="resources/logos/cinema-close.svg">
|
||||||
</div>
|
</div>
|
||||||
<div id="btn-monitor" class="btn-monitor">
|
<div id="btn-micro" class="btn-micro">
|
||||||
<img id="monitor" src="resources/logos/monitor.svg">
|
<img id="microphone" src="resources/logos/microphone.svg">
|
||||||
<img id="monitor-close" src="resources/logos/monitor-close.svg">
|
<img id="microphone-close" src="resources/logos/microphone-close.svg">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -48,7 +48,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 {
|
#enableCameraScene button {
|
||||||
|
@ -60,6 +60,9 @@
|
|||||||
font-size: 8px;
|
font-size: 8px;
|
||||||
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;
|
||||||
}
|
}
|
||||||
@ -95,6 +98,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="#" id="helpCameraSettingsFormRefresh">Refresh</a>
|
<a href="#" id="helpCameraSettingsFormRefresh">Refresh</a>
|
||||||
<button type="submit" id="helpCameraSettingsFormContinue">Continue</button>
|
<button type="submit" id="helpCameraSettingsFormContinue">Continue</button>
|
||||||
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
103
front/dist/resources/style/style.css
vendored
@ -98,7 +98,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{
|
||||||
@ -126,6 +126,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{
|
||||||
@ -141,7 +142,7 @@ body .message-info.warning{
|
|||||||
right: 15px;
|
right: 15px;
|
||||||
bottom: 30px;
|
bottom: 30px;
|
||||||
border-radius: 15px 15px 15px 15px;
|
border-radius: 15px 15px 15px 15px;
|
||||||
max-height: 200px;
|
max-height: 20%;
|
||||||
}
|
}
|
||||||
|
|
||||||
video#myCamVideo{
|
video#myCamVideo{
|
||||||
@ -153,19 +154,60 @@ 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: 450px;
|
right: 15px;
|
||||||
height: 150px;
|
width: 15vw;
|
||||||
|
height: 40px;
|
||||||
|
text-align: center;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
justify-items: center;
|
||||||
}
|
}
|
||||||
/*btn animation*/
|
/*btn animation*/
|
||||||
.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;*/
|
||||||
border: solid 0px black;
|
border: solid 0px black;
|
||||||
width: 44px;
|
width: 44px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
@ -174,7 +216,8 @@ video#myCamVideo{
|
|||||||
border-radius: 48px;
|
border-radius: 48px;
|
||||||
transform: translateY(20px);
|
transform: translateY(20px);
|
||||||
transition-timing-function: ease-in-out;
|
transition-timing-function: ease-in-out;
|
||||||
bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
margin: 0 4%;
|
||||||
}
|
}
|
||||||
.btn-cam-action div.disabled {
|
.btn-cam-action div.disabled {
|
||||||
background: #d75555;
|
background: #d75555;
|
||||||
@ -193,17 +236,17 @@ video#myCamVideo{
|
|||||||
.btn-micro{
|
.btn-micro{
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
transition: all .3s;
|
transition: all .3s;
|
||||||
right: 44px;
|
/*right: 44px;*/
|
||||||
}
|
}
|
||||||
.btn-video{
|
.btn-video{
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
transition: all .25s;
|
transition: all .25s;
|
||||||
right: 134px;
|
/*right: 134px;*/
|
||||||
}
|
}
|
||||||
.btn-monitor{
|
.btn-monitor{
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
transition: all .2s;
|
transition: all .2s;
|
||||||
right: 224px;
|
/*right: 224px;*/
|
||||||
}
|
}
|
||||||
.btn-copy{
|
.btn-copy{
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
@ -501,12 +544,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 {
|
||||||
@ -522,6 +573,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;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,7 +585,6 @@ 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 {
|
||||||
@ -541,12 +592,17 @@ input[type=range]:focus::-ms-fill-upper {
|
|||||||
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;
|
||||||
border-radius: 15px 15px 15px 15px;
|
border-radius: 15px 15px 15px 15px;
|
||||||
|
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 */
|
||||||
.media-container {
|
.media-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -1111,17 +1167,34 @@ div.action{
|
|||||||
animation-iteration-count: infinite;
|
animation-iteration-count: infinite;
|
||||||
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;
|
||||||
background-color: #2d2d2dba;
|
background-color: #2d2d2dba;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
max-width: 250px;
|
max-width: 350px;
|
||||||
margin-left: calc(50% - 125px);
|
margin-left: calc(50% - 175px);
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
186
front/dist/static/images/favicons/manifest.json
vendored
@ -1,41 +1,149 @@
|
|||||||
{
|
{
|
||||||
"name": "App",
|
"short_name": "WA",
|
||||||
"icons": [
|
"name": "WorkAdventure",
|
||||||
{
|
"icons": [
|
||||||
"src": "\/android-icon-36x36.png",
|
{
|
||||||
"sizes": "36x36",
|
"src": "/static/images/favicons/apple-icon-57x57.png",
|
||||||
"type": "image\/png",
|
"sizes": "57x57",
|
||||||
"density": "0.75"
|
"type": "image\/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "\/android-icon-48x48.png",
|
"src": "/static/images/favicons/apple-icon-60x60.png",
|
||||||
"sizes": "48x48",
|
"sizes": "60x60",
|
||||||
"type": "image\/png",
|
"type": "image\/png"
|
||||||
"density": "1.0"
|
},
|
||||||
},
|
{
|
||||||
{
|
"src": "/static/images/favicons/apple-icon-72x72.png",
|
||||||
"src": "\/android-icon-72x72.png",
|
"sizes": "72x72",
|
||||||
"sizes": "72x72",
|
"type": "image\/png"
|
||||||
"type": "image\/png",
|
},
|
||||||
"density": "1.5"
|
{
|
||||||
},
|
"src": "/static/images/favicons/apple-icon-76x76.png",
|
||||||
{
|
"sizes": "76x76",
|
||||||
"src": "\/android-icon-96x96.png",
|
"type": "image\/png"
|
||||||
"sizes": "96x96",
|
},
|
||||||
"type": "image\/png",
|
{
|
||||||
"density": "2.0"
|
"src": "/static/images/favicons/apple-icon-114x114.png",
|
||||||
},
|
"sizes": "114x114",
|
||||||
{
|
"type": "image\/png"
|
||||||
"src": "\/android-icon-144x144.png",
|
},
|
||||||
"sizes": "144x144",
|
{
|
||||||
"type": "image\/png",
|
"src": "/static/images/favicons/apple-icon-120x120.png",
|
||||||
"density": "3.0"
|
"sizes": "120x120",
|
||||||
},
|
"type": "image\/png"
|
||||||
{
|
},
|
||||||
"src": "\/android-icon-192x192.png",
|
{
|
||||||
"sizes": "192x192",
|
"src": "/static/images/favicons/apple-icon-144x144.png",
|
||||||
"type": "image\/png",
|
"sizes": "144x144",
|
||||||
"density": "4.0"
|
"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",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "0.75"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/android-icon-48x48.png",
|
||||||
|
"sizes": "48x48",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "1.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "1.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/android-icon-96x96.png",
|
||||||
|
"sizes": "96x96",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "2.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/android-icon-144x144.png",
|
||||||
|
"sizes": "144x144",
|
||||||
|
"type": "image\/png",
|
||||||
|
"density": "3.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/images/favicons/android-icon-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image\/png",
|
||||||
|
"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"
|
||||||
|
}]
|
||||||
}
|
}
|
@ -30,7 +30,7 @@
|
|||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"generic-type-guard": "^3.2.0",
|
"generic-type-guard": "^3.2.0",
|
||||||
"google-protobuf": "^3.13.0",
|
"google-protobuf": "^3.13.0",
|
||||||
"phaser": "3.24.1",
|
"phaser": "^3.54.0",
|
||||||
"phaser3-rex-plugins": "^1.1.42",
|
"phaser3-rex-plugins": "^1.1.42",
|
||||||
"queue-typescript": "^1.0.1",
|
"queue-typescript": "^1.0.1",
|
||||||
"quill": "1.3.6",
|
"quill": "1.3.6",
|
||||||
|
@ -65,14 +65,14 @@ class IframeListener {
|
|||||||
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,8 +91,12 @@ class IframeListener {
|
|||||||
else if(payload.type === 'goToPage' && isGoToPageEvent(payload.data)) {
|
else if(payload.type === 'goToPage' && isGoToPageEvent(payload.data)) {
|
||||||
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();
|
||||||
|
@ -11,8 +11,8 @@ class ScriptUtils {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public openCoWebsite(url : string){
|
public openCoWebsite(url: string, base: string) {
|
||||||
coWebsiteManager.loadCoWebsite(url,url);
|
coWebsiteManager.loadCoWebsite(url, base);
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeCoWebSite(){
|
public closeCoWebSite(){
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,8 @@ import {
|
|||||||
SendJitsiJwtMessage,
|
SendJitsiJwtMessage,
|
||||||
CharacterLayerMessage,
|
CharacterLayerMessage,
|
||||||
PingMessage,
|
PingMessage,
|
||||||
SendUserMessage, BanUserMessage
|
SendUserMessage,
|
||||||
|
BanUserMessage
|
||||||
} from "../Messages/generated/messages_pb"
|
} from "../Messages/generated/messages_pb"
|
||||||
|
|
||||||
import {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
|
import {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
|
||||||
@ -169,7 +170,10 @@ export class RoomConnection implements RoomConnection {
|
|||||||
} else if (message.hasWorldfullmessage()) {
|
} else if (message.hasWorldfullmessage()) {
|
||||||
worldFullMessageStream.onMessage();
|
worldFullMessageStream.onMessage();
|
||||||
this.closed = true;
|
this.closed = true;
|
||||||
} else if (message.hasWebrtcsignaltoclientmessage()) {
|
} else if (message.hasWorldconnexionmessage()) {
|
||||||
|
worldFullMessageStream.onMessage(message.getWorldconnexionmessage()?.getMessage());
|
||||||
|
this.closed = true;
|
||||||
|
}else if (message.hasWebrtcsignaltoclientmessage()) {
|
||||||
this.dispatch(EventMessage.WEBRTC_SIGNAL, message.getWebrtcsignaltoclientmessage());
|
this.dispatch(EventMessage.WEBRTC_SIGNAL, message.getWebrtcsignaltoclientmessage());
|
||||||
} else if (message.hasWebrtcscreensharingsignaltoclientmessage()) {
|
} else if (message.hasWebrtcscreensharingsignaltoclientmessage()) {
|
||||||
this.dispatch(EventMessage.WEBRTC_SCREEN_SHARING_SIGNAL, message.getWebrtcscreensharingsignaltoclientmessage());
|
this.dispatch(EventMessage.WEBRTC_SCREEN_SHARING_SIGNAL, message.getWebrtcscreensharingsignaltoclientmessage());
|
||||||
|
@ -2,12 +2,12 @@ import {Subject} from "rxjs";
|
|||||||
|
|
||||||
class WorldFullMessageStream {
|
class WorldFullMessageStream {
|
||||||
|
|
||||||
private _stream:Subject<void> = new Subject();
|
private _stream:Subject<string|null> = new Subject<string|null>();
|
||||||
public stream = this._stream.asObservable();
|
public stream = this._stream.asObservable();
|
||||||
|
|
||||||
|
|
||||||
onMessage() {
|
onMessage(message? :string) {
|
||||||
this._stream.next();
|
this._stream.next(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,8 +10,6 @@ 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;
|
||||||
@ -25,8 +23,6 @@ export {
|
|||||||
PUSHER_URL,
|
PUSHER_URL,
|
||||||
UPLOADER_URL,
|
UPLOADER_URL,
|
||||||
ADMIN_URL,
|
ADMIN_URL,
|
||||||
RESOLUTION,
|
|
||||||
ZOOM_LEVEL,
|
|
||||||
POSITION_DELAY,
|
POSITION_DELAY,
|
||||||
MAX_EXTRAPOLATION_TIME,
|
MAX_EXTRAPOLATION_TIME,
|
||||||
STUN_SERVER,
|
STUN_SERVER,
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js';
|
import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js';
|
||||||
|
import ScaleManager = Phaser.Scale.ScaleManager;
|
||||||
const outOfScreenX = -1000;
|
import {waScaleManager} from "../Services/WaScaleManager";
|
||||||
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 +8,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 }) => {
|
||||||
this.x = pointer.x;
|
if (!pointer.wasTouch) {
|
||||||
this.y = pointer.y;
|
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.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);
|
||||||
}
|
}
|
||||||
}
|
}
|
52
front/src/Phaser/Game/DirtyScene.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import {ResizableScene} from "../Login/ResizableScene";
|
||||||
|
import GameObject = Phaser.GameObjects.GameObject;
|
||||||
|
import Events = Phaser.Scenes.Events;
|
||||||
|
import AnimationEvents = Phaser.Animations.Events;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isAlreadyTracking = true;
|
||||||
|
const trackAnimationFunction = this.trackAnimation.bind(this);
|
||||||
|
this.events.on(Events.ADDED_TO_SCENE, (gameObject: GameObject) => {
|
||||||
|
this.objectListChanged = true;
|
||||||
|
gameObject.on(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.events.on(Events.REMOVED_FROM_SCENE, (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;
|
||||||
|
}
|
||||||
|
}
|
88
front/src/Phaser/Game/Game.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
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 (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;
|
||||||
|
}
|
||||||
|
}
|
@ -89,10 +89,7 @@ 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);
|
||||||
|
scenePlugin.launch(HelpCameraSettingsSceneName);//700
|
||||||
if (!localUserStore.getHelpCameraSettingsShown()) {
|
|
||||||
scenePlugin.launch(HelpCameraSettingsSceneName);//700
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public gameSceneIsCreated(scene: GameScene) {
|
public gameSceneIsCreated(scene: GameScene) {
|
||||||
|
@ -15,8 +15,6 @@ 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 {
|
||||||
ITiledMap,
|
ITiledMap,
|
||||||
@ -86,10 +84,14 @@ import EVENT_TYPE =Phaser.Scenes.Events
|
|||||||
import {Subscription} from "rxjs";
|
import {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";
|
||||||
|
|
||||||
export interface GameSceneInitInterface {
|
export interface GameSceneInitInterface {
|
||||||
initPosition: PointInterface|null,
|
initPosition: PointInterface|null,
|
||||||
@ -128,13 +130,13 @@ 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;
|
||||||
MapPlayersByKey : Map<number, RemotePlayer> = new Map<number, RemotePlayer>();
|
MapPlayersByKey : Map<number, RemotePlayer> = new Map<number, RemotePlayer>();
|
||||||
Map!: Phaser.Tilemaps.Tilemap;
|
Map!: Phaser.Tilemaps.Tilemap;
|
||||||
Layers!: Array<Phaser.Tilemaps.StaticTilemapLayer>;
|
Layers!: Array<Phaser.Tilemaps.TilemapLayer>;
|
||||||
Objects!: Array<Phaser.Physics.Arcade.Sprite>;
|
Objects!: Array<Phaser.Physics.Arcade.Sprite>;
|
||||||
mapFile!: ITiledMap;
|
mapFile!: ITiledMap;
|
||||||
groups: Map<number, Sprite>;
|
groups: Map<number, Sprite>;
|
||||||
@ -184,6 +186,7 @@ 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;
|
||||||
|
|
||||||
constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) {
|
constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) {
|
||||||
super({
|
super({
|
||||||
@ -202,12 +205,11 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
})
|
})
|
||||||
this.connectionAnswerPromise = new Promise<RoomJoinedMessageInterface>((resolve, reject): void => {
|
this.connectionAnswerPromise = new Promise<RoomJoinedMessageInterface>((resolve, reject): void => {
|
||||||
this.connectionAnswerPromiseResolve = resolve;
|
this.connectionAnswerPromiseResolve = resolve;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//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) {
|
||||||
@ -267,6 +269,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.
|
||||||
@ -365,15 +370,17 @@ 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())
|
this.messageSubscription = worldFullMessageStream.stream.subscribe((message) => this.showWorldFullError(message))
|
||||||
|
|
||||||
const playerName = gameManager.getPlayerName();
|
const playerName = gameManager.getPlayerName();
|
||||||
if (!playerName) {
|
if (!playerName) {
|
||||||
@ -395,12 +402,11 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
this.physics.world.setBounds(0, 0, this.Map.widthInPixels, this.Map.heightInPixels);
|
this.physics.world.setBounds(0, 0, this.Map.widthInPixels, this.Map.heightInPixels);
|
||||||
|
|
||||||
//add layer on map
|
//add layer on map
|
||||||
this.Layers = new Array<Phaser.Tilemaps.StaticTilemapLayer>();
|
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') {
|
||||||
this.addLayer(this.Map.createStaticLayer(layer.name, this.Terrains, 0, 0).setDepth(depth));
|
this.addLayer(this.Map.createLayer(layer.name, this.Terrains, 0, 0).setDepth(depth));
|
||||||
|
|
||||||
const exitSceneUrl = this.getExitSceneUrl(layer);
|
const exitSceneUrl = this.getExitSceneUrl(layer);
|
||||||
if (exitSceneUrl !== undefined) {
|
if (exitSceneUrl !== undefined) {
|
||||||
@ -436,8 +442,8 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
|
|
||||||
|
|
||||||
//create input to move
|
//create input to move
|
||||||
mediaManager.setUserInputManager(this.userInputManager);
|
|
||||||
this.userInputManager = new UserInputManager(this);
|
this.userInputManager = new UserInputManager(this);
|
||||||
|
mediaManager.setUserInputManager(this.userInputManager);
|
||||||
|
|
||||||
if (localUserStore.getFullscreen()) {
|
if (localUserStore.getFullscreen()) {
|
||||||
document.querySelector('body')?.requestFullscreen();
|
document.querySelector('body')?.requestFullscreen();
|
||||||
@ -917,9 +923,11 @@ ${escapedMessage}
|
|||||||
audioManager.unloadAudio();
|
audioManager.unloadAudio();
|
||||||
// We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map.
|
// We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map.
|
||||||
this.connection?.closeConnection();
|
this.connection?.closeConnection();
|
||||||
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();
|
||||||
@ -1067,17 +1075,18 @@ ${escapedMessage}
|
|||||||
//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.StaticTilemapLayer){
|
addLayer(Layer : Phaser.Tilemaps.TilemapLayer){
|
||||||
this.Layers.push(Layer);
|
this.Layers.push(Layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
createCollisionWithPlayer() {
|
createCollisionWithPlayer() {
|
||||||
|
this.physics.disableUpdate();
|
||||||
//add collision layer
|
//add collision layer
|
||||||
this.Layers.forEach((Layer: Phaser.Tilemaps.StaticTilemapLayer) => {
|
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) => {
|
||||||
//this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name)
|
//this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name)
|
||||||
});
|
});
|
||||||
@ -1206,12 +1215,24 @@ ${escapedMessage}
|
|||||||
* @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":
|
||||||
@ -1237,6 +1258,7 @@ ${escapedMessage}
|
|||||||
// 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 + '"');
|
||||||
@ -1406,7 +1428,8 @@ ${escapedMessage}
|
|||||||
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
|
||||||
@ -1441,19 +1464,18 @@ ${escapedMessage}
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 {
|
||||||
@ -1496,14 +1518,29 @@ ${escapedMessage}
|
|||||||
}
|
}
|
||||||
|
|
||||||
//todo: put this into an 'orchestrator' scene (EntryScene?)
|
//todo: put this into an 'orchestrator' scene (EntryScene?)
|
||||||
private showWorldFullError(): void {
|
private showWorldFullError(message: string|null): void {
|
||||||
this.cleanupClosingScene();
|
this.cleanupClosingScene();
|
||||||
this.scene.stop(ReconnectingSceneName);
|
this.scene.stop(ReconnectingSceneName);
|
||||||
|
this.scene.remove(ReconnectingSceneName);
|
||||||
this.userInputManager.disableControls();
|
this.userInputManager.disableControls();
|
||||||
this.scene.start(ErrorSceneName, {
|
//FIX ME to use status code
|
||||||
title: 'Connection rejected',
|
if(message == undefined){
|
||||||
subTitle: 'The world you are trying to join is full. Try again later.',
|
this.scene.start(ErrorSceneName, {
|
||||||
message: 'If you want more information, you may contact us at: workadventure@thecodingmachine.com'
|
title: 'Connection rejected',
|
||||||
});
|
subTitle: 'The world you are trying to join is full. Try again later.',
|
||||||
|
message: 'If you want more information, you may contact us at: workadventure@thecodingmachine.com'
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
this.scene.start(ErrorSceneName, {
|
||||||
|
title: 'Connection rejected',
|
||||||
|
subTitle: 'You cannot join the World. Try again later. \n\r \n\r Error: '+message+'.',
|
||||||
|
message: 'If you want more information, you may contact administrator or contact us at: workadventure@thecodingmachine.com'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zoomByFactor(zoomFactor: number) {
|
||||||
|
waScaleManager.zoomModifier *= zoomFactor;
|
||||||
|
this.updateCameraOffset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ 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 {
|
||||||
@ -256,16 +258,10 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
this.containersRow[i][j].add(children);
|
this.containersRow[i][j].add(children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,26 +270,9 @@ 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(){
|
||||||
const layers: string[] = [];
|
const layers: string[] = [];
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
@ -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,41 @@ 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.updateScene();
|
||||||
|
|
||||||
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 +279,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,11 +18,18 @@ 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) {
|
||||||
ErrorScene.showError(new WAError('Page Not Found', 'Could not find map', window.location.pathname), this.scene);
|
ErrorScene.showError(new WAError(
|
||||||
|
'Access link incorrect',
|
||||||
|
'Could not find map. Please check your access link.',
|
||||||
|
'If you want more information, you may contact administrator or contact us at: workadventure@thecodingmachine.com'), this.scene);
|
||||||
} else {
|
} else {
|
||||||
ErrorScene.showError(err, this.scene);
|
ErrorScene.showError(err, this.scene);
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ 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 {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||||
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
||||||
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
|
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
|
||||||
|
@ -37,8 +37,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 +45,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');
|
||||||
@ -240,36 +240,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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingM
|
|||||||
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,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;
|
||||||
|
|
||||||
@ -21,7 +21,6 @@ export class HelpCameraSettingsScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
create(){
|
create(){
|
||||||
localUserStore.setHelpCameraSettingsShown();
|
|
||||||
this.createHelpCameraSettings();
|
this.createHelpCameraSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,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();
|
||||||
@ -39,18 +41,27 @@ 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();
|
||||||
|
localUserStore.setHelpCameraSettingsShown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
if(window.navigator.userAgent.includes('Firefox')){
|
try{
|
||||||
HtmlUtils.getElementByIdOrFail<HTMLParagraphElement>('browserHelpSetting').innerHTML ='<img src="/resources/objects/help-setting-camera-permission-firefox.png"/>';
|
if(window.navigator.userAgent.includes('Firefox')){
|
||||||
}else if(window.navigator.userAgent.includes('Chrome')){
|
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-chrome.png"/>';
|
}else if(window.navigator.userAgent.includes('Chrome')){
|
||||||
|
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();
|
||||||
@ -62,22 +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'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private revealMenusAfterInit(menuElement: Phaser.GameObjects.DOMElement, rootDomId: string) {
|
private revealMenusAfterInit(menuElement: Phaser.GameObjects.DOMElement, rootDomId: string) {
|
||||||
@ -89,18 +104,11 @@ 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);
|
||||||
const middleX = this.getMiddleX();
|
const middleX = this.getMiddleX();
|
||||||
const middleY = this.getMiddleY();
|
const middleY = this.getMiddleY();
|
||||||
this.tweens.add({
|
this.tweens.add({
|
||||||
@ -113,24 +121,27 @@ export class HelpCameraSettingsScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ export class ErrorScene extends Phaser.Scene {
|
|||||||
|
|
||||||
this.subTitleField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2 + 24, this.subTitle);
|
this.subTitleField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2 + 24, this.subTitle);
|
||||||
|
|
||||||
this.messageField = this.add.text(this.game.renderer.width / 2, this.game.renderer.height / 2 + 38, this.message, {
|
this.messageField = this.add.text(this.game.renderer.width / 2, this.game.renderer.height / 2 + 48, this.message, {
|
||||||
fontFamily: 'Georgia, "Goudy Bookletter 1911", Times, serif',
|
fontFamily: 'Georgia, "Goudy Bookletter 1911", Times, serif',
|
||||||
fontSize: '10px'
|
fontSize: '10px'
|
||||||
});
|
});
|
||||||
|
105
front/src/Phaser/Services/HdpiManager.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import ScaleManager = Phaser.Scale.ScaleManager;
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = 1;
|
||||||
|
|
||||||
|
while (realPixelNumber > this.minRecommendedGamePixelsNumber * i * i) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has the canvas more pixels than the screen? This is forbidden
|
||||||
|
if ((i - 1) * this._zoomModifier < 1) {
|
||||||
|
// Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter)
|
||||||
|
this._zoomModifier = 1 / (i - 1);
|
||||||
|
|
||||||
|
return {
|
||||||
|
game: {
|
||||||
|
width: realPixelScreenSize.width,
|
||||||
|
height: realPixelScreenSize.height,
|
||||||
|
},
|
||||||
|
real: {
|
||||||
|
width: realPixelScreenSize.width,
|
||||||
|
height: realPixelScreenSize.height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const gameWidth = Math.ceil(realPixelScreenSize.width / (i - 1) / this._zoomModifier);
|
||||||
|
const gameHeight = Math.ceil(realPixelScreenSize.height / (i - 1) / 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 / (i - 1);
|
||||||
|
|
||||||
|
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 / (i - 1)) * (i - 1),
|
||||||
|
height: Math.ceil(realPixelScreenSize.height / (i - 1)) * (i - 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,4 @@
|
|||||||
export class OutlinePipeline extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline {
|
export class OutlinePipeline extends Phaser.Renderer.WebGL.Pipelines.MultiPipeline {
|
||||||
|
|
||||||
// the unique id of this pipeline
|
// the unique id of this pipeline
|
||||||
public static readonly KEY = 'Outline';
|
public static readonly KEY = 'Outline';
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -346,7 +346,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){
|
||||||
@ -354,10 +354,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();
|
||||||
|
@ -4,6 +4,8 @@ import {discussionManager, SendMessageCallback} from "./DiscussionManager";
|
|||||||
import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
||||||
import {localUserStore} from "../Connexion/LocalUserStore";
|
import {localUserStore} from "../Connexion/LocalUserStore";
|
||||||
import {UserSimplePeerInterface} from "./SimplePeer";
|
import {UserSimplePeerInterface} from "./SimplePeer";
|
||||||
|
import {SoundMeter} from "../Phaser/Components/SoundMeter";
|
||||||
|
|
||||||
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 +28,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 +43,7 @@ export class MediaManager {
|
|||||||
microphoneClose: HTMLImageElement;
|
microphoneClose: HTMLImageElement;
|
||||||
microphone: HTMLImageElement;
|
microphone: HTMLImageElement;
|
||||||
webrtcInAudio: HTMLAudioElement;
|
webrtcInAudio: HTMLAudioElement;
|
||||||
|
mySoundMeterElement: HTMLDivElement;
|
||||||
private webrtcOutAudio: HTMLAudioElement;
|
private webrtcOutAudio: HTMLAudioElement;
|
||||||
constraintsMedia : MediaStreamConstraints = {
|
constraintsMedia : MediaStreamConstraints = {
|
||||||
audio: audioConstraint,
|
audio: audioConstraint,
|
||||||
@ -49,6 +53,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;
|
||||||
@ -63,6 +69,12 @@ export class MediaManager {
|
|||||||
|
|
||||||
private triggerCloseJistiFrame : Map<String, Function> = new Map<String, Function>();
|
private triggerCloseJistiFrame : Map<String, Function> = new Map<String, Function>();
|
||||||
|
|
||||||
|
private userInputManager?: UserInputManager;
|
||||||
|
|
||||||
|
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');
|
||||||
@ -121,10 +133,16 @@ export class MediaManager {
|
|||||||
this.pingCameraStatus();
|
this.pingCameraStatus();
|
||||||
|
|
||||||
this.checkActiveUser(); //todo: desactivated in case of bug
|
this.checkActiveUser(); //todo: desactivated in case of bug
|
||||||
|
|
||||||
|
this.mySoundMeterElement = (HtmlUtils.getElementByIdOrFail('mySoundMeter'));
|
||||||
|
this.mySoundMeterElement.childNodes.forEach((value: ChildNode, index) => {
|
||||||
|
this.mySoundMeterElement.children.item(index)?.classList.remove('active');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public setLastUpdateScene(){
|
public updateScene(){
|
||||||
this.lastUpdateScene = new Date();
|
this.lastUpdateScene = new Date();
|
||||||
|
this.updateSoudMeter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public blurCamera() {
|
public blurCamera() {
|
||||||
@ -225,6 +243,10 @@ export class MediaManager {
|
|||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
this.disableCameraStyle();
|
this.disableCameraStyle();
|
||||||
|
|
||||||
|
layoutManager.addInformation('warning', 'Camera access denied. Click here and check navigators permissions.', () => {
|
||||||
|
this.showHelpCameraSettingsCallBack();
|
||||||
|
}, this.userInputManager);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,6 +275,10 @@ export class MediaManager {
|
|||||||
}).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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,6 +350,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 +432,14 @@ 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();
|
||||||
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 +456,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;
|
||||||
|
|
||||||
|
//init sound meter
|
||||||
|
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 +489,7 @@ export class MediaManager {
|
|||||||
track.stop();
|
track.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.mySoundMeter?.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
setCamera(id: string): Promise<MediaStream> {
|
setCamera(id: string): Promise<MediaStream> {
|
||||||
@ -496,6 +535,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 +631,12 @@ export class MediaManager {
|
|||||||
throw `Unable to find video for ${userId}`;
|
throw `Unable to find video for ${userId}`;
|
||||||
}
|
}
|
||||||
remoteVideo.srcObject = stream;
|
remoteVideo.srcObject = stream;
|
||||||
|
|
||||||
|
//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 +652,10 @@ export class MediaManager {
|
|||||||
layoutManager.remove(userId);
|
layoutManager.remove(userId);
|
||||||
this.remoteVideo.delete(userId);
|
this.remoteVideo.delete(userId);
|
||||||
|
|
||||||
|
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,6 +773,7 @@ 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
|
//check if user is active
|
||||||
@ -739,6 +796,57 @@ export class MediaManager {
|
|||||||
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mediaManager = new MediaManager();
|
export const mediaManager = new MediaManager();
|
||||||
|
@ -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: [
|
||||||
{
|
{
|
||||||
|
@ -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 => {
|
||||||
|
@ -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: [
|
||||||
{
|
{
|
||||||
|
@ -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, isMobile, 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";
|
||||||
@ -18,6 +18,9 @@ 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 { 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();
|
||||||
|
|
||||||
@ -68,12 +71,20 @@ 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,
|
||||||
isMobile() ? SelectCharacterMobileScene : SelectCharacterScene,
|
isMobile() ? SelectCharacterMobileScene : SelectCharacterScene,
|
||||||
@ -84,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
|
||||||
@ -100,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
|
||||||
@ -111,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)) {
|
||||||
@ -127,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();
|
||||||
|
@ -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(1);
|
||||||
|
});
|
||||||
|
});
|
1167
front/yarn.lock
@ -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 |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 3.0 KiB |
BIN
maps/Village/tileset1-repositioning.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
maps/Village/tileset1.png
Normal file
After Width: | Height: | Size: 29 KiB |
82
maps/tests/autoresize.json
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":25,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
"height":25,
|
||||||
|
"id":1,
|
||||||
|
"name":"floor",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":25,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":25,
|
||||||
|
"id":2,
|
||||||
|
"name":"start",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":25,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"draworder":"topdown",
|
||||||
|
"id":3,
|
||||||
|
"name":"floorLayer",
|
||||||
|
"objects":[
|
||||||
|
{
|
||||||
|
"height":114.5,
|
||||||
|
"id":3,
|
||||||
|
"name":"",
|
||||||
|
"rotation":0,
|
||||||
|
"text":
|
||||||
|
{
|
||||||
|
"fontfamily":"Sans Serif",
|
||||||
|
"pixelsize":11,
|
||||||
|
"text":"Test:\nOpen your browser to the maximum size of your screen. Resize the browser window just smaller than the blue \"carpet\".\nResult:\nThe viewport is zoomed out x2 so that you can still see the \"carpet\"",
|
||||||
|
"wrap":true
|
||||||
|
},
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":252.4375,
|
||||||
|
"x":162.78125,
|
||||||
|
"y":129.5
|
||||||
|
}],
|
||||||
|
"opacity":1,
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":8,
|
||||||
|
"nextobjectid":5,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"2021.03.23",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[
|
||||||
|
{
|
||||||
|
"columns":11,
|
||||||
|
"firstgid":1,
|
||||||
|
"image":"tileset1.png",
|
||||||
|
"imageheight":352,
|
||||||
|
"imagewidth":352,
|
||||||
|
"margin":0,
|
||||||
|
"name":"tileset1",
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":121,
|
||||||
|
"tileheight":32,
|
||||||
|
"tilewidth":32
|
||||||
|
}],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":1.5,
|
||||||
|
"width":25
|
||||||
|
}
|
82
maps/tests/energy.json
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":10,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
"height":10,
|
||||||
|
"id":1,
|
||||||
|
"name":"floor",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":10,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":10,
|
||||||
|
"id":2,
|
||||||
|
"name":"start",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":10,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"draworder":"topdown",
|
||||||
|
"id":3,
|
||||||
|
"name":"floorLayer",
|
||||||
|
"objects":[
|
||||||
|
{
|
||||||
|
"height":261.73266830836,
|
||||||
|
"id":3,
|
||||||
|
"name":"",
|
||||||
|
"rotation":0,
|
||||||
|
"text":
|
||||||
|
{
|
||||||
|
"fontfamily":"Sans Serif",
|
||||||
|
"pixelsize":11,
|
||||||
|
"text":"Test:\nOpen this page in Chrome.\nOpen Chrome Task Manager (Shift - Esc)\n\nResult:\nThe WorkAdventure tab should take about 10% of CPU.\nThe GPU should almost consume nothing (~4%)",
|
||||||
|
"wrap":true
|
||||||
|
},
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":252.4375,
|
||||||
|
"x":46.5894222943362,
|
||||||
|
"y":34.2876372135732
|
||||||
|
}],
|
||||||
|
"opacity":1,
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":8,
|
||||||
|
"nextobjectid":5,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"2021.03.23",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[
|
||||||
|
{
|
||||||
|
"columns":11,
|
||||||
|
"firstgid":1,
|
||||||
|
"image":"tileset1.png",
|
||||||
|
"imageheight":352,
|
||||||
|
"imagewidth":352,
|
||||||
|
"margin":0,
|
||||||
|
"name":"tileset1",
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":121,
|
||||||
|
"tileheight":32,
|
||||||
|
"tilewidth":32
|
||||||
|
}],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":1.5,
|
||||||
|
"width":10
|
||||||
|
}
|
@ -50,6 +50,38 @@
|
|||||||
<a href="#" class="testLink" data-testmap="goToPage.json" target="_blank">Testing scripting API with a script</a>
|
<a href="#" class="testLink" data-testmap="goToPage.json" target="_blank">Testing scripting API with a script</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="radio" name="test-autoresize"> Success <input type="radio" name="test-autoresize"> Failure <input type="radio" name="test-autoresize" checked> Pending
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="#" class="testLink" data-testmap="autoresize.json" target="_blank">Testing auto-zoom of viewport</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="radio" name="test-mouse-wheel"> Success <input type="radio" name="test-mouse-wheel"> Failure <input type="radio" name="test-mouse-wheel" checked> Pending
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="#" class="testLink" data-testmap="mousewheel.json" target="_blank">Testing zoom via mouse wheel</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="radio" name="test-mobile"> Success <input type="radio" name="test-mobile"> Failure <input type="radio" name="test-mobile" checked> Pending
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="#" class="testLink" data-testmap="mobile.json" target="_blank">Testing movement on mobile</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="radio" name="test-energy"> Success <input type="radio" name="test-energy"> Failure <input type="radio" name="test-energy" checked> Pending
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="#" class="testLink" data-testmap="energy.json" target="_blank">Test energy consumption</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
82
maps/tests/mobile.json
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":10,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
"height":10,
|
||||||
|
"id":1,
|
||||||
|
"name":"floor",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":10,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":10,
|
||||||
|
"id":2,
|
||||||
|
"name":"start",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":10,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"draworder":"topdown",
|
||||||
|
"id":3,
|
||||||
|
"name":"floorLayer",
|
||||||
|
"objects":[
|
||||||
|
{
|
||||||
|
"height":281.232647439376,
|
||||||
|
"id":3,
|
||||||
|
"name":"",
|
||||||
|
"rotation":0,
|
||||||
|
"text":
|
||||||
|
{
|
||||||
|
"fontfamily":"Sans Serif",
|
||||||
|
"pixelsize":11,
|
||||||
|
"text":"Test:\nOpen the page on a mobile device (or set Chrome in mobile mode)\n\nResult:\nBy default, the zoom level is not too zoomed in nor out (zoom level gives you the same visibility you typically have on desktop)\n\nYou can move using a virtual joystick\n\nYou can zoom using the \"pinch\" gesture\n\nWhen you zoom in or out, the size of the virtual joystick stays the same (about the size of your thumb)\n\nWhen you \"pinch\", your character does not move\n\nChanging phone orientation works",
|
||||||
|
"wrap":true
|
||||||
|
},
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":252.4375,
|
||||||
|
"x":46.5894222943362,
|
||||||
|
"y":34.2876372135732
|
||||||
|
}],
|
||||||
|
"opacity":1,
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":8,
|
||||||
|
"nextobjectid":5,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"2021.03.23",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[
|
||||||
|
{
|
||||||
|
"columns":11,
|
||||||
|
"firstgid":1,
|
||||||
|
"image":"tileset1.png",
|
||||||
|
"imageheight":352,
|
||||||
|
"imagewidth":352,
|
||||||
|
"margin":0,
|
||||||
|
"name":"tileset1",
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":121,
|
||||||
|
"tileheight":32,
|
||||||
|
"tilewidth":32
|
||||||
|
}],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":1.5,
|
||||||
|
"width":10
|
||||||
|
}
|
82
maps/tests/mousewheel.json
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":10,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
"height":10,
|
||||||
|
"id":1,
|
||||||
|
"name":"floor",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":10,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":10,
|
||||||
|
"id":2,
|
||||||
|
"name":"start",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":10,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"draworder":"topdown",
|
||||||
|
"id":3,
|
||||||
|
"name":"floorLayer",
|
||||||
|
"objects":[
|
||||||
|
{
|
||||||
|
"height":261.73266830836,
|
||||||
|
"id":3,
|
||||||
|
"name":"",
|
||||||
|
"rotation":0,
|
||||||
|
"text":
|
||||||
|
{
|
||||||
|
"fontfamily":"Sans Serif",
|
||||||
|
"pixelsize":11,
|
||||||
|
"text":"Test:\nUse your mouse wheel\nResult:\nThe canvas is zooming in or out\n\nTest:\nZoom out to the maximum\nResult:\nThe pixel size is the canvas = the pixel size on your monitor\n\nTest:\nZoom in to the maximum\nResult:\nYou see an area slightly larger than your character (with your character at the center if you are not standing on a border of the map)",
|
||||||
|
"wrap":true
|
||||||
|
},
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":252.4375,
|
||||||
|
"x":46.5894222943362,
|
||||||
|
"y":34.2876372135732
|
||||||
|
}],
|
||||||
|
"opacity":1,
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":8,
|
||||||
|
"nextobjectid":5,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"2021.03.23",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[
|
||||||
|
{
|
||||||
|
"columns":11,
|
||||||
|
"firstgid":1,
|
||||||
|
"image":"tileset1.png",
|
||||||
|
"imageheight":352,
|
||||||
|
"imagewidth":352,
|
||||||
|
"margin":0,
|
||||||
|
"name":"tileset1",
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":121,
|
||||||
|
"tileheight":32,
|
||||||
|
"tilewidth":32
|
||||||
|
}],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":1.5,
|
||||||
|
"width":10
|
||||||
|
}
|
@ -218,6 +218,10 @@ message RefreshRoomMessage{
|
|||||||
message WorldFullMessage{
|
message WorldFullMessage{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message WorldConnexionMessage{
|
||||||
|
string message = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message BanUserMessage{
|
message BanUserMessage{
|
||||||
string type = 1;
|
string type = 1;
|
||||||
string message = 2;
|
string message = 2;
|
||||||
@ -242,6 +246,7 @@ message ServerToClientMessage {
|
|||||||
WorldFullWarningMessage worldFullWarningMessage = 15;
|
WorldFullWarningMessage worldFullWarningMessage = 15;
|
||||||
WorldFullMessage worldFullMessage = 16;
|
WorldFullMessage worldFullMessage = 16;
|
||||||
RefreshRoomMessage refreshRoomMessage = 17;
|
RefreshRoomMessage refreshRoomMessage = 17;
|
||||||
|
WorldConnexionMessage worldConnexionMessage = 18;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,15 +117,15 @@ export class IoSocketController {
|
|||||||
upgradeAborted.aborted = true;
|
upgradeAborted.aborted = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
const url = req.getUrl();
|
||||||
const url = req.getUrl();
|
const query = parse(req.getQuery());
|
||||||
const query = parse(req.getQuery());
|
const websocketKey = req.getHeader('sec-websocket-key');
|
||||||
const websocketKey = req.getHeader('sec-websocket-key');
|
const websocketProtocol = req.getHeader('sec-websocket-protocol');
|
||||||
const websocketProtocol = req.getHeader('sec-websocket-protocol');
|
const websocketExtensions = req.getHeader('sec-websocket-extensions');
|
||||||
const websocketExtensions = req.getHeader('sec-websocket-extensions');
|
const IPAddress = req.getHeader('x-forwarded-for');
|
||||||
const IPAddress = req.getHeader('x-forwarded-for');
|
|
||||||
|
|
||||||
const roomId = query.roomId;
|
const roomId = query.roomId;
|
||||||
|
try {
|
||||||
if (typeof roomId !== 'string') {
|
if (typeof roomId !== 'string') {
|
||||||
throw new Error('Undefined room ID: ');
|
throw new Error('Undefined room ID: ');
|
||||||
}
|
}
|
||||||
@ -183,14 +183,15 @@ export class IoSocketController {
|
|||||||
console.warn('Cannot find user with uuid "'+userUuid+'". Performing an anonymous login instead.');
|
console.warn('Cannot find user with uuid "'+userUuid+'". Performing an anonymous login instead.');
|
||||||
} else if(err?.response?.status == 403) {
|
} else if(err?.response?.status == 403) {
|
||||||
// If we get an HTTP 404, the world is full. We need to broadcast a special error to the client.
|
// If we get an HTTP 404, the world is full. We need to broadcast a special error to the client.
|
||||||
// we finish immediatly the upgrade then we will close the socket as soon as it starts opening.
|
// we finish immediately the upgrade then we will close the socket as soon as it starts opening.
|
||||||
res.upgrade({
|
return res.upgrade({
|
||||||
rejected: true,
|
rejected: true,
|
||||||
|
message: err?.response?.data.message,
|
||||||
|
status: err?.response?.status
|
||||||
}, websocketKey,
|
}, websocketKey,
|
||||||
websocketProtocol,
|
websocketProtocol,
|
||||||
websocketExtensions,
|
websocketExtensions,
|
||||||
context);
|
context);
|
||||||
return;
|
|
||||||
}else{
|
}else{
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@ -199,15 +200,15 @@ export class IoSocketController {
|
|||||||
memberTags = userData.tags;
|
memberTags = userData.tags;
|
||||||
memberTextures = userData.textures;
|
memberTextures = userData.textures;
|
||||||
if (!room.public && room.policyType === GameRoomPolicyTypes.USE_TAGS_POLICY && (userData.anonymous === true || !room.canAccess(memberTags))) {
|
if (!room.public && room.policyType === GameRoomPolicyTypes.USE_TAGS_POLICY && (userData.anonymous === true || !room.canAccess(memberTags))) {
|
||||||
throw new Error('No correct tags')
|
throw new Error('Insufficient privileges to access this room')
|
||||||
}
|
}
|
||||||
if (!room.public && room.policyType === GameRoomPolicyTypes.MEMBERS_ONLY_POLICY && userData.anonymous === true) {
|
if (!room.public && room.policyType === GameRoomPolicyTypes.MEMBERS_ONLY_POLICY && userData.anonymous === true) {
|
||||||
throw new Error('No correct member')
|
throw new Error('Use the login URL to connect')
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('access not granted for user '+userUuid+' and room '+roomId);
|
console.log('access not granted for user '+userUuid+' and room '+roomId);
|
||||||
console.error(e);
|
console.error(e);
|
||||||
throw new Error('Client cannot acces this ressource.')
|
throw new Error('User cannot access this world')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,21 +255,33 @@ export class IoSocketController {
|
|||||||
context);
|
context);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
/*if (e instanceof Error) {
|
||||||
console.log(e.message);
|
console.log(e.message);
|
||||||
res.writeStatus("401 Unauthorized").end(e.message);
|
res.writeStatus("401 Unauthorized").end(e.message);
|
||||||
} else {
|
} else {
|
||||||
res.writeStatus("500 Internal Server Error").end('An error occurred');
|
res.writeStatus("500 Internal Server Error").end('An error occurred');
|
||||||
}
|
}*/
|
||||||
return;
|
return res.upgrade({
|
||||||
|
rejected: true,
|
||||||
|
message: e.message ? e.message : '500 Internal Server Error'
|
||||||
|
}, websocketKey,
|
||||||
|
websocketProtocol,
|
||||||
|
websocketExtensions,
|
||||||
|
context);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
},
|
},
|
||||||
/* Handlers */
|
/* Handlers */
|
||||||
open: (ws) => {
|
open: (ws) => {
|
||||||
if(ws.rejected === true) {
|
if(ws.rejected === true) {
|
||||||
socketManager.emitWorldFullMessage(ws);
|
//FIX ME to use status code
|
||||||
|
if(ws.message === 'World is full'){
|
||||||
|
socketManager.emitWorldFullMessage(ws);
|
||||||
|
}else{
|
||||||
|
socketManager.emitConnexionErrorMessage(ws, ws.message as string);
|
||||||
|
}
|
||||||
ws.close();
|
ws.close();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's join the room
|
// Let's join the room
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
CharacterLayerMessage,
|
CharacterLayerMessage,
|
||||||
PusherToBackMessage,
|
PusherToBackMessage,
|
||||||
WorldFullMessage,
|
WorldFullMessage,
|
||||||
|
WorldConnexionMessage,
|
||||||
AdminPusherToBackMessage,
|
AdminPusherToBackMessage,
|
||||||
ServerToAdminClientMessage,
|
ServerToAdminClientMessage,
|
||||||
UserJoinedRoomMessage, UserLeftRoomMessage, AdminMessage, BanMessage, RefreshRoomMessage
|
UserJoinedRoomMessage, UserLeftRoomMessage, AdminMessage, BanMessage, RefreshRoomMessage
|
||||||
@ -560,6 +561,16 @@ export class SocketManager implements ZoneEventListener {
|
|||||||
client.send(serverToClientMessage.serializeBinary().buffer, true);
|
client.send(serverToClientMessage.serializeBinary().buffer, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public emitConnexionErrorMessage(client: WebSocket, message: string) {
|
||||||
|
const errorMessage = new WorldConnexionMessage();
|
||||||
|
errorMessage.setMessage(message);
|
||||||
|
|
||||||
|
const serverToClientMessage = new ServerToClientMessage();
|
||||||
|
serverToClientMessage.setWorldconnexionmessage(errorMessage);
|
||||||
|
|
||||||
|
client.send(serverToClientMessage.serializeBinary().buffer, true);
|
||||||
|
}
|
||||||
|
|
||||||
private refreshRoomData(roomId: string, versionNumber: number): void {
|
private refreshRoomData(roomId: string, versionNumber: number): void {
|
||||||
const room = this.rooms.get(roomId);
|
const room = this.rooms.get(roomId);
|
||||||
//this function is run for every users connected to the room, so we need to make sure the room wasn't already refreshed.
|
//this function is run for every users connected to the room, so we need to make sure the room wasn't already refreshed.
|
||||||
|