Merge pull request #939 from thecodingmachine/develop
Release 26/04/2021
@ -13,3 +13,7 @@ TURN_STATIC_AUTH_SECRET=
|
||||
|
||||
# The email address used by Let's encrypt to send renewal warnings (compulsory)
|
||||
ACME_EMAIL=
|
||||
|
||||
MAX_PER_GROUP=4
|
||||
MAX_USERNAME_LENGTH=8
|
||||
|
||||
|
10
CHANGELOG.md
Normal file
@ -0,0 +1,10 @@
|
||||
## Version 1.3.0 - in dev
|
||||
|
||||
### New Features
|
||||
|
||||
* Maps can now contain "group" layers (layers that contain other layers) - #899 #779 (@Lurkars @moufmouf)
|
||||
|
||||
### Updates
|
||||
|
||||
|
||||
### Bug Fixes
|
@ -11,6 +11,7 @@ const HTTP_PORT = parseInt(process.env.HTTP_PORT || '8080') || 8080;
|
||||
const GRPC_PORT = parseInt(process.env.GRPC_PORT || '50051') || 50051;
|
||||
export const SOCKET_IDLE_TIMER = parseInt(process.env.SOCKET_IDLE_TIMER as string) || 30; // maximum time (in second) without activity before a socket is closed
|
||||
export const TURN_STATIC_AUTH_SECRET = process.env.TURN_STATIC_AUTH_SECRET || '';
|
||||
export const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || '4');
|
||||
|
||||
export {
|
||||
MINIMUM_DISTANCE,
|
||||
|
@ -105,7 +105,8 @@ export class GameRoom {
|
||||
socket,
|
||||
joinRoomMessage.getTagList(),
|
||||
joinRoomMessage.getName(),
|
||||
ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList())
|
||||
ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList()),
|
||||
joinRoomMessage.getCompanion()
|
||||
);
|
||||
this.nextUserId++;
|
||||
this.users.set(user.id, user);
|
||||
|
@ -4,9 +4,9 @@ import {PositionInterface} from "_Model/PositionInterface";
|
||||
import {Movable} from "_Model/Movable";
|
||||
import {PositionNotifier} from "_Model/PositionNotifier";
|
||||
import {gaugeManager} from "../Services/GaugeManager";
|
||||
import {MAX_PER_GROUP} from "../Enum/EnvironmentVariable";
|
||||
|
||||
export class Group implements Movable {
|
||||
static readonly MAX_PER_GROUP = 4;
|
||||
|
||||
private static nextId: number = 1;
|
||||
|
||||
@ -88,7 +88,7 @@ export class Group implements Movable {
|
||||
}
|
||||
|
||||
isFull(): boolean {
|
||||
return this.users.size >= Group.MAX_PER_GROUP;
|
||||
return this.users.size >= MAX_PER_GROUP;
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
|
@ -4,7 +4,7 @@ import {Zone} from "_Model/Zone";
|
||||
import {Movable} from "_Model/Movable";
|
||||
import {PositionNotifier} from "_Model/PositionNotifier";
|
||||
import {ServerDuplexStream} from "grpc";
|
||||
import {BatchMessage, PusherToBackMessage, ServerToClientMessage, SubMessage} from "../Messages/generated/messages_pb";
|
||||
import {BatchMessage, CompanionMessage, PusherToBackMessage, ServerToClientMessage, SubMessage} from "../Messages/generated/messages_pb";
|
||||
import {CharacterLayer} from "_Model/Websocket/CharacterLayer";
|
||||
|
||||
export type UserSocket = ServerDuplexStream<PusherToBackMessage, ServerToClientMessage>;
|
||||
@ -23,7 +23,8 @@ export class User implements Movable {
|
||||
public readonly socket: UserSocket,
|
||||
public readonly tags: string[],
|
||||
public readonly name: string,
|
||||
public readonly characterLayers: CharacterLayer[]
|
||||
public readonly characterLayers: CharacterLayer[],
|
||||
public readonly companion?: CompanionMessage
|
||||
) {
|
||||
this.listenedZones = new Set<Zone>();
|
||||
|
||||
|
@ -296,6 +296,7 @@ export class SocketManager {
|
||||
userJoinedZoneMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers));
|
||||
userJoinedZoneMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition()));
|
||||
userJoinedZoneMessage.setFromzone(this.toProtoZone(fromZone));
|
||||
userJoinedZoneMessage.setCompanion(thing.companion);
|
||||
|
||||
const subMessage = new SubToPusherMessage();
|
||||
subMessage.setUserjoinedzonemessage(userJoinedZoneMessage);
|
||||
@ -509,19 +510,6 @@ export class SocketManager {
|
||||
return this.rooms;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param token
|
||||
*/
|
||||
/*searchClientByUuid(uuid: string): ExSocketInterface | null {
|
||||
for(const socket of this.sockets.values()){
|
||||
if(socket.userUuid === uuid){
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}*/
|
||||
|
||||
|
||||
public handleQueryJitsiJwtMessage(user: User, queryJitsiJwtMessage: QueryJitsiJwtMessage) {
|
||||
const room = queryJitsiJwtMessage.getJitsiroom();
|
||||
@ -605,6 +593,7 @@ export class SocketManager {
|
||||
userJoinedMessage.setName(thing.name);
|
||||
userJoinedMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers));
|
||||
userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition()));
|
||||
userJoinedMessage.setCompanion(thing.companion);
|
||||
|
||||
const subMessage = new SubToPusherMessage();
|
||||
subMessage.setUserjoinedzonemessage(userJoinedMessage);
|
||||
|
@ -116,6 +116,7 @@ services:
|
||||
ADMIN_API_TOKEN: "$ADMIN_API_TOKEN"
|
||||
JITSI_URL: $JITSI_URL
|
||||
JITSI_ISS: $JITSI_ISS
|
||||
MAX_PER_GROUP: "$MAX_PER_GROUP"
|
||||
volumes:
|
||||
- ./back:/usr/src/app
|
||||
labels:
|
||||
|
@ -38,6 +38,8 @@ services:
|
||||
TURN_USER: ""
|
||||
TURN_PASSWORD: ""
|
||||
START_ROOM_URL: "$START_ROOM_URL"
|
||||
MAX_PER_GROUP: "$MAX_PER_GROUP"
|
||||
MAX_USERNAME_LENGTH: "$MAX_USERNAME_LENGTH"
|
||||
command: yarn run start
|
||||
volumes:
|
||||
- ./front:/usr/src/app
|
||||
@ -110,6 +112,7 @@ services:
|
||||
JITSI_URL: $JITSI_URL
|
||||
JITSI_ISS: $JITSI_ISS
|
||||
TURN_STATIC_AUTH_SECRET: SomeStaticAuthSecret
|
||||
MAX_PER_GROUP: "MAX_PER_GROUP"
|
||||
volumes:
|
||||
- ./back:/usr/src/app
|
||||
labels:
|
||||
|
159
front/dist/resources/html/CustomCharacterScene.html
vendored
Normal file
@ -0,0 +1,159 @@
|
||||
<style>
|
||||
*{
|
||||
font-family: PixelFont-7,monospace!important;
|
||||
}
|
||||
#customizeScene {
|
||||
background: #0000;
|
||||
/*border: 1px solid #ebeeee;*/
|
||||
border-radius: 6px;
|
||||
margin: 10px auto 0;
|
||||
color: #ebeeee;
|
||||
overflow: scroll;
|
||||
width: 42vw;
|
||||
height: 38vh;
|
||||
/*max-width: 300px;*/
|
||||
max-height: 40vh;
|
||||
}
|
||||
#customizeScene h1 {
|
||||
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
|
||||
border-bottom: 1px solid #a6abaf;
|
||||
border-radius: 6px 6px 0 0;
|
||||
box-sizing: border-box;
|
||||
color: #727678;
|
||||
display: block;
|
||||
height: 43px;
|
||||
padding-top: 10px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
|
||||
}
|
||||
#customizeScene input {
|
||||
font-size: 70%;
|
||||
background: linear-gradient(top, #d6d7d7, #dee0e0);
|
||||
border: 1px solid #a1a3a3;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px #fff;
|
||||
box-sizing: border-box;
|
||||
color: #696969;
|
||||
height: 30px;
|
||||
transition: box-shadow 0.3s;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
#customizeScene section {
|
||||
margin: 10px;
|
||||
}
|
||||
#customizeScene section.action {
|
||||
text-align: center;
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
top: 100%;
|
||||
}
|
||||
#customizeScene section.action.action-move {
|
||||
top: 55%;
|
||||
}
|
||||
#customizeScene button {
|
||||
margin: 2px 10px;
|
||||
background-color: black;;
|
||||
color: #ebeeee;
|
||||
border-radius: 7px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
#customizeScene button#customizeSceneFormCancel {
|
||||
background-color: #aca6a600;
|
||||
color: #292929;
|
||||
}
|
||||
#customizeScene section h6,
|
||||
#customizeScene section h5{
|
||||
margin: 1px;
|
||||
}
|
||||
#customizeScene section.text-center{
|
||||
text-align: center;
|
||||
}
|
||||
#customizeScene section a{
|
||||
font-size: 14px;
|
||||
text-decoration: underline;
|
||||
color: #ebeeee;
|
||||
}
|
||||
#customizeScene section a:hover{
|
||||
font-weight: 700;
|
||||
}
|
||||
#customizeScene section p{
|
||||
text-align: left;
|
||||
font-size: 8px;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
#customizeScene section p.err{
|
||||
color: red;
|
||||
text-align: center;
|
||||
}
|
||||
#customizeScene section p.info{
|
||||
display: none;
|
||||
text-align: center;
|
||||
}
|
||||
#customizeScene section input#customizeSceneLink{
|
||||
background-color: #a1a3a3;
|
||||
}
|
||||
#customizeScene section button.customizeSceneButton{
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
top: -8vh;
|
||||
font-size: 10px;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
#customizeScene section button.customizeSceneButton#customizeSceneButtonLeft{
|
||||
left: 0vw;
|
||||
}
|
||||
#customizeScene section button.customizeSceneButton#customizeSceneButtonRight{
|
||||
right: 0vw;
|
||||
}
|
||||
#customizeScene section button.customizeSceneButton#customizeSceneButtonUp{
|
||||
left: calc(2vw + 40px);
|
||||
transform: rotate(90deg);
|
||||
margin-top: -20px;
|
||||
}
|
||||
#customizeScene section button.customizeSceneButton#customizeSceneButtonDown{
|
||||
right: calc(2vw + 40px);
|
||||
transform: rotate(90deg);
|
||||
margin-top: 20px;
|
||||
}
|
||||
#customizeScene section.action img{
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
#customizeScene section.action a#customizeSceneFormBack img{
|
||||
margin-top: -2px;
|
||||
}
|
||||
#customizeScene section.action button#customizeSceneFormSubmit img{
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
@media only screen and (max-width: 600px) {
|
||||
#customizeScene {
|
||||
max-width: 160px;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-height: 400px) {
|
||||
#customizeScene section.action {
|
||||
top: 92%;
|
||||
}
|
||||
#customizeScene section.action.action-move {
|
||||
top: 80%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<form id="customizeScene" hidden>
|
||||
<section class="text-center">
|
||||
<h5>Custom your WOKA</h3>
|
||||
</section>
|
||||
<section class="action action-move">
|
||||
<button class="customizeSceneButton" id="customizeSceneButtonLeft"> < </button>
|
||||
<button class="customizeSceneButton" id="customizeSceneButtonRight"> > </button>
|
||||
<!--<button class="customizeSceneButton" id="customizeSceneButtonUp"> < </button>
|
||||
<button class="customizeSceneButton" id="customizeSceneButtonDown"> > </button>-->
|
||||
</section>
|
||||
<section class="action">
|
||||
<a type="submit" id="customizeSceneFormBack">Back <img src="resources/objects/arrow_up.png"/></a>
|
||||
<button type="submit" id="customizeSceneFormSubmit">Next <img src="resources/objects/arrow_up.png"/></button>
|
||||
</section>
|
||||
</form>
|
123
front/dist/resources/html/EnableCameraScene.html
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
<style>
|
||||
*{
|
||||
font-family: PixelFont-7,monospace!important;
|
||||
}
|
||||
#enableCameraScene {
|
||||
background: #000000;
|
||||
/*border: 1px solid #ebeeee;*/
|
||||
border-radius: 6px;
|
||||
margin: 20px auto 0;
|
||||
color: #ebeeee;
|
||||
max-height: 40vh;
|
||||
width: 42vw;
|
||||
max-width: 300px;
|
||||
/*overflow: scroll;*/
|
||||
}
|
||||
#enableCameraScene h1 {
|
||||
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
|
||||
border-bottom: 1px solid #a6abaf;
|
||||
border-radius: 6px 6px 0 0;
|
||||
box-sizing: border-box;
|
||||
color: #727678;
|
||||
display: block;
|
||||
height: 43px;
|
||||
padding-top: 10px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
|
||||
}
|
||||
#enableCameraScene input {
|
||||
font-size: 70%;
|
||||
background: linear-gradient(top, #d6d7d7, #dee0e0);
|
||||
border: 1px solid #a1a3a3;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px #fff;
|
||||
box-sizing: border-box;
|
||||
color: #696969;
|
||||
height: 30px;
|
||||
transition: box-shadow 0.3s;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
#enableCameraScene section.title {
|
||||
position: absolute;
|
||||
top: 1vh;
|
||||
width: 100%;
|
||||
}
|
||||
#enableCameraScene section.action{
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 40vh;
|
||||
width: 100%;
|
||||
}
|
||||
#enableCameraScene button {
|
||||
margin: 10px;
|
||||
background-color: black;;
|
||||
color: #ebeeee;
|
||||
border-radius: 7px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
#enableCameraScene button#enableCameraSceneFormCancel {
|
||||
background-color: #c7c7c700;
|
||||
color: #292929;
|
||||
}
|
||||
#enableCameraScene section h6,
|
||||
#enableCameraScene section h5{
|
||||
margin: 1px;
|
||||
}
|
||||
#enableCameraScene section.text-center{
|
||||
text-align: center;
|
||||
}
|
||||
#enableCameraScene section a{
|
||||
font-size: 8px;
|
||||
text-decoration: underline;
|
||||
color: #ebeeee;
|
||||
}
|
||||
#enableCameraScene section a:hover{
|
||||
font-weight: 700;
|
||||
}
|
||||
#enableCameraScene section p{
|
||||
text-align: left;
|
||||
font-size: 8px;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
#enableCameraScene section p.err{
|
||||
color: red;
|
||||
text-align: center;
|
||||
}
|
||||
#enableCameraScene section p.info{
|
||||
display: none;
|
||||
text-align: center;
|
||||
}
|
||||
#enableCameraScene section input#enableCameraSceneLink{
|
||||
background-color: #a1a3a3;
|
||||
}
|
||||
#enableCameraScene section img{
|
||||
width: 160px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<form id="enableCameraScene" hidden>
|
||||
<!-- FIX me -->
|
||||
<section class="title text-center">
|
||||
<h5>Turn on your camera and microphone</h5>
|
||||
</section>
|
||||
<!--<section class="text-center">
|
||||
<video id="myCamVideoSetup" autoplay muted></video>
|
||||
</section>
|
||||
<section class="text-center">
|
||||
<h5>Select your camera</h3>
|
||||
<select id="camera">
|
||||
</select>
|
||||
</section>
|
||||
<section class="text-center">
|
||||
<h5>Select your michrophone</h3>
|
||||
<select id="michrophone">
|
||||
</select>
|
||||
</section>-->
|
||||
<section class="action">
|
||||
<button type="submit" id="enableCameraSceneFormSubmit">Let's go!</button>
|
||||
</section>
|
||||
</form>
|
140
front/dist/resources/html/SelectCharacterScene.html
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
<style>
|
||||
*{
|
||||
font-family: PixelFont-7,monospace!important;
|
||||
}
|
||||
#selectCharacterScene {
|
||||
background: #0000;
|
||||
/*border: 1px solid #ebeeee;*/
|
||||
border-radius: 6px;
|
||||
margin: 10px auto 0;
|
||||
color: #ebeeee;
|
||||
max-height: 40vh;
|
||||
overflow: scroll;
|
||||
max-width: 300px;
|
||||
width: 40vw;
|
||||
overflow: unset;
|
||||
}
|
||||
#selectCharacterScene h1 {
|
||||
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
|
||||
border-bottom: 1px solid #a6abaf;
|
||||
border-radius: 6px 6px 0 0;
|
||||
box-sizing: border-box;
|
||||
color: #727678;
|
||||
display: block;
|
||||
height: 43px;
|
||||
padding-top: 10px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
|
||||
}
|
||||
#selectCharacterScene input {
|
||||
font-size: 70%;
|
||||
background: linear-gradient(top, #d6d7d7, #dee0e0);
|
||||
border: 1px solid #a1a3a3;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px #fff;
|
||||
box-sizing: border-box;
|
||||
color: #696969;
|
||||
height: 30px;
|
||||
transition: box-shadow 0.3s;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
#selectCharacterScene section {
|
||||
margin: 10px;
|
||||
}
|
||||
#selectCharacterScene section.action {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
margin-top: 28vh;
|
||||
}
|
||||
#selectCharacterScene button {
|
||||
margin: 10px 0px;
|
||||
background-color: black;;
|
||||
color: #ebeeee;
|
||||
border-radius: 7px;
|
||||
padding-bottom: 4px;
|
||||
width: 100px;
|
||||
}
|
||||
#selectCharacterScene button#selectCharacterSceneFormCancel {
|
||||
background-color: #aca6a600;
|
||||
color: #292929;
|
||||
}
|
||||
#selectCharacterScene section h6,
|
||||
#selectCharacterScene section h5{
|
||||
margin: 1px;
|
||||
}
|
||||
#selectCharacterScene section.text-center{
|
||||
text-align: center;
|
||||
}
|
||||
#selectCharacterScene section a{
|
||||
font-size: 8px;
|
||||
text-decoration: underline;
|
||||
color: #ebeeee;
|
||||
}
|
||||
#selectCharacterScene section a:hover{
|
||||
font-weight: 700;
|
||||
}
|
||||
#selectCharacterScene section p{
|
||||
text-align: left;
|
||||
font-size: 8px;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
#selectCharacterScene section p.err{
|
||||
color: red;
|
||||
text-align: center;
|
||||
}
|
||||
#selectCharacterScene section p.info{
|
||||
display: none;
|
||||
text-align: center;
|
||||
}
|
||||
#selectCharacterScene section input#selectCharacterSceneLink{
|
||||
background-color: #a1a3a3;
|
||||
}
|
||||
#selectCharacterScene section img{
|
||||
width: 160px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
#selectCharacterScene section button.selectCharacterButton{
|
||||
position: absolute;
|
||||
top: 20vh;
|
||||
margin: 0;
|
||||
width: 25px;
|
||||
}
|
||||
#selectCharacterScene section button.selectCharacterButton#selectCharacterButtonLeft{
|
||||
display: none;
|
||||
left: 1vw;
|
||||
}
|
||||
#selectCharacterScene section button.selectCharacterButton#selectCharacterButtonRight{
|
||||
display: none;
|
||||
right: 1vw;
|
||||
}
|
||||
#selectCharacterScene section button#selectCharacterSceneFormCustomYourOwnSubmit{
|
||||
font-size: 14px;
|
||||
width: auto;
|
||||
margin-top: -2px;
|
||||
background-color: #ffd700;
|
||||
color: black;
|
||||
}
|
||||
@media only screen and (max-width: 800px),
|
||||
only screen and (max-height: 600px) {
|
||||
#selectCharacterScene section button.selectCharacterButton#selectCharacterButtonRight{
|
||||
display: block;
|
||||
}
|
||||
#selectCharacterScene section button.selectCharacterButton#selectCharacterButtonLeft{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<form id="selectCharacterScene" hidden>
|
||||
<section class="text-center">
|
||||
<h5>Select your WOKA</h5>
|
||||
<button class="selectCharacterButton" id="selectCharacterButtonLeft"> < </button>
|
||||
<button class="selectCharacterButton" id="selectCharacterButtonRight"> > </button>
|
||||
</section>
|
||||
<section class="action">
|
||||
<button type="submit" id="selectCharacterSceneFormSubmit">Continue</button>
|
||||
<button type="submit" id="selectCharacterSceneFormCustomYourOwnSubmit">Custom your WOKA</button>
|
||||
</section>
|
||||
</form>
|
128
front/dist/resources/html/SelectCompanionScene.html
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
<style>
|
||||
*{
|
||||
font-family: PixelFont-7,monospace!important;
|
||||
}
|
||||
#selectCompanionScene {
|
||||
background: #0000;
|
||||
/*border: 1px solid #ebeeee;*/
|
||||
border-radius: 6px;
|
||||
margin: 10px auto 0;
|
||||
color: #ebeeee;
|
||||
max-height: 40vh;
|
||||
overflow: scroll;
|
||||
max-width: 300px;
|
||||
width: 40vw;
|
||||
}
|
||||
#selectCompanionScene h1 {
|
||||
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
|
||||
border-bottom: 1px solid #a6abaf;
|
||||
border-radius: 6px 6px 0 0;
|
||||
box-sizing: border-box;
|
||||
color: #727678;
|
||||
display: block;
|
||||
height: 43px;
|
||||
padding-top: 10px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
|
||||
}
|
||||
#selectCompanionScene input {
|
||||
font-size: 70%;
|
||||
background: linear-gradient(top, #d6d7d7, #dee0e0);
|
||||
border: 1px solid #a1a3a3;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px #fff;
|
||||
box-sizing: border-box;
|
||||
color: #696969;
|
||||
height: 30px;
|
||||
transition: box-shadow 0.3s;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
#selectCompanionScene section {
|
||||
margin: 10px;
|
||||
}
|
||||
#selectCompanionScene section.action {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
margin-top: 20vh;
|
||||
}
|
||||
#selectCompanionScene button {
|
||||
margin: 10px 4px;
|
||||
background-color: black;;
|
||||
color: #ebeeee;
|
||||
border-radius: 7px;
|
||||
padding-bottom: 4px;
|
||||
width: 100px;
|
||||
}
|
||||
#selectCompanionScene button#selectCompanionSceneFormCancel {
|
||||
background-color: #aca6a600;
|
||||
color: #292929;
|
||||
}
|
||||
#selectCompanionScene section h6,
|
||||
#selectCompanionScene section h5{
|
||||
margin: 1px;
|
||||
}
|
||||
#selectCompanionScene section.text-center{
|
||||
text-align: center;
|
||||
}
|
||||
#selectCompanionScene section a{
|
||||
font-size: 14px;
|
||||
text-decoration: underline;
|
||||
color: #ebeeee;
|
||||
}
|
||||
#selectCompanionScene section a:hover{
|
||||
font-weight: 700;
|
||||
}
|
||||
#selectCompanionScene section p{
|
||||
text-align: left;
|
||||
font-size: 8px;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
#selectCompanionScene section p.err{
|
||||
color: red;
|
||||
text-align: center;
|
||||
}
|
||||
#selectCompanionScene section p.info{
|
||||
display: none;
|
||||
text-align: center;
|
||||
}
|
||||
#selectCompanionScene section input#selectCompanionSceneLink{
|
||||
background-color: #a1a3a3;
|
||||
}
|
||||
#selectCompanionScene section img{
|
||||
width: 160px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
#selectCompanionScene section button.selectCharacterButton{
|
||||
position: absolute;
|
||||
top: 20vh;
|
||||
margin: 0;
|
||||
width: 25px;
|
||||
}
|
||||
#selectCompanionScene section button.selectCharacterButton#selectCharacterButtonLeft{
|
||||
left: 1vw;
|
||||
}
|
||||
#selectCompanionScene section button.selectCharacterButton#selectCharacterButtonRight{
|
||||
right: 1vw;
|
||||
}
|
||||
#selectCompanionScene section button#selectCompanionSceneFormCustomYourOwnSubmit{
|
||||
font-size: 14px;
|
||||
width: auto;
|
||||
margin-top: -2px;
|
||||
background-color: #ffd700;
|
||||
color: black;
|
||||
}
|
||||
</style>
|
||||
|
||||
<form id="selectCompanionScene" hidden>
|
||||
<section class="text-center">
|
||||
<h5>Select your WOKA</h5>
|
||||
<button class="selectCharacterButton" id="selectCharacterButtonLeft"> < </button>
|
||||
<button class="selectCharacterButton" id="selectCharacterButtonRight"> > </button>
|
||||
</section>
|
||||
<section class="action">
|
||||
<a herf="#" id="selectCompanionSceneFormBack">Back</a>
|
||||
<button type="submit" id="selectCompanionSceneFormSubmit">Continue</button>
|
||||
</section>
|
||||
</form>
|
27
front/dist/resources/html/gameMenu.html
vendored
@ -1,4 +1,10 @@
|
||||
<style>
|
||||
*{
|
||||
font-family: PixelFont-7,monospace!important;
|
||||
}
|
||||
#gameMenu main{
|
||||
margin-top: 15px;
|
||||
}
|
||||
#gameMenu button {
|
||||
background-color: black;
|
||||
color: white;
|
||||
@ -16,6 +22,21 @@
|
||||
width: 32px;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
}
|
||||
@media only screen and (max-height: 700px) {
|
||||
#gameMenu main {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 0;
|
||||
}
|
||||
#gameMenu section{
|
||||
margin: 2px;
|
||||
}
|
||||
section#socialLinks{
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="gameMenu" hidden>
|
||||
@ -30,9 +51,15 @@
|
||||
<section>
|
||||
<button id="changeSkinButton">Edit skin</button>
|
||||
</section>
|
||||
<section>
|
||||
<button id="changeCompanionButton">Edit companion</button>
|
||||
</section>
|
||||
<section>
|
||||
<button id="editGameSettingsButton">Settings</button>
|
||||
</section>
|
||||
<section>
|
||||
<button id="toggleFullscreen">Toggle fullscreen</button>
|
||||
</section>
|
||||
<section>
|
||||
<button id="sparkButton">Create map</button>
|
||||
</section>
|
||||
|
11
front/dist/resources/html/gameMenuIcon.html
vendored
@ -1,10 +1,12 @@
|
||||
<style>
|
||||
*{
|
||||
font-family: PixelFont-7,monospace!important;
|
||||
}
|
||||
#menuIcon button {
|
||||
background-color: black;
|
||||
color: white;
|
||||
border-radius: 7px;
|
||||
height: 28px;
|
||||
width: 34px;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
#menuIcon button img{
|
||||
width: 14px;
|
||||
@ -14,6 +16,11 @@
|
||||
#menuIcon section {
|
||||
margin: 10px;
|
||||
}
|
||||
@media only screen and (max-height: 700px) {
|
||||
#menuIcon section {
|
||||
margin: 2px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<main id="menuIcon" hidden>
|
||||
<section>
|
||||
|
16
front/dist/resources/html/gameQualityMenu.html
vendored
@ -1,11 +1,14 @@
|
||||
<style>
|
||||
*{
|
||||
font-family: PixelFont-7,monospace!important;
|
||||
}
|
||||
#gameQuality {
|
||||
background: #eceeee;
|
||||
border: 1px solid #42464b;
|
||||
border-radius: 6px;
|
||||
height: 257px;
|
||||
margin: 20px auto 0;
|
||||
width: 298px;
|
||||
width: 50vw;
|
||||
max-width: 300px;
|
||||
}
|
||||
#gameQuality .cautiousText {
|
||||
font-size: 50%;
|
||||
@ -33,7 +36,7 @@
|
||||
color: #696969;
|
||||
height: 30px;
|
||||
transition: box-shadow 0.3s;
|
||||
width: 240px;
|
||||
width: 100%;
|
||||
}
|
||||
#gameQuality section {
|
||||
margin: 10px;
|
||||
@ -42,12 +45,11 @@
|
||||
text-align: center;
|
||||
}
|
||||
#gameQuality button {
|
||||
margin-top: 10px;
|
||||
margin: 10px;
|
||||
background-color: black;
|
||||
color: white;
|
||||
border-radius: 7px;
|
||||
padding-bottom: 4px;
|
||||
width: 60px;
|
||||
}
|
||||
#gameQuality button#gameQualityFormCancel {
|
||||
background-color: #c7c7c700;
|
||||
@ -57,7 +59,7 @@
|
||||
|
||||
<form id="gameQuality" hidden>
|
||||
<section>
|
||||
<h3>Game quality</h3>
|
||||
<h5>Game quality</h3>
|
||||
<p class="cautiousText">(Editing these settings will restart the game)</p>
|
||||
<select id="select-game-quality">
|
||||
<option value="120">High video quality (120 fps)</option>
|
||||
@ -67,7 +69,7 @@
|
||||
</select>
|
||||
</section>
|
||||
<section>
|
||||
<h3>Video quality</h3>
|
||||
<h5>Video quality</h3>
|
||||
<select id="select-video-quality">
|
||||
<option value="30">High video quality (30 fps)</option>
|
||||
<option value="20">Medium video quality (20 fps, recommended)</option>
|
||||
|
3
front/dist/resources/html/gameReport.html
vendored
@ -1,4 +1,7 @@
|
||||
<style>
|
||||
*{
|
||||
font-family: PixelFont-7,monospace!important;
|
||||
}
|
||||
#gameReport {
|
||||
background: #eceeee;
|
||||
border: 1px solid #42464b;
|
||||
|
11
front/dist/resources/html/gameShare.html
vendored
@ -1,11 +1,14 @@
|
||||
<style>
|
||||
*{
|
||||
font-family: PixelFont-7,monospace!important;
|
||||
}
|
||||
#gameShare {
|
||||
background: #eceeee;
|
||||
border: 1px solid #42464b;
|
||||
border-radius: 6px;
|
||||
margin: 20px auto 0;
|
||||
width: 298px;
|
||||
height: 160px;
|
||||
width: 50vw;
|
||||
max-width: 400px;
|
||||
}
|
||||
#gameShare h1 {
|
||||
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
|
||||
@ -40,7 +43,7 @@
|
||||
margin: 0;
|
||||
}
|
||||
#gameShare button {
|
||||
margin-top: 10px;
|
||||
margin: 10px;
|
||||
background-color: black;
|
||||
color: white;
|
||||
border-radius: 7px;
|
||||
@ -66,7 +69,7 @@
|
||||
}
|
||||
#gameShare section p{
|
||||
font-size: 8px;
|
||||
margin: 0px 70px;
|
||||
margin: 0;
|
||||
}
|
||||
#gameShare section p.err{
|
||||
color: red;
|
||||
|
@ -1,11 +1,16 @@
|
||||
<style>
|
||||
*{
|
||||
font-family: PixelFont-7,monospace!important;
|
||||
}
|
||||
#helpCameraSettings {
|
||||
background: #eceeee;
|
||||
border: 1px solid #42464b;
|
||||
border-radius: 6px;
|
||||
margin: 10px auto 0;
|
||||
margin: 25px auto 0;
|
||||
width: 400px;
|
||||
height: 370px;
|
||||
max-height: calc(50vh - 25px);
|
||||
overflow: scroll;
|
||||
max-width: 48vw;
|
||||
}
|
||||
#helpCameraSettings h1 {
|
||||
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
|
||||
@ -20,18 +25,6 @@
|
||||
text-align: center;
|
||||
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
|
||||
}
|
||||
#helpCameraSettings input {
|
||||
font-size: 70%;
|
||||
background: linear-gradient(top, #d6d7d7, #dee0e0);
|
||||
border: 1px solid #a1a3a3;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px #fff;
|
||||
box-sizing: border-box;
|
||||
color: #696969;
|
||||
height: 30px;
|
||||
transition: box-shadow 0.3s;
|
||||
width: 100%;
|
||||
}
|
||||
#helpCameraSettings section {
|
||||
margin: 10px;
|
||||
}
|
||||
@ -40,7 +33,7 @@
|
||||
margin: 0;
|
||||
}
|
||||
#helpCameraSettings button {
|
||||
margin-top: 10px;
|
||||
margin: 10px 4px;
|
||||
background-color: black;
|
||||
color: white;
|
||||
border-radius: 7px;
|
||||
@ -51,9 +44,8 @@
|
||||
color: #292929;
|
||||
}
|
||||
#helpCameraSettings section a{
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
margin: 0 6px;
|
||||
text-decoration: underline;
|
||||
color: black;
|
||||
}
|
||||
#helpCameraSettings section h6,
|
||||
@ -97,7 +89,7 @@
|
||||
<p id='browserHelpSetting'></p>
|
||||
</section>
|
||||
<section class="action">
|
||||
<button type="submit" id="helpCameraSettingsFormRefresh">Refresh</button>
|
||||
<a href="#" id="helpCameraSettingsFormRefresh">Refresh</a>
|
||||
<button type="submit" id="helpCameraSettingsFormContinue">Continue</button>
|
||||
</section>
|
||||
</form>
|
||||
|
114
front/dist/resources/html/loginScene.html
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
<style>
|
||||
*{
|
||||
font-family: PixelFont-7,monospace!important;
|
||||
}
|
||||
#loginScene {
|
||||
background: #000000;
|
||||
/*border: 1px solid #ebeeee;*/
|
||||
border-radius: 6px;
|
||||
margin: 20px auto 0;
|
||||
width: 90%;
|
||||
max-width: 200px;
|
||||
color: #ebeeee;
|
||||
max-height: 40vh;
|
||||
overflow: scroll;
|
||||
}
|
||||
#loginScene h1 {
|
||||
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
|
||||
border-bottom: 1px solid #a6abaf;
|
||||
border-radius: 6px 6px 0 0;
|
||||
box-sizing: border-box;
|
||||
color: #727678;
|
||||
display: block;
|
||||
height: 43px;
|
||||
padding-top: 10px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
|
||||
}
|
||||
#loginScene input {
|
||||
font-size: 70%;
|
||||
background: linear-gradient(top, #d6d7d7, #dee0e0);
|
||||
border: 1px solid #a1a3a3;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px #fff;
|
||||
box-sizing: border-box;
|
||||
color: #696969;
|
||||
height: 30px;
|
||||
transition: box-shadow 0.3s;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
#loginScene section {
|
||||
margin: 10px;
|
||||
}
|
||||
#loginScene section.action{
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
#loginScene button {
|
||||
margin: 10px;
|
||||
background-color: black;;
|
||||
color: #ebeeee;
|
||||
border-radius: 7px;
|
||||
padding-bottom: 4px;
|
||||
width: 100px;
|
||||
}
|
||||
#loginScene button#loginSceneFormCancel {
|
||||
background-color: #c7c7c700;
|
||||
color: #292929;
|
||||
}
|
||||
#loginScene section h6,
|
||||
#loginScene section h5{
|
||||
margin: 1px;
|
||||
}
|
||||
#loginScene section.text-center{
|
||||
text-align: center;
|
||||
}
|
||||
#loginScene section a{
|
||||
font-size: 8px;
|
||||
text-decoration: underline;
|
||||
color: #ebeeee;
|
||||
}
|
||||
#loginScene section a:hover{
|
||||
font-weight: 700;
|
||||
}
|
||||
#loginScene section p{
|
||||
text-align: left;
|
||||
font-size: 8px;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
#loginScene section p.err{
|
||||
color: red;
|
||||
text-align: center;
|
||||
}
|
||||
#loginScene section p.info{
|
||||
display: none;
|
||||
text-align: center;
|
||||
}
|
||||
#loginScene section input#loginSceneLink{
|
||||
background-color: #a1a3a3;
|
||||
}
|
||||
#loginScene section img{
|
||||
width: 160px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<form id="loginScene" hidden>
|
||||
<section class="text-center">
|
||||
<img src="resources/logos/logo.png">
|
||||
</section>
|
||||
<section class="text-center">
|
||||
<h5>Enter your name</h5>
|
||||
<p class="info">9 chars maximum</p>
|
||||
<p class="err" id="errorLoginScene"></p>
|
||||
</section>
|
||||
<section>
|
||||
<input type="text" name="email" id="loginSceneName">
|
||||
<p>By continuing, you are agreeing our <a href="https://workadventu.re/terms-of-use" target="_blank">terms of use</a>, <a href="https://workadventu.re/privacy-policy" target="_blank">privacy policy</a> and <a href="https://workadventu.re/cookie-policy" target="_blank">cookie policy</a>.</p>
|
||||
</section>
|
||||
<section class="action">
|
||||
<button type="submit" id="loginSceneFormSubmit">Continue</button>
|
||||
</section>
|
||||
</form>
|
@ -1,4 +1,7 @@
|
||||
<style>
|
||||
*{
|
||||
font-family: PixelFont-7,monospace!important;
|
||||
}
|
||||
#warningMain {
|
||||
border-radius: 5px;
|
||||
height: 100px;
|
||||
|
45
front/dist/resources/logos/monitor-close.svg
vendored
@ -1,44 +1 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 480" style="enable-background:new 0 0 512 480;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M411.1,384.2c-12.2,0-24.3,0-36.5,0C259.6,257.6,144.5,130.9,29.5,4.3c11.4-0.8,22.9-1.6,34.3-2.4
|
||||
C179.6,129.3,295.3,256.8,411.1,384.2z"/>
|
||||
<g>
|
||||
<path class="st0" d="M352,152.5c-8.8-8.7-34.2-31.6-74.5-38.1c-32.3-5.2-58.1,2.7-70,7.3C170.9,81.5,134.3,41.3,97.8,1
|
||||
C220.4,1,343,1,465.6,1C427.7,51.5,389.8,102,352,152.5z"/>
|
||||
<path class="st0" d="M511.5,338.3c0,4.7-0.8,12.2-4.7,20.2c-1.2,2.4-3.4,6.3-7.1,10.5c-4,4.4-7.9,7.1-10.2,8.5
|
||||
c-5.6,3.5-10.7,4.9-13.5,5.6c-3.8,0.9-6.7,1-10.2,1.2c-3.6,0.2-5.3,0-13.1,0c-3,0-5.4,0-7,0C414.5,307,383.2,229.8,352,152.5
|
||||
C402.9,62.7,448.7,1,465.6,1c7.5,0,14.3,2.3,14.3,2.3c14.5,4.8,22.3,15.8,23.6,17.8c7.4,10.8,8,21.7,8,25.9
|
||||
C511.5,144.1,511.5,241.2,511.5,338.3z"/>
|
||||
<path class="st0" d="M312.5,192c-5.2-5.2-15.6-14.1-31.4-19.4c-12.8-4.2-24-4.3-30.9-3.8c-6.2-7.1-12.4-14.2-18.6-21.3
|
||||
c10.3-2.4,36.5-6.8,65.1,5.3c15.3,6.5,26.1,15.5,32.7,22.2C323.7,180.8,318.1,186.4,312.5,192z"/>
|
||||
<path class="st0" d="M329.4,175.1c38.8,69.7,77.6,139.4,116.4,209.1c-50.3-55.4-100.6-110.8-151-166.2c6.9,2.9,14.9,0.7,19.2-5.2
|
||||
c4.6-6.2,4-15.1-1.6-20.8C318.1,186.4,323.7,180.8,329.4,175.1z"/>
|
||||
<path class="st0" d="M445.8,384.2L445.8,384.2c-38.8-69.7-77.6-139.4-116.4-209.1c5.3,4.9,12.9,6,18.9,2.7
|
||||
c7.8-4.2,8.3-13.4,8.3-13.8C386.4,237.4,416.1,310.8,445.8,384.2z"/>
|
||||
</g>
|
||||
<path class="st0" d="M162.2,150.4C108.3,213,54.4,275.7,0.5,338.3c0-97.1,0-194.3,0-291.4c0-4,0.6-15.1,8.1-26
|
||||
C16,10.2,25.7,5.8,29.5,4.3C73.7,53,118,101.7,162.2,150.4z"/>
|
||||
<path class="st0" d="M199.5,192c-5.3-6-10.6-12-15.8-18C122.6,228.8,61.6,283.6,0.5,338.3c0,4.1,0.6,15.5,8.6,26.7
|
||||
c1.7,2.4,9.6,12.9,24.1,17.2c5.3,1.6,10,1.9,13.1,1.9C97.5,320.2,148.5,256.1,199.5,192z"/>
|
||||
<path class="st0" d="M84.7,384.2c-12.7,0-25.5,0-38.2,0c58.2-56.2,116.5-112.5,174.7-168.7c8.3,9.1,16.6,18.2,24.9,27.2
|
||||
c-2.2,1.1-5.5,3-8.4,6.4c-3.1,3.7-4.2,7.3-4.4,7.8C231.3,262.4,194.5,295.2,84.7,384.2z"/>
|
||||
<path class="st0" d="M46.4,384.2c-15.3-15.3-30.6-30.6-45.9-45.9C52.8,277.5,105.1,216.8,157.4,156c-3.7,6.7-2.2,15.1,3.5,20
|
||||
c5.4,4.6,13.3,5.1,19.4,1C135.7,246.1,91.1,315.1,46.4,384.2z"/>
|
||||
<path class="st0" d="M49.6,384.3c-1.1,0-2.1,0-3.2-0.1c50.1-62.9,100.2-125.8,150.3-188.7c-3.5,6.6-2.1,14.8,3.4,19.7
|
||||
c5.8,5.2,14.8,5.3,21,0.3C164,271.7,106.8,328,49.6,384.3z"/>
|
||||
<path class="st0" d="M374.6,384.2c-96.6,0-193.3,0-289.9,0C16.5,308,1.3,223,28.5,194.5C57,164.7,150.6,177,233.3,256.9
|
||||
c-3.9,11.8,2,24.8,13.3,29.6c11,4.7,24,0.4,30.2-10C309.3,312.4,342,348.3,374.6,384.2z"/>
|
||||
<path class="st0" d="M219.7,226.7"/>
|
||||
<path class="st0" d="M368.9,480c-74.9,0-149.8,0-224.7,0c-8.8,0-16-7.2-16-16c0-8.8,7.2-16,16-16c74.9,0,149.9,0,224.8,0.1
|
||||
c8.3,0.7,14.7,7.6,14.7,15.9C383.7,472.3,377.2,479.3,368.9,480z"/>
|
||||
<rect x="208.1" y="384.2" class="st0" width="31.9" height="63.9"/>
|
||||
<rect x="272" y="384.2" class="st0" width="32" height="63.9"/>
|
||||
<path class="st0" d="M410.3,395.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
<svg id="Capa_1" data-name="Capa 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 469.33 426.67"><defs><style>.cls-1{fill:#fff;}</style></defs><path class="cls-1" d="M426.67,21.33h-384A42.66,42.66,0,0,0,0,64V320a42.66,42.66,0,0,0,42.67,42.67H192v42.66H149.33V448H320V405.33H277.33V362.67H426.67A42.66,42.66,0,0,0,469.33,320V64A42.66,42.66,0,0,0,426.67,21.33Zm0,298.67h-384V64h384Z" transform="translate(0 -21.33)"/><path class="cls-1" d="M267.2,127.15V86.26a8.14,8.14,0,0,1,14.18-5.44l73.2,81.34a8.12,8.12,0,0,1,.25,10.57l-73.2,89.47a8.14,8.14,0,0,1-14.43-5.13V216.54c-64.25,2.09-104.35,29.55-122.42,83.77a8.13,8.13,0,0,1-15.84-2.58C128.94,202,186.59,131.59,267.2,127.15Zm8.14,73a8.13,8.13,0,0,1,8.13,8.14v26l54.36-66.44-54.36-60.39v27.6a8.13,8.13,0,0,1-8.13,8.14c-63.93,0-111.77,44.24-125.87,111.73C175.65,218.53,217.8,200.13,275.34,200.13Z" transform="translate(0 -21.33)"/></svg>
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 884 B |
29
front/dist/resources/logos/monitor.svg
vendored
@ -1,15 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 480" style="enable-background:new 0 0 512 480;" xml:space="preserve">
|
||||
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 469.3 469.3" style="enable-background:new 0 0 469.3 469.3;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{opacity:0.9;fill:#FFFFFF;stroke:#FFFFFF;stroke-width:15;stroke-miterlimit:10;enable-background:new ;}
|
||||
</style>
|
||||
<path class="st0" d="M466,0H46C20.6,0,0,20.6,0,46v292c0,25.4,20.6,46,46,46h162v64h-64c-8.8,0-16,7.2-16,16s7.2,16,16,16h224
|
||||
c8.8,0,16-7.2,16-16s-7.2-16-16-16h-64v-64h162c25.4,0,46-20.6,46-46V46C512,20.6,491.4,0,466,0z M232,264c0-13.3,10.7-24,24-24
|
||||
c13.3,0,24,10.7,24,24s-10.7,24-24,24C242.7,288,232,277.3,232,264z M272,448h-32v-64h32V448z M312.6,214.1
|
||||
c-6.2,6.2-16.4,6.2-22.6,0c-18.7-18.8-49.1-18.8-67.9,0c0,0,0,0,0,0c-6.4,6.1-16.5,5.8-22.6-0.6c-5.9-6.2-5.9-15.9,0-22
|
||||
c31.2-31.2,81.9-31.2,113.1,0c0,0,0,0,0,0C318.8,197.7,318.8,207.8,312.6,214.1z M352.2,174.5c-6.2,6.2-16.4,6.3-22.6,0c0,0,0,0,0,0
|
||||
c-40.6-40.6-106.4-40.6-147.1,0c-6.2,6.3-16.4,6.3-22.6,0c-6.3-6.2-6.3-16.4,0-22.6c53.1-53.1,139.2-53.1,192.3,0c0,0,0,0,0,0
|
||||
C358.4,158.1,358.4,168.2,352.2,174.5C352.2,174.5,352.2,174.5,352.2,174.5L352.2,174.5z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M426.7,21.3h-384C19.1,21.3,0,40.4,0,64v256c0,23.6,19.1,42.7,42.7,42.7H192v42.7h-42.7V448H320v-42.7h-42.7
|
||||
v-42.7h149.3c23.6,0,42.7-19.1,42.7-42.7V64C469.3,40.4,450.2,21.3,426.7,21.3z M426.7,320h-384V64h384V320z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M267.2,127.2V86.3c0-4.5,3.6-8.1,8.1-8.1c2.3,0,4.5,1,6,2.7l73.2,81.3c2.7,3,2.8,7.5,0.3,10.6l-73.2,89.5
|
||||
c-2.8,3.5-8,4-11.4,1.1c-1.9-1.5-3-3.8-3-6.3v-40.5c-64.3,2.1-104.4,29.6-122.4,83.8c-1.1,3.3-4.2,5.6-7.7,5.6
|
||||
c-0.4,0-0.9,0-1.3-0.1c-3.9-0.6-6.8-4-6.8-8C128.9,202,186.6,131.6,267.2,127.2z M275.3,200.1c4.5,0,8.1,3.6,8.1,8.1v26l54.4-66.4
|
||||
l-54.4-60.4V135c0,4.5-3.6,8.1-8.1,8.1c-63.9,0-111.8,44.2-125.9,111.7C175.6,218.5,217.8,200.1,275.3,200.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
<path class="st1" d="M13.4,42.7C153.6,142.3,293.8,241.9,434,341.5"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.3 KiB |
BIN
front/dist/resources/objects/joystickSplitted.png
vendored
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
front/dist/resources/objects/play_button.png
vendored
Before Width: | Height: | Size: 969 B |
BIN
front/dist/resources/objects/smallHandleFilledGrey.png
vendored
Normal file
After Width: | Height: | Size: 3.5 KiB |
49
front/dist/resources/style/cowebsite-mobile.scss
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
@media (max-aspect-ratio: 1/1) {
|
||||
|
||||
#main-container {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
|
||||
#cowebsite {
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
|
||||
&.loading {
|
||||
transform: translateY(-90%);
|
||||
}
|
||||
&.hidden {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
main {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
aside {
|
||||
height: 30px;
|
||||
cursor: ns-resize;
|
||||
flex-direction: column;
|
||||
|
||||
img {
|
||||
cursor: ns-resize;
|
||||
}
|
||||
}
|
||||
|
||||
.top-right-btn {
|
||||
&#cowebsite-close {
|
||||
right: 0;
|
||||
}
|
||||
&#cowebsite-fullscreen {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
43
front/dist/resources/style/cowebsite.scss
vendored
@ -87,46 +87,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-aspect-ratio: 1/1) {
|
||||
|
||||
|
||||
#cowebsite {
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.loading {
|
||||
transform: translateY(90%);
|
||||
}
|
||||
&.hidden {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
main {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
aside {
|
||||
height: 30px;
|
||||
cursor: ns-resize;
|
||||
flex-direction: column;
|
||||
|
||||
img {
|
||||
cursor: ns-resize;
|
||||
}
|
||||
}
|
||||
|
||||
.top-right-btn {
|
||||
&#cowebsite-close {
|
||||
right: 0px;
|
||||
}
|
||||
&#cowebsite-fullscreen {
|
||||
right: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
4
front/dist/resources/style/index.scss
vendored
@ -1,2 +1,4 @@
|
||||
@import "cowebsite.scss";
|
||||
@import "style.css";
|
||||
@import "cowebsite-mobile.scss";
|
||||
@import "style.css";
|
||||
@import "mobile-style.scss";
|
59
front/dist/resources/style/mobile-style.scss
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
@media screen and (max-width: 700px),
|
||||
screen and (max-height: 700px){
|
||||
video#myCamVideo {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 20%;
|
||||
min-width: 200px;
|
||||
position: absolute;
|
||||
display: block;
|
||||
right: 0;
|
||||
height: 80%;
|
||||
|
||||
&> div {
|
||||
max-height: 120px;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.video-container{
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-cam-action {
|
||||
&:hover{
|
||||
transform: translateY(20px);
|
||||
}
|
||||
div {
|
||||
&:hover {
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
bottom: 30px;
|
||||
|
||||
&.btn-micro {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&.btn-monitor {
|
||||
right: 130px;
|
||||
}
|
||||
|
||||
&.btn-video {
|
||||
right: 65px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-section {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
min-width: 400px;
|
||||
|
||||
& > div {
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
}
|
38
front/dist/resources/style/style.css
vendored
@ -53,6 +53,7 @@ body .message-info.warning{
|
||||
padding-top: 32px;
|
||||
font-size: 28px;
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.video-container img{
|
||||
@ -138,7 +139,7 @@ body .message-info.warning{
|
||||
#div-myCamVideo {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
bottom: 15px;
|
||||
bottom: 30px;
|
||||
border-radius: 15px 15px 15px 15px;
|
||||
}
|
||||
|
||||
@ -153,6 +154,7 @@ video#myCamVideo{
|
||||
|
||||
|
||||
.btn-cam-action {
|
||||
pointer-events: all;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
@ -169,7 +171,7 @@ video#myCamVideo{
|
||||
background: #666;
|
||||
box-shadow: 2px 2px 24px #444;
|
||||
border-radius: 48px;
|
||||
transform: translateY(40px);
|
||||
transform: translateY(20px);
|
||||
transition-timing-function: ease-in-out;
|
||||
bottom: 20px;
|
||||
}
|
||||
@ -185,21 +187,29 @@ video#myCamVideo{
|
||||
.btn-cam-action div:hover{
|
||||
background: #407cf7;
|
||||
box-shadow: 4px 4px 48px #666;
|
||||
transition: 280ms;
|
||||
transition: 120ms;
|
||||
}
|
||||
.btn-micro{
|
||||
pointer-events: auto;
|
||||
transition: all .3s;
|
||||
right: 44px;
|
||||
}
|
||||
.btn-video{
|
||||
pointer-events: auto;
|
||||
transition: all .25s;
|
||||
right: 134px;
|
||||
}
|
||||
.btn-monitor{
|
||||
pointer-events: auto;
|
||||
transition: all .2s;
|
||||
right: 224px;
|
||||
}
|
||||
|
||||
.btn-copy{
|
||||
pointer-events: auto;
|
||||
transition: all .3s;
|
||||
right: 44px;
|
||||
opacity: 1;
|
||||
}
|
||||
.btn-cam-action div img{
|
||||
height: 22px;
|
||||
width: 30px;
|
||||
@ -329,24 +339,6 @@ body {
|
||||
|
||||
|
||||
}
|
||||
@media (max-aspect-ratio: 1/1) {
|
||||
.game-overlay {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.sidebar > div {
|
||||
max-width: 21%;
|
||||
}
|
||||
|
||||
.sidebar > div:hover {
|
||||
max-width: 25%;
|
||||
}
|
||||
}
|
||||
|
||||
#game {
|
||||
width: 100%;
|
||||
@ -504,6 +496,7 @@ input[type=range]:focus::-ms-fill-upper {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
/* TODO: DO WE NEED FLEX HERE???? WE WANT A SIDEBAR OF EXACTLY 25% (note: flex useful for direction!!!) */
|
||||
}
|
||||
|
||||
@ -539,6 +532,7 @@ input[type=range]:focus::-ms-fill-upper {
|
||||
.sidebar {
|
||||
flex: 0 0 25%;
|
||||
display: flex;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.sidebar > div {
|
||||
|
@ -31,6 +31,7 @@
|
||||
"generic-type-guard": "^3.2.0",
|
||||
"google-protobuf": "^3.13.0",
|
||||
"phaser": "3.24.1",
|
||||
"phaser3-rex-plugins": "^1.1.42",
|
||||
"queue-typescript": "^1.0.1",
|
||||
"quill": "^1.3.7",
|
||||
"rxjs": "^6.6.3",
|
||||
|
@ -88,9 +88,9 @@ class ConnectionManager {
|
||||
this.localUser = new LocalUser('', 'test', []);
|
||||
}
|
||||
|
||||
public connectToRoomSocket(roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface): Promise<OnConnectInterface> {
|
||||
public connectToRoomSocket(roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface, companion: string|null): Promise<OnConnectInterface> {
|
||||
return new Promise<OnConnectInterface>((resolve, reject) => {
|
||||
const connection = new RoomConnection(this.localUser.jwtToken, roomId, name, characterLayers, position, viewport);
|
||||
const connection = new RoomConnection(this.localUser.jwtToken, roomId, name, characterLayers, position, viewport, companion);
|
||||
connection.onConnectError((error: object) => {
|
||||
console.log('An error occurred while connecting to socket server. Retrying');
|
||||
reject(error);
|
||||
@ -111,7 +111,7 @@ class ConnectionManager {
|
||||
this.reconnectingTimeout = setTimeout(() => {
|
||||
//todo: allow a way to break recursion?
|
||||
//todo: find a way to avoid recursive function. Otherwise, the call stack will grow indefinitely.
|
||||
this.connectToRoomSocket(roomId, name, characterLayers, position, viewport).then((connection) => resolve(connection));
|
||||
this.connectToRoomSocket(roomId, name, characterLayers, position, viewport, companion).then((connection) => resolve(connection));
|
||||
}, 4000 + Math.floor(Math.random() * 2000) );
|
||||
});
|
||||
});
|
||||
|
@ -47,6 +47,7 @@ export interface MessageUserPositionInterface {
|
||||
name: string;
|
||||
characterLayers: BodyResourceDescriptionInterface[];
|
||||
position: PointInterface;
|
||||
companion: string|null;
|
||||
}
|
||||
|
||||
export interface MessageUserMovedInterface {
|
||||
@ -58,7 +59,8 @@ export interface MessageUserJoined {
|
||||
userId: number;
|
||||
name: string;
|
||||
characterLayers: BodyResourceDescriptionInterface[];
|
||||
position: PointInterface
|
||||
position: PointInterface;
|
||||
companion: string|null;
|
||||
}
|
||||
|
||||
export interface PositionInterface {
|
||||
|
@ -1,3 +1,5 @@
|
||||
import {MAX_USERNAME_LENGTH} from "../Enum/EnvironmentVariable";
|
||||
|
||||
export interface CharacterTexture {
|
||||
id: number,
|
||||
level: number,
|
||||
@ -5,6 +7,23 @@ export interface CharacterTexture {
|
||||
rights: string
|
||||
}
|
||||
|
||||
export const maxUserNameLength: number = MAX_USERNAME_LENGTH;
|
||||
|
||||
export function isUserNameValid(value: string): boolean {
|
||||
const regexp = new RegExp('^[A-Za-z]{1,'+maxUserNameLength+'}$');
|
||||
return regexp.test(value);
|
||||
}
|
||||
|
||||
export function areCharacterLayersValid(value: string[] | null): boolean {
|
||||
if (!value || !value.length) return false;
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (/^\w+$/.exec(value[i]) === null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export class LocalUser {
|
||||
constructor(public readonly uuid:string, public readonly jwtToken: string, public readonly textures: CharacterTexture[]) {
|
||||
}
|
||||
|
@ -1,14 +1,16 @@
|
||||
import {LocalUser} from "./LocalUser";
|
||||
import {areCharacterLayersValid, isUserNameValid, LocalUser} from "./LocalUser";
|
||||
|
||||
const playerNameKey = 'playerName';
|
||||
const selectedPlayerKey = 'selectedPlayer';
|
||||
const customCursorPositionKey = 'customCursorPosition';
|
||||
const characterLayersKey = 'characterLayers';
|
||||
const companionKey = 'companion';
|
||||
const gameQualityKey = 'gameQuality';
|
||||
const videoQualityKey = 'videoQuality';
|
||||
const audioPlayerVolumeKey = 'audioVolume';
|
||||
const audioPlayerMuteKey = 'audioMute';
|
||||
const helpCameraSettingsShown = 'helpCameraSettingsShown';
|
||||
const helpCameraSettingsShown = 'helpCameraSettingsShown';
|
||||
const fullscreenKey = 'fullscreen';
|
||||
|
||||
class LocalUserStore {
|
||||
saveUser(localUser: LocalUser) {
|
||||
@ -22,8 +24,9 @@ class LocalUserStore {
|
||||
setName(name:string): void {
|
||||
localStorage.setItem(playerNameKey, name);
|
||||
}
|
||||
getName(): string {
|
||||
return localStorage.getItem(playerNameKey) || '';
|
||||
getName(): string|null {
|
||||
const value = localStorage.getItem(playerNameKey) || '';
|
||||
return isUserNameValid(value) ? value : null;
|
||||
}
|
||||
|
||||
setPlayerCharacterIndex(playerCharacterIndex: number): void {
|
||||
@ -44,7 +47,24 @@ class LocalUserStore {
|
||||
localStorage.setItem(characterLayersKey, JSON.stringify(layers));
|
||||
}
|
||||
getCharacterLayers(): string[]|null {
|
||||
return JSON.parse(localStorage.getItem(characterLayersKey) || "null");
|
||||
const value = JSON.parse(localStorage.getItem(characterLayersKey) || "null");
|
||||
return areCharacterLayersValid(value) ? value : null;
|
||||
}
|
||||
|
||||
setCompanion(companion: string|null): void {
|
||||
return localStorage.setItem(companionKey, JSON.stringify(companion));
|
||||
}
|
||||
getCompanion(): string|null {
|
||||
const companion = JSON.parse(localStorage.getItem(companionKey) || "null");
|
||||
|
||||
if (typeof companion !== "string" || companion === "") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return companion;
|
||||
}
|
||||
wasCompanionSet(): boolean {
|
||||
return localStorage.getItem(companionKey) ? true : false;
|
||||
}
|
||||
|
||||
setGameQualityValue(value: number): void {
|
||||
@ -81,6 +101,13 @@ class LocalUserStore {
|
||||
getHelpCameraSettingsShown(): boolean {
|
||||
return localStorage.getItem(helpCameraSettingsShown) === '1';
|
||||
}
|
||||
|
||||
setFullscreen(value: boolean): void {
|
||||
localStorage.setItem(fullscreenKey, value.toString());
|
||||
}
|
||||
getFullscreen(): boolean {
|
||||
return localStorage.getItem(fullscreenKey) === 'true';
|
||||
}
|
||||
}
|
||||
|
||||
export const localUserStore = new LocalUserStore();
|
||||
|
@ -66,7 +66,7 @@ export class RoomConnection implements RoomConnection {
|
||||
* @param token A JWT token containing the UUID of the user
|
||||
* @param roomId The ID of the room in the form "_/[instance]/[map_url]" or "@/[org]/[event]/[map]"
|
||||
*/
|
||||
public constructor(token: string|null, roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface) {
|
||||
public constructor(token: string|null, roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface, companion: string|null) {
|
||||
let url = new URL(PUSHER_URL, window.location.toString()).toString();
|
||||
url = url.replace('http://', 'ws://').replace('https://', 'wss://');
|
||||
if (!url.endsWith('/')) {
|
||||
@ -85,6 +85,10 @@ export class RoomConnection implements RoomConnection {
|
||||
url += '&bottom='+Math.floor(viewport.bottom);
|
||||
url += '&left='+Math.floor(viewport.left);
|
||||
url += '&right='+Math.floor(viewport.right);
|
||||
|
||||
if (typeof companion === 'string') {
|
||||
url += '&companion='+encodeURIComponent(companion);
|
||||
}
|
||||
|
||||
if (RoomConnection.websocketFactory) {
|
||||
this.socket = RoomConnection.websocketFactory(url);
|
||||
@ -322,11 +326,14 @@ export class RoomConnection implements RoomConnection {
|
||||
}
|
||||
})
|
||||
|
||||
const companion = message.getCompanion();
|
||||
|
||||
return {
|
||||
userId: message.getUserid(),
|
||||
name: message.getName(),
|
||||
characterLayers,
|
||||
position: ProtobufClientUtils.toPointInterface(position)
|
||||
position: ProtobufClientUtils.toPointInterface(position),
|
||||
companion: companion ? companion.getName() : null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,10 @@ const RESOLUTION = 2;
|
||||
const ZOOM_LEVEL = 1/*3/4*/;
|
||||
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
|
||||
export const MAX_USERNAME_LENGTH = parseInt(process.env.MAX_USERNAME_LENGTH || '') || 8;
|
||||
export const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || '4');
|
||||
|
||||
export const isMobile = ():boolean => ( ( window.innerWidth <= 800 ) || ( window.innerHeight <= 600 ) );
|
||||
|
||||
export {
|
||||
DEBUG_MODE,
|
||||
|
221
front/src/Phaser/Companion/Companion.ts
Normal file
@ -0,0 +1,221 @@
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import Container = Phaser.GameObjects.Container;
|
||||
import { lazyLoadCompanionResource } from "./CompanionTexturesLoadingManager";
|
||||
import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation";
|
||||
|
||||
export interface CompanionStatus {
|
||||
x: number;
|
||||
y: number;
|
||||
name: string;
|
||||
moving: boolean;
|
||||
direction: PlayerAnimationDirections;
|
||||
}
|
||||
|
||||
export class Companion extends Container {
|
||||
public sprites: Map<string, Sprite>;
|
||||
|
||||
private delta: number;
|
||||
private invisible: boolean;
|
||||
private updateListener: Function;
|
||||
private target: { x: number, y: number, direction: PlayerAnimationDirections };
|
||||
|
||||
private companionName: string;
|
||||
private direction: PlayerAnimationDirections;
|
||||
private animationType: PlayerAnimationTypes;
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, name: string, texturePromise: Promise<string>) {
|
||||
super(scene, x + 14, y + 4);
|
||||
|
||||
this.sprites = new Map<string, Sprite>();
|
||||
|
||||
this.delta = 0;
|
||||
this.invisible = true;
|
||||
this.target = { x, y, direction: PlayerAnimationDirections.Down };
|
||||
|
||||
this.direction = PlayerAnimationDirections.Down;
|
||||
this.animationType = PlayerAnimationTypes.Idle;
|
||||
|
||||
this.companionName = name;
|
||||
|
||||
texturePromise.then(resource => {
|
||||
this.addResource(resource);
|
||||
this.invisible = false;
|
||||
})
|
||||
|
||||
this.scene.physics.world.enableBody(this);
|
||||
|
||||
this.getBody().setImmovable(true);
|
||||
this.getBody().setCollideWorldBounds(false);
|
||||
this.setSize(16, 16);
|
||||
this.getBody().setSize(16, 16);
|
||||
this.getBody().setOffset(0, 8);
|
||||
|
||||
this.setDepth(-1);
|
||||
|
||||
this.updateListener = this.step.bind(this);
|
||||
this.scene.events.addListener('update', this.updateListener);
|
||||
|
||||
this.scene.add.existing(this);
|
||||
}
|
||||
|
||||
public setTarget(x: number, y: number, direction: PlayerAnimationDirections) {
|
||||
this.target = { x, y: y + 4, direction };
|
||||
}
|
||||
|
||||
public step(time: number, delta: number) {
|
||||
if (typeof this.target === 'undefined') return;
|
||||
|
||||
this.delta += delta;
|
||||
if (this.delta < 128) {
|
||||
return;
|
||||
}
|
||||
this.delta = 0;
|
||||
|
||||
const xDist = this.target.x - this.x;
|
||||
const yDist = this.target.y - this.y;
|
||||
|
||||
const distance = Math.pow(xDist, 2) + Math.pow(yDist, 2);
|
||||
|
||||
if (distance < 650) {
|
||||
this.animationType = PlayerAnimationTypes.Idle;
|
||||
this.direction = this.target.direction;
|
||||
|
||||
this.getBody().stop();
|
||||
} else {
|
||||
this.animationType = PlayerAnimationTypes.Walk;
|
||||
|
||||
const xDir = xDist / Math.max(Math.abs(xDist), 1);
|
||||
const yDir = yDist / Math.max(Math.abs(yDist), 1);
|
||||
|
||||
const speed = 256;
|
||||
this.getBody().setVelocity(Math.min(Math.abs(xDist * 2.5), speed) * xDir, Math.min(Math.abs(yDist * 2.5), speed) * yDir);
|
||||
|
||||
if (Math.abs(xDist) > Math.abs(yDist)) {
|
||||
if (xDist < 0) {
|
||||
this.direction = PlayerAnimationDirections.Left;
|
||||
} else {
|
||||
this.direction = PlayerAnimationDirections.Right;
|
||||
}
|
||||
} else {
|
||||
if (yDist < 0) {
|
||||
this.direction = PlayerAnimationDirections.Up;
|
||||
} else {
|
||||
this.direction = PlayerAnimationDirections.Down;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setDepth(this.y);
|
||||
this.playAnimation(this.direction, this.animationType);
|
||||
}
|
||||
|
||||
public getStatus(): CompanionStatus {
|
||||
const { x, y, direction, animationType, companionName } = this;
|
||||
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
direction,
|
||||
moving: animationType === PlayerAnimationTypes.Walk,
|
||||
name: companionName
|
||||
}
|
||||
}
|
||||
|
||||
private playAnimation(direction: PlayerAnimationDirections, type: PlayerAnimationTypes): void {
|
||||
if (this.invisible) return;
|
||||
|
||||
for (const [resource, sprite] of this.sprites.entries()) {
|
||||
sprite.play(`${resource}-${direction}-${type}`, true);
|
||||
}
|
||||
}
|
||||
|
||||
private addResource(resource: string, frame?: string | number): void {
|
||||
const sprite = new Sprite(this.scene, 0, 0, resource, frame);
|
||||
|
||||
this.add(sprite);
|
||||
|
||||
this.getAnimations(resource).forEach(animation => {
|
||||
this.scene.anims.create(animation);
|
||||
});
|
||||
|
||||
this.scene.sys.updateList.add(sprite);
|
||||
this.sprites.set(resource, sprite);
|
||||
}
|
||||
|
||||
private getAnimations(resource: string): Phaser.Types.Animations.Animation[] {
|
||||
return [
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Idle}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [1]}),
|
||||
frameRate: 10,
|
||||
repeat: 1
|
||||
},
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Idle}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [4]}),
|
||||
frameRate: 10,
|
||||
repeat: 1
|
||||
},
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Idle}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [7]}),
|
||||
frameRate: 10,
|
||||
repeat: 1
|
||||
},
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Idle}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [10]}),
|
||||
frameRate: 10,
|
||||
repeat: 1
|
||||
},
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Walk}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [0, 1, 2]}),
|
||||
frameRate: 15,
|
||||
repeat: -1
|
||||
},
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Walk}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [3, 4, 5]}),
|
||||
frameRate: 15,
|
||||
repeat: -1
|
||||
},
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Walk}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [6, 7, 8]}),
|
||||
frameRate: 15,
|
||||
repeat: -1
|
||||
},
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Walk}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [9, 10, 11]}),
|
||||
frameRate: 15,
|
||||
repeat: -1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
private getBody(): Phaser.Physics.Arcade.Body {
|
||||
const body = this.body;
|
||||
|
||||
if (!(body instanceof Phaser.Physics.Arcade.Body)) {
|
||||
throw new Error('Container does not have arcade body');
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
for (const sprite of this.sprites.values()) {
|
||||
if (this.scene) {
|
||||
this.scene.sys.updateList.remove(sprite);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.scene) {
|
||||
this.scene.events.removeListener('update', this.updateListener);
|
||||
}
|
||||
|
||||
super.destroy();
|
||||
}
|
||||
}
|
14
front/src/Phaser/Companion/CompanionTextures.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export interface CompanionResourceDescriptionInterface {
|
||||
name: string,
|
||||
img: string,
|
||||
behaviour: "dog" | "cat"
|
||||
}
|
||||
|
||||
export const COMPANION_RESOURCES: CompanionResourceDescriptionInterface[] = [
|
||||
{ name: "dog1", img: "resources/characters/pipoya/Dog 01-1.png", behaviour: "dog" },
|
||||
{ name: "dog2", img: "resources/characters/pipoya/Dog 01-2.png", behaviour: "dog" },
|
||||
{ name: "dog3", img: "resources/characters/pipoya/Dog 01-3.png", behaviour: "dog" },
|
||||
{ name: "cat1", img: "resources/characters/pipoya/Cat 01-1.png", behaviour: "cat" },
|
||||
{ name: "cat2", img: "resources/characters/pipoya/Cat 01-2.png", behaviour: "cat" },
|
||||
{ name: "cat3", img: "resources/characters/pipoya/Cat 01-3.png", behaviour: "cat" },
|
||||
]
|
@ -0,0 +1,29 @@
|
||||
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
||||
import { COMPANION_RESOURCES, CompanionResourceDescriptionInterface } from "./CompanionTextures";
|
||||
|
||||
export const getAllCompanionResources = (loader: LoaderPlugin): CompanionResourceDescriptionInterface[] => {
|
||||
COMPANION_RESOURCES.forEach((resource: CompanionResourceDescriptionInterface) => {
|
||||
lazyLoadCompanionResource(loader, resource.name);
|
||||
});
|
||||
|
||||
return COMPANION_RESOURCES;
|
||||
}
|
||||
|
||||
export const lazyLoadCompanionResource = (loader: LoaderPlugin, name: string): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const resource = COMPANION_RESOURCES.find(item => item.name === name);
|
||||
|
||||
if (typeof resource === 'undefined') {
|
||||
return reject(`Texture '${name}' not found!`);
|
||||
}
|
||||
|
||||
if (loader.textureManager.exists(resource.name)) {
|
||||
return resolve(resource.name);
|
||||
}
|
||||
|
||||
loader.spritesheet(resource.name, resource.img, { frameWidth: 32, frameHeight: 32, endFrame: 12 });
|
||||
loader.once(`filecomplete-spritesheet-${resource.name}`, () => resolve(resource.name));
|
||||
|
||||
loader.start(); // It's only automatically started during the Scene preload.
|
||||
});
|
||||
}
|
35
front/src/Phaser/Components/MobileJoystick.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js';
|
||||
|
||||
const outOfScreenX = -1000;
|
||||
const outOfScreenY = -1000;
|
||||
|
||||
|
||||
//the assets were found here: https://hannemann.itch.io/virtual-joystick-pack-free
|
||||
export const joystickBaseKey = 'joystickBase';
|
||||
export const joystickBaseImg = 'resources/objects/joystickSplitted.png';
|
||||
export const joystickThumbKey = 'joystickThumb';
|
||||
export const joystickThumbImg = 'resources/objects/smallHandleFilledGrey.png';
|
||||
|
||||
export class MobileJoystick extends VirtualJoystick {
|
||||
|
||||
constructor(scene: Phaser.Scene) {
|
||||
super(scene, {
|
||||
x: outOfScreenX,
|
||||
y: outOfScreenY,
|
||||
radius: 20,
|
||||
base: scene.add.image(0, 0, joystickBaseKey).setDisplaySize(60, 60).setDepth(99999),
|
||||
thumb: scene.add.image(0, 0, joystickThumbKey).setDisplaySize(30, 30).setDepth(99999),
|
||||
enable: true,
|
||||
dir: "8dir",
|
||||
});
|
||||
|
||||
this.scene.input.on('pointerdown', (pointer: { x: number; y: number; }) => {
|
||||
this.x = pointer.x;
|
||||
this.y = pointer.y;
|
||||
});
|
||||
this.scene.input.on('pointerup', () => {
|
||||
this.x = outOfScreenX;
|
||||
this.y = outOfScreenY;
|
||||
});
|
||||
}
|
||||
}
|
@ -1,46 +1,68 @@
|
||||
|
||||
const IGNORED_KEYS = new Set([
|
||||
'Esc',
|
||||
'Escape',
|
||||
'Alt',
|
||||
'Meta',
|
||||
'Control',
|
||||
'Ctrl',
|
||||
'Space',
|
||||
'Backspace'
|
||||
])
|
||||
|
||||
export class TextInput extends Phaser.GameObjects.BitmapText {
|
||||
private minUnderLineLength = 4;
|
||||
private underLine: Phaser.GameObjects.Text;
|
||||
private domInput = document.createElement('input');
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, maxLength: number, text: string, onChange: (text: string) => void) {
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, maxLength: number, text: string,
|
||||
onChange: (text: string) => void) {
|
||||
super(scene, x, y, 'main_font', text, 32);
|
||||
this.setOrigin(0.5).setCenterAlign()
|
||||
this.setOrigin(0.5).setCenterAlign();
|
||||
this.scene.add.existing(this);
|
||||
|
||||
this.underLine = this.scene.add.text(x, y+1, this.getUnderLineBody(text.length), { fontFamily: 'Arial', fontSize: "32px", color: '#ffffff'})
|
||||
this.underLine.setOrigin(0.5)
|
||||
const style = {fontFamily: 'Arial', fontSize: "32px", color: '#ffffff'};
|
||||
this.underLine = this.scene.add.text(x, y+1, this.getUnderLineBody(text.length), style);
|
||||
this.underLine.setOrigin(0.5);
|
||||
|
||||
this.domInput.maxLength = maxLength;
|
||||
this.domInput.style.opacity = "0";
|
||||
if (text) {
|
||||
this.domInput.value = text;
|
||||
}
|
||||
|
||||
this.scene.input.keyboard.on('keydown', (event: KeyboardEvent) => {
|
||||
if (event.keyCode === 8 && this.text.length > 0) {
|
||||
this.deleteLetter();
|
||||
} else if ((event.keyCode === 32 || (event.keyCode >= 48 && event.keyCode <= 90)) && this.text.length < maxLength) {
|
||||
this.addLetter(event.key);
|
||||
this.domInput.addEventListener('keydown', event => {
|
||||
if (IGNORED_KEYS.has(event.key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/[a-zA-Z0-9:.!&?()+-]/.exec(event.key)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
this.domInput.addEventListener('input', (event) => {
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
this.text = this.domInput.value;
|
||||
this.underLine.text = this.getUnderLineBody(this.text.length);
|
||||
onChange(this.text);
|
||||
});
|
||||
|
||||
document.body.append(this.domInput);
|
||||
this.focus();
|
||||
}
|
||||
|
||||
|
||||
private getUnderLineBody(textLength:number): string {
|
||||
if (textLength < this.minUnderLineLength) textLength = this.minUnderLineLength;
|
||||
let text = '_______';
|
||||
for (let i = this.minUnderLineLength; i < textLength; i++) {
|
||||
text += '__'
|
||||
text += '__';
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private deleteLetter() {
|
||||
this.text = this.text.substr(0, this.text.length - 1);
|
||||
}
|
||||
|
||||
|
||||
private addLetter(letter: string) {
|
||||
this.text += letter;
|
||||
}
|
||||
|
||||
getText(): string {
|
||||
return this.text;
|
||||
}
|
||||
@ -56,4 +78,13 @@ export class TextInput extends Phaser.GameObjects.BitmapText {
|
||||
this.underLine.y = y+1;
|
||||
return this;
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.domInput.focus();
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
super.destroy();
|
||||
this.domInput.remove();
|
||||
}
|
||||
}
|
||||
|
53
front/src/Phaser/Components/TextUtils.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import {ITiledMapObject} from "../Map/ITiledMap";
|
||||
import Text = Phaser.GameObjects.Text;
|
||||
import {GameScene} from "../Game/GameScene";
|
||||
import TextStyle = Phaser.GameObjects.TextStyle;
|
||||
|
||||
export class TextUtils {
|
||||
public static createTextFromITiledMapObject(scene: GameScene, object: ITiledMapObject): void {
|
||||
if (object.text === undefined) {
|
||||
throw new Error('This object has not textual representation.');
|
||||
}
|
||||
const options: {
|
||||
fontStyle?: string,
|
||||
fontSize?: string,
|
||||
fontFamily?: string,
|
||||
color?: string,
|
||||
align?: string,
|
||||
wordWrap?: {
|
||||
width: number,
|
||||
useAdvancedWrap?: boolean
|
||||
}
|
||||
} = {};
|
||||
if (object.text.italic) {
|
||||
options.fontStyle = 'italic';
|
||||
}
|
||||
// Note: there is no support for "strikeout" and "underline"
|
||||
let fontSize: number = 16;
|
||||
if (object.text.pixelsize) {
|
||||
fontSize = object.text.pixelsize;
|
||||
}
|
||||
options.fontSize = fontSize + 'px';
|
||||
if (object.text.fontfamily) {
|
||||
options.fontFamily = '"'+object.text.fontfamily+'"';
|
||||
}
|
||||
let color = '#000000';
|
||||
if (object.text.color !== undefined) {
|
||||
color = object.text.color;
|
||||
}
|
||||
options.color = color;
|
||||
if (object.text.wrap === true) {
|
||||
options.wordWrap = {
|
||||
width: object.width,
|
||||
//useAdvancedWrap: true
|
||||
}
|
||||
}
|
||||
if (object.text.halign !== undefined) {
|
||||
options.align = object.text.halign;
|
||||
}
|
||||
|
||||
console.warn(options);
|
||||
const textElem = scene.add.text(object.x, object.y, object.text.text, options);
|
||||
textElem.setAngle(object.rotation);
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import BitmapText = Phaser.GameObjects.BitmapText;
|
||||
import Container = Phaser.GameObjects.Container;
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import {TextureError} from "../../Exception/TextureError";
|
||||
import {Companion} from "../Companion/Companion";
|
||||
|
||||
interface AnimationData {
|
||||
key: string;
|
||||
@ -21,6 +22,7 @@ export abstract class Character extends Container {
|
||||
private lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down;
|
||||
//private teleportation: Sprite;
|
||||
private invisible: boolean;
|
||||
public companion?: Companion;
|
||||
|
||||
constructor(scene: Phaser.Scene,
|
||||
x: number,
|
||||
@ -69,6 +71,12 @@ export abstract class Character extends Container {
|
||||
this.playAnimation(direction, moving);
|
||||
}
|
||||
|
||||
public addCompanion(name: string, texturePromise?: Promise<string>): void {
|
||||
if (typeof texturePromise !== 'undefined') {
|
||||
this.companion = new Companion(this.scene, this.x, this.y, name, texturePromise);
|
||||
}
|
||||
}
|
||||
|
||||
public addTextures(textures: string[], frame?: string | number): void {
|
||||
for (const texture of textures) {
|
||||
if(!this.scene.textures.exists(texture)){
|
||||
@ -189,6 +197,10 @@ export abstract class Character extends Container {
|
||||
}
|
||||
|
||||
this.setDepth(this.y);
|
||||
|
||||
if (this.companion) {
|
||||
this.companion.setTarget(this.x, this.y, this.lastDirection);
|
||||
}
|
||||
}
|
||||
|
||||
stop(){
|
||||
|
@ -17,12 +17,18 @@ export class RemotePlayer extends Character {
|
||||
name: string,
|
||||
texturesPromise: Promise<string[]>,
|
||||
direction: PlayerAnimationDirections,
|
||||
moving: boolean
|
||||
moving: boolean,
|
||||
companion: string|null,
|
||||
companionTexturePromise?: Promise<string>
|
||||
) {
|
||||
super(Scene, x, y, texturesPromise, name, direction, moving, 1);
|
||||
|
||||
|
||||
//set data
|
||||
this.userId = userId;
|
||||
|
||||
if (typeof companion === 'string') {
|
||||
this.addCompanion(companion, companionTexturePromise);
|
||||
}
|
||||
}
|
||||
|
||||
updatePosition(position: PointInterface): void {
|
||||
@ -31,5 +37,9 @@ export class RemotePlayer extends Character {
|
||||
this.setY(position.y);
|
||||
|
||||
this.setDepth(position.y); //this is to make sure the perspective (player models closer the bottom of the screen will appear in front of models nearer the top of the screen).
|
||||
|
||||
if (this.companion) {
|
||||
this.companion.setTarget(position.x, position.y, position.direction as PlayerAnimationDirections);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,4 +6,5 @@ export interface AddPlayerInterface {
|
||||
name: string;
|
||||
characterLayers: BodyResourceDescriptionInterface[];
|
||||
position: PointInterface;
|
||||
companion: string|null;
|
||||
}
|
||||
|
@ -21,21 +21,23 @@ export interface HasMovedEvent {
|
||||
export class GameManager {
|
||||
private playerName: string|null;
|
||||
private characterLayers: string[]|null;
|
||||
private companion: string|null;
|
||||
private startRoom!:Room;
|
||||
currentGameSceneName: string|null = null;
|
||||
|
||||
constructor() {
|
||||
this.playerName = localUserStore.getName();
|
||||
this.characterLayers = localUserStore.getCharacterLayers();
|
||||
this.companion = localUserStore.getCompanion();
|
||||
}
|
||||
|
||||
public async init(scenePlugin: Phaser.Scenes.ScenePlugin): Promise<string> {
|
||||
this.startRoom = await connectionManager.initGameConnexion();
|
||||
await this.loadMap(this.startRoom, scenePlugin);
|
||||
|
||||
|
||||
if (!this.playerName) {
|
||||
return LoginSceneName;
|
||||
} else if (!this.characterLayers) {
|
||||
} else if (!this.characterLayers || !this.characterLayers.length) {
|
||||
return SelectCharacterSceneName;
|
||||
} else {
|
||||
return EnableCameraSceneName;
|
||||
@ -63,6 +65,14 @@ export class GameManager {
|
||||
return this.characterLayers;
|
||||
}
|
||||
|
||||
|
||||
setCompanion(companion: string|null): void {
|
||||
this.companion = companion;
|
||||
}
|
||||
|
||||
getCompanion(): string|null {
|
||||
return this.companion;
|
||||
}
|
||||
|
||||
public async loadMap(room: Room, scenePlugin: Phaser.Scenes.ScenePlugin): Promise<void> {
|
||||
const roomID = room.id;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {ITiledMap} from "../Map/ITiledMap";
|
||||
import {ITiledMap, ITiledMapLayer} from "../Map/ITiledMap";
|
||||
import {LayersIterator} from "../Map/LayersIterator";
|
||||
|
||||
export type PropertyChangeCallback = (newValue: string | number | boolean | undefined, oldValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) => void;
|
||||
|
||||
@ -10,8 +11,10 @@ export class GameMap {
|
||||
private key: number|undefined;
|
||||
private lastProperties = new Map<string, string|boolean|number>();
|
||||
private callbacks = new Map<string, Array<PropertyChangeCallback>>();
|
||||
public readonly layersIterator: LayersIterator;
|
||||
|
||||
public constructor(private map: ITiledMap) {
|
||||
this.layersIterator = new LayersIterator(map);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,7 +58,7 @@ export class GameMap {
|
||||
private getProperties(key: number): Map<string, string|boolean|number> {
|
||||
const properties = new Map<string, string|boolean|number>();
|
||||
|
||||
for (const layer of this.map.layers) {
|
||||
for (const layer of this.layersIterator) {
|
||||
if (layer.type !== 'tilelayer') {
|
||||
continue;
|
||||
}
|
||||
|
@ -10,8 +10,23 @@ import {
|
||||
RoomJoinedMessageInterface
|
||||
} from "../../Connexion/ConnexionModels";
|
||||
import {CurrentGamerInterface, hasMovedEventName, Player} from "../Player/Player";
|
||||
import {DEBUG_MODE, JITSI_PRIVATE_MODE, POSITION_DELAY, RESOLUTION, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable";
|
||||
import {ITiledMap, ITiledMapLayer, ITiledMapLayerProperty, ITiledMapObject, ITiledTileSet} from "../Map/ITiledMap";
|
||||
import {
|
||||
DEBUG_MODE,
|
||||
JITSI_PRIVATE_MODE,
|
||||
MAX_PER_GROUP,
|
||||
POSITION_DELAY,
|
||||
RESOLUTION,
|
||||
ZOOM_LEVEL
|
||||
} from "../../Enum/EnvironmentVariable";
|
||||
import {
|
||||
ITiledMap,
|
||||
ITiledMapLayer,
|
||||
ITiledMapLayerProperty,
|
||||
ITiledMapObject,
|
||||
ITiledText,
|
||||
ITiledMapTileLayer,
|
||||
ITiledTileSet
|
||||
} from "../Map/ITiledMap";
|
||||
import {AddPlayerInterface} from "./AddPlayerInterface";
|
||||
import {PlayerAnimationDirections} from "../Player/Animation";
|
||||
import {PlayerMovement} from "./PlayerMovement";
|
||||
@ -69,6 +84,12 @@ import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
|
||||
import DOMElement = Phaser.GameObjects.DOMElement;
|
||||
import {Subscription} from "rxjs";
|
||||
import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream";
|
||||
import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager";
|
||||
import {TextUtils} from "../Components/TextUtils";
|
||||
import {LayersIterator} from "../Map/LayersIterator";
|
||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||
import {PinchManager} from "../UserInput/PinchManager";
|
||||
import {joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey} from "../Components/MobileJoystick";
|
||||
|
||||
export interface GameSceneInitInterface {
|
||||
initPosition: PointInterface|null,
|
||||
@ -124,7 +145,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
pendingEvents: Queue<InitUserPositionEventInterface|AddPlayerEventInterface|RemovePlayerEventInterface|UserMovedEventInterface|GroupCreatedUpdatedEventInterface|DeleteGroupEventInterface> = new Queue<InitUserPositionEventInterface|AddPlayerEventInterface|RemovePlayerEventInterface|UserMovedEventInterface|GroupCreatedUpdatedEventInterface|DeleteGroupEventInterface>();
|
||||
private initPosition: PositionInterface|null = null;
|
||||
private playersPositionInterpolator = new PlayersPositionInterpolator();
|
||||
public connection!: RoomConnection;
|
||||
public connection: RoomConnection|undefined;
|
||||
private simplePeer!: SimplePeer;
|
||||
private GlobalMessageManager!: GlobalMessageManager;
|
||||
public ConsoleGlobalMessageManager!: ConsoleGlobalMessageManager;
|
||||
@ -133,7 +154,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
// A promise that will resolve when the "create" method is called (signaling loading is ended)
|
||||
private createPromise: Promise<void>;
|
||||
private createPromiseResolve!: (value?: void | PromiseLike<void>) => void;
|
||||
|
||||
private iframeSubscriptionList! : Array<Subscription>;
|
||||
MapUrlFile: string;
|
||||
RoomId: string;
|
||||
instance: string;
|
||||
@ -159,6 +180,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
private openChatIcon!: OpenChatIcon;
|
||||
private playerName!: string;
|
||||
private characterLayers!: string[];
|
||||
private companion!: string|null;
|
||||
private messageSubscription: Subscription|null = null;
|
||||
private popUpElements : Map<number, DOMElement> = new Map<number, Phaser.GameObjects.DOMElement>();
|
||||
private originalMapUrl: string|undefined;
|
||||
@ -185,6 +207,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
|
||||
//hook preload scene
|
||||
preload(): void {
|
||||
addLoader(this);
|
||||
const localUser = localUserStore.getLocalUser();
|
||||
const textures = localUser?.textures;
|
||||
if (textures) {
|
||||
@ -194,6 +217,10 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
}
|
||||
|
||||
this.load.image(openChatIconName, 'resources/objects/talk.png');
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
this.load.image(joystickBaseKey, joystickBaseImg);
|
||||
this.load.image(joystickThumbKey, joystickThumbImg);
|
||||
}
|
||||
this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => {
|
||||
// If we happen to be in HTTP and we are trying to load a URL in HTTPS only... (this happens only in dev environments)
|
||||
if (window.location.protocol === 'http:' && file.src === this.MapUrlFile && file.src.startsWith('http:') && this.originalMapUrl === undefined) {
|
||||
@ -240,8 +267,6 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
|
||||
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');
|
||||
|
||||
addLoader(this);
|
||||
}
|
||||
|
||||
// FIXME: we need to put a "unknown" instead of a "any" and validate the structure of the JSON we are receiving.
|
||||
@ -344,6 +369,10 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
urlManager.pushRoomIdToUrl(this.room);
|
||||
this.startLayerName = urlManager.getStartLayerNameFromUrl();
|
||||
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
new PinchManager(this);
|
||||
}
|
||||
|
||||
this.messageSubscription = worldFullMessageStream.stream.subscribe((message) => this.showWorldFullError())
|
||||
|
||||
const playerName = gameManager.getPlayerName();
|
||||
@ -352,7 +381,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
}
|
||||
this.playerName = playerName;
|
||||
this.characterLayers = gameManager.getCharacterLayers();
|
||||
|
||||
this.companion = gameManager.getCompanion();
|
||||
|
||||
//initalise map
|
||||
this.Map = this.add.tilemap(this.MapUrlFile);
|
||||
@ -367,8 +396,9 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
|
||||
//add layer on map
|
||||
this.Layers = new Array<Phaser.Tilemaps.StaticTilemapLayer>();
|
||||
|
||||
let depth = -2;
|
||||
for (const layer of this.mapFile.layers) {
|
||||
for (const layer of this.gameMap.layersIterator) {
|
||||
if (layer.type === 'tilelayer') {
|
||||
this.addLayer(this.Map.createStaticLayer(layer.name, this.Terrains, 0, 0).setDepth(depth));
|
||||
|
||||
@ -384,9 +414,16 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
if (layer.type === 'objectgroup' && layer.name === 'floorLayer') {
|
||||
depth = 10000;
|
||||
}
|
||||
if (layer.type === 'objectgroup') {
|
||||
for (const object of layer.objects) {
|
||||
if (object.text) {
|
||||
TextUtils.createTextFromITiledMapObject(this, object);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (depth === -2) {
|
||||
throw new Error('Your map MUST contain a layer of type "objectgroup" whose name is "floorLayer" that represents the layer characters are drawn at.');
|
||||
throw new Error('Your map MUST contain a layer of type "objectgroup" whose name is "floorLayer" that represents the layer characters are drawn at. This layer cannot be contained in a group.');
|
||||
}
|
||||
|
||||
this.initStartXAndStartY();
|
||||
@ -397,9 +434,14 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
//initialise list of other player
|
||||
this.MapPlayers = this.physics.add.group({immovable: true});
|
||||
|
||||
|
||||
//create input to move
|
||||
this.userInputManager = new UserInputManager(this);
|
||||
mediaManager.setUserInputManager(this.userInputManager);
|
||||
this.userInputManager = new UserInputManager(this);
|
||||
|
||||
if (localUserStore.getFullscreen()) {
|
||||
document.querySelector('body')?.requestFullscreen();
|
||||
}
|
||||
|
||||
//notify game manager can to create currentUser in map
|
||||
this.createCurrentPlayer();
|
||||
@ -477,7 +519,9 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
top: camera.scrollY,
|
||||
right: camera.scrollX + camera.width,
|
||||
bottom: camera.scrollY + camera.height,
|
||||
}).then((onConnect: OnConnectInterface) => {
|
||||
},
|
||||
this.companion
|
||||
).then((onConnect: OnConnectInterface) => {
|
||||
this.connection = onConnect.connection;
|
||||
|
||||
this.connection.onUserJoins((message: MessageUserJoined) => {
|
||||
@ -485,7 +529,8 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
userId: message.userId,
|
||||
characterLayers: message.characterLayers,
|
||||
name: message.name,
|
||||
position: message.position
|
||||
position: message.position,
|
||||
companion: message.companion
|
||||
}
|
||||
this.addPlayer(userMessage);
|
||||
});
|
||||
@ -664,7 +709,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
if(openWebsiteTriggerValue && openWebsiteTriggerValue === ON_ACTION_TRIGGER_BUTTON) {
|
||||
let message = allProps.get(WEBSITE_MESSAGE_PROPERTIES);
|
||||
if(message === undefined){
|
||||
message = 'Press on SPACE to open the web site';
|
||||
message = 'Press SPACE or touch here to open web site';
|
||||
}
|
||||
layoutManager.addActionButton('openWebsite', message.toString(), () => {
|
||||
openWebsiteFunction();
|
||||
@ -685,7 +730,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
if (JITSI_PRIVATE_MODE && !jitsiUrl) {
|
||||
const adminTag = allProps.get("jitsiRoomAdminTag") as string|undefined;
|
||||
|
||||
this.connection.emitQueryJitsiJwtMessage(roomName, adminTag);
|
||||
this.connection?.emitQueryJitsiJwtMessage(roomName, adminTag);
|
||||
} else {
|
||||
this.startJitsi(roomName, undefined);
|
||||
}
|
||||
@ -696,7 +741,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
if(jitsiTriggerValue && jitsiTriggerValue === ON_ACTION_TRIGGER_BUTTON) {
|
||||
let message = allProps.get(JITSI_MESSAGE_PROPERTIES);
|
||||
if (message === undefined) {
|
||||
message = 'Press on SPACE to enter in jitsi meet room';
|
||||
message = 'Press SPACE or touch here to enter Jitsi Meet room';
|
||||
}
|
||||
layoutManager.addActionButton('jitsiRoom', message.toString(), () => {
|
||||
openJitsiRoomFunction();
|
||||
@ -708,9 +753,9 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
});
|
||||
this.gameMap.onPropertyChange('silent', (newValue, oldValue) => {
|
||||
if (newValue === undefined || newValue === false || newValue === '') {
|
||||
this.connection.setSilent(false);
|
||||
this.connection?.setSilent(false);
|
||||
} else {
|
||||
this.connection.setSilent(true);
|
||||
this.connection?.setSilent(true);
|
||||
}
|
||||
});
|
||||
this.gameMap.onPropertyChange('playAudio', (newValue, oldValue, allProps) => {
|
||||
@ -734,7 +779,8 @@ export class GameScene extends ResizableScene implements CenterListener {
|
||||
}
|
||||
|
||||
private listenToIframeEvents(): void {
|
||||
iframeListener.openPopupStream.subscribe((openPopupEvent) => {
|
||||
this.iframeSubscriptionList = [];
|
||||
this.iframeSubscriptionList.push(iframeListener.openPopupStream.subscribe((openPopupEvent) => {
|
||||
|
||||
let objectLayerSquare : ITiledMapObject;
|
||||
const targetObjectData = this.getObjectLayerData(openPopupEvent.targetObject);
|
||||
@ -778,7 +824,6 @@ ${escapedMessage}
|
||||
}
|
||||
id++;
|
||||
}
|
||||
|
||||
this.tweens.add({
|
||||
targets : domElement ,
|
||||
scale : 1,
|
||||
@ -787,9 +832,9 @@ ${escapedMessage}
|
||||
});
|
||||
|
||||
this.popUpElements.set(openPopupEvent.popupId, domElement);
|
||||
});
|
||||
}));
|
||||
|
||||
iframeListener.closePopupStream.subscribe((closePopupEvent) => {
|
||||
this.iframeSubscriptionList.push(iframeListener.closePopupStream.subscribe((closePopupEvent) => {
|
||||
const popUpElement = this.popUpElements.get(closePopupEvent.popupId);
|
||||
if (popUpElement === undefined) {
|
||||
console.error('Could not close popup with ID ', closePopupEvent.popupId,'. Maybe it has already been closed?');
|
||||
@ -805,23 +850,25 @@ ${escapedMessage}
|
||||
this.popUpElements.delete(closePopupEvent.popupId);
|
||||
},
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
iframeListener.disablePlayerControlStream.subscribe(()=>{
|
||||
this.iframeSubscriptionList.push(iframeListener.disablePlayerControlStream.subscribe(()=>{
|
||||
this.userInputManager.disableControls();
|
||||
})
|
||||
iframeListener.enablePlayerControlStream.subscribe(()=>{
|
||||
}));
|
||||
|
||||
this.iframeSubscriptionList.push(iframeListener.enablePlayerControlStream.subscribe(()=>{
|
||||
this.userInputManager.restoreControls();
|
||||
})
|
||||
}));
|
||||
let scriptedBubbleSprite : Sprite;
|
||||
iframeListener.displayBubbleStream.subscribe(()=>{
|
||||
this.iframeSubscriptionList.push(iframeListener.displayBubbleStream.subscribe(()=>{
|
||||
scriptedBubbleSprite = new Sprite(this,this.CurrentPlayer.x + 25,this.CurrentPlayer.y,'circleSprite-white');
|
||||
scriptedBubbleSprite.setDisplayOrigin(48, 48);
|
||||
this.add.existing(scriptedBubbleSprite);
|
||||
})
|
||||
iframeListener.removeBubbleStream.subscribe(()=>{
|
||||
}));
|
||||
|
||||
this.iframeSubscriptionList.push(iframeListener.removeBubbleStream.subscribe(()=>{
|
||||
scriptedBubbleSprite.destroy();
|
||||
})
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
@ -866,11 +913,20 @@ ${escapedMessage}
|
||||
this.simplePeer.closeAllConnections();
|
||||
this.simplePeer?.unregister();
|
||||
this.messageSubscription?.unsubscribe();
|
||||
|
||||
for(const iframeEvents of this.iframeSubscriptionList){
|
||||
iframeEvents.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
private removeAllRemotePlayers(): void {
|
||||
this.MapPlayersByKey.forEach((player: RemotePlayer) => {
|
||||
player.destroy();
|
||||
|
||||
if (player.companion) {
|
||||
player.companion.destroy();
|
||||
}
|
||||
|
||||
this.MapPlayers.remove(player);
|
||||
});
|
||||
this.MapPlayersByKey = new Map<number, RemotePlayer>();
|
||||
@ -918,13 +974,14 @@ ${escapedMessage}
|
||||
}
|
||||
|
||||
private initPositionFromLayerName(layerName: string) {
|
||||
for (const layer of this.mapFile.layers) {
|
||||
if (layerName === layer.name && layer.type === 'tilelayer' && (layerName === defaultStartLayerName || this.isStartLayer(layer))) {
|
||||
for (const layer of this.gameMap.layersIterator) {
|
||||
if ((layerName === layer.name || layer.name.endsWith('/'+layerName)) && layer.type === 'tilelayer' && (layerName === defaultStartLayerName || this.isStartLayer(layer))) {
|
||||
const startPosition = this.startUser(layer);
|
||||
this.startX = startPosition.x + this.mapFile.tilewidth/2;
|
||||
this.startY = startPosition.y + this.mapFile.tileheight/2;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private getExitUrl(layer: ITiledMapLayer): string|undefined {
|
||||
@ -947,7 +1004,7 @@ ${escapedMessage}
|
||||
}
|
||||
|
||||
private getProperty(layer: ITiledMapLayer|ITiledMap, name: string): string|boolean|number|undefined {
|
||||
const properties: ITiledMapLayerProperty[] = layer.properties;
|
||||
const properties: ITiledMapLayerProperty[]|undefined = layer.properties;
|
||||
if (!properties) {
|
||||
return undefined;
|
||||
}
|
||||
@ -959,7 +1016,7 @@ ${escapedMessage}
|
||||
}
|
||||
|
||||
private getProperties(layer: ITiledMapLayer|ITiledMap, name: string): (string|number|boolean|undefined)[] {
|
||||
const properties: ITiledMapLayerProperty[] = layer.properties;
|
||||
const properties: ITiledMapLayerProperty[]|undefined = layer.properties;
|
||||
if (!properties) {
|
||||
return [];
|
||||
}
|
||||
@ -973,7 +1030,7 @@ ${escapedMessage}
|
||||
await gameManager.loadMap(room, this.scene);
|
||||
}
|
||||
|
||||
private startUser(layer: ITiledMapLayer): PositionInterface {
|
||||
private startUser(layer: ITiledMapTileLayer): PositionInterface {
|
||||
const tiles = layer.data;
|
||||
if (typeof(tiles) === 'string') {
|
||||
throw new Error('The content of a JSON map must be filled as a JSON array, not as a string');
|
||||
@ -1041,7 +1098,9 @@ ${escapedMessage}
|
||||
texturesPromise,
|
||||
PlayerAnimationDirections.Down,
|
||||
false,
|
||||
this.userInputManager
|
||||
this.userInputManager,
|
||||
this.companion,
|
||||
this.companion !== null ? lazyLoadCompanionResource(this.load, this.companion) : undefined
|
||||
);
|
||||
}catch (err){
|
||||
if(err instanceof TextureError) {
|
||||
@ -1127,7 +1186,7 @@ ${escapedMessage}
|
||||
this.lastMoveEventSent = event;
|
||||
this.lastSentTick = this.currentTick;
|
||||
const camera = this.cameras.main;
|
||||
this.connection.sharePosition(event.x, event.y, event.direction, event.moving, {
|
||||
this.connection?.sharePosition(event.x, event.y, event.direction, event.moving, {
|
||||
left: camera.scrollX,
|
||||
top: camera.scrollY,
|
||||
right: camera.scrollX + camera.width,
|
||||
@ -1193,7 +1252,7 @@ ${escapedMessage}
|
||||
* Put all the players on the map on map load.
|
||||
*/
|
||||
private doInitUsersPosition(usersPosition: MessageUserPositionInterface[]): void {
|
||||
const currentPlayerId = this.connection.getUserId();
|
||||
const currentPlayerId = this.connection?.getUserId();
|
||||
this.removeAllRemotePlayers();
|
||||
// load map
|
||||
usersPosition.forEach((userPosition : MessageUserPositionInterface) => {
|
||||
@ -1233,7 +1292,9 @@ ${escapedMessage}
|
||||
addPlayerData.name,
|
||||
texturesPromise,
|
||||
addPlayerData.position.direction as PlayerAnimationDirections,
|
||||
addPlayerData.position.moving
|
||||
addPlayerData.position.moving,
|
||||
addPlayerData.companion,
|
||||
addPlayerData.companion !== null ? lazyLoadCompanionResource(this.load, addPlayerData.companion) : undefined
|
||||
);
|
||||
this.MapPlayers.add(player);
|
||||
this.MapPlayersByKey.set(player.userId, player);
|
||||
@ -1256,6 +1317,11 @@ ${escapedMessage}
|
||||
console.error('Cannot find user with id ', userId);
|
||||
} else {
|
||||
player.destroy();
|
||||
|
||||
if (player.companion) {
|
||||
player.companion.destroy();
|
||||
}
|
||||
|
||||
this.MapPlayers.remove(player);
|
||||
}
|
||||
this.MapPlayersByKey.delete(userId);
|
||||
@ -1300,7 +1366,7 @@ ${escapedMessage}
|
||||
this,
|
||||
Math.round(groupPositionMessage.position.x),
|
||||
Math.round(groupPositionMessage.position.y),
|
||||
groupPositionMessage.groupSize === 4 ? 'circleSprite-red' : 'circleSprite-white'
|
||||
groupPositionMessage.groupSize === MAX_PER_GROUP ? 'circleSprite-red' : 'circleSprite-white'
|
||||
);
|
||||
sprite.setDisplayOrigin(48, 48);
|
||||
this.add.existing(sprite);
|
||||
@ -1330,7 +1396,7 @@ ${escapedMessage}
|
||||
* Sends to the server an event emitted by one of the ActionableItems.
|
||||
*/
|
||||
emitActionableEvent(itemId: number, eventName: string, state: unknown, parameters: unknown) {
|
||||
this.connection.emitActionableEvent(itemId, eventName, state, parameters);
|
||||
this.connection?.emitActionableEvent(itemId, eventName, state, parameters);
|
||||
}
|
||||
|
||||
public onResize(): void {
|
||||
@ -1338,7 +1404,7 @@ ${escapedMessage}
|
||||
|
||||
// Send new viewport to server
|
||||
const camera = this.cameras.main;
|
||||
this.connection.setViewport({
|
||||
this.connection?.setViewport({
|
||||
left: camera.scrollX,
|
||||
top: camera.scrollY,
|
||||
right: camera.scrollX + camera.width,
|
||||
@ -1394,7 +1460,7 @@ ${escapedMessage}
|
||||
const jitsiUrl = allProps.get("jitsiUrl") as string|undefined;
|
||||
|
||||
jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl);
|
||||
this.connection.setSilent(true);
|
||||
this.connection?.setSilent(true);
|
||||
mediaManager.hideGameOverlay();
|
||||
|
||||
//permit to stop jitsi when user close iframe
|
||||
|
@ -1,46 +1,32 @@
|
||||
import {EnableCameraSceneName} from "./EnableCameraScene";
|
||||
import {TextField} from "../Components/TextField";
|
||||
import Image = Phaser.GameObjects.Image;
|
||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||
import {loadAllLayers, loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager";
|
||||
import {loadAllLayers} from "../Entity/PlayerTexturesLoadingManager";
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import Container = Phaser.GameObjects.Container;
|
||||
import {gameManager} from "../Game/GameManager";
|
||||
import {ResizableScene} from "./ResizableScene";
|
||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||
import {addLoader} from "../Components/Loader";
|
||||
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
||||
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
|
||||
import { MenuScene } from "../Menu/MenuScene";
|
||||
import { SelectCharacterSceneName } from "./SelectCharacterScene";
|
||||
|
||||
export const CustomizeSceneName = "CustomizeScene";
|
||||
|
||||
enum CustomizeTextures{
|
||||
icon = "icon",
|
||||
arrowRight = "arrow_right",
|
||||
mainFont = "main_font",
|
||||
arrowUp = "arrow_up",
|
||||
}
|
||||
export const CustomizeSceneKey = "CustomizeScene";
|
||||
const customizeSceneKey = 'customizeScene';
|
||||
|
||||
export class CustomizeScene extends AbstractCharacterScene {
|
||||
|
||||
private textField!: TextField;
|
||||
private enterField!: TextField;
|
||||
|
||||
private arrowRight!: Image;
|
||||
private arrowLeft!: Image;
|
||||
|
||||
private arrowDown!: Image;
|
||||
private arrowUp!: Image;
|
||||
|
||||
private Rectangle!: Rectangle;
|
||||
|
||||
private logo!: Image;
|
||||
|
||||
private selectedLayers: number[] = [0];
|
||||
private containersRow: Container[][] = [];
|
||||
private activeRow:number = 0;
|
||||
private layers: BodyResourceDescriptionInterface[][] = [];
|
||||
|
||||
private customizeSceneElement!: Phaser.GameObjects.DOMElement;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
key: CustomizeSceneName
|
||||
@ -50,6 +36,8 @@ export class CustomizeScene extends AbstractCharacterScene {
|
||||
preload() {
|
||||
addLoader(this);
|
||||
|
||||
this.load.html(customizeSceneKey, 'resources/html/CustomCharacterScene.html');
|
||||
|
||||
this.layers = loadAllLayers(this.load);
|
||||
this.loadCustomSceneSelectCharacters().then((bodyResourceDescriptions) => {
|
||||
bodyResourceDescriptions.forEach((bodyResourceDescription) => {
|
||||
@ -59,41 +47,43 @@ export class CustomizeScene extends AbstractCharacterScene {
|
||||
this.layers[bodyResourceDescription.level].unshift(bodyResourceDescription);
|
||||
});
|
||||
});
|
||||
|
||||
this.load.image(CustomizeTextures.arrowRight, "resources/objects/arrow_right.png");
|
||||
this.load.image(CustomizeTextures.icon, "resources/logos/tcm_full.png");
|
||||
this.load.image(CustomizeTextures.arrowUp, "resources/objects/arrow_up.png");
|
||||
this.load.bitmapFont(CustomizeTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||
}
|
||||
|
||||
create() {
|
||||
this.textField = new TextField(this, this.game.renderer.width / 2, 30, 'Customize your own Avatar!');
|
||||
const middleX = this.getMiddleX();
|
||||
this.customizeSceneElement = this.add.dom(middleX, 0).createFromCache(customizeSceneKey);
|
||||
MenuScene.revealMenusAfterInit(this.customizeSceneElement, customizeSceneKey);
|
||||
|
||||
this.enterField = new TextField(this, this.game.renderer.width / 2, 40, 'you can start the game by pressing SPACE..');
|
||||
this.customizeSceneElement.addListener('click');
|
||||
this.customizeSceneElement.on('click', (event:MouseEvent) => {
|
||||
event.preventDefault();
|
||||
if((event?.target as HTMLInputElement).id === 'customizeSceneButtonLeft') {
|
||||
this.moveCursorHorizontally(-1);
|
||||
}else if((event?.target as HTMLInputElement).id === 'customizeSceneButtonRight') {
|
||||
this.moveCursorHorizontally(1);
|
||||
}else if((event?.target as HTMLInputElement).id === 'customizeSceneButtonDown') {
|
||||
this.moveCursorVertically(1);
|
||||
}else if((event?.target as HTMLInputElement).id === 'customizeSceneButtonUp') {
|
||||
this.moveCursorVertically(-1);
|
||||
}else if((event?.target as HTMLInputElement).id === 'customizeSceneFormBack') {
|
||||
if(this.activeRow > 0){
|
||||
this.moveCursorVertically(-1);
|
||||
}else{
|
||||
this.backToPreviousScene();
|
||||
}
|
||||
}else if((event?.target as HTMLButtonElement).id === 'customizeSceneFormSubmit') {
|
||||
if(this.activeRow < 5){
|
||||
this.moveCursorVertically(1);
|
||||
}else{
|
||||
this.nextSceneToCamera();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, CustomizeTextures.icon);
|
||||
this.add.existing(this.logo);
|
||||
|
||||
|
||||
this.arrowRight = new Image(this, this.game.renderer.width*0.9, this.game.renderer.height/2, CustomizeTextures.arrowRight);
|
||||
this.add.existing(this.arrowRight);
|
||||
|
||||
this.arrowLeft = new Image(this, this.game.renderer.width/9, this.game.renderer.height/2, CustomizeTextures.arrowRight);
|
||||
this.arrowLeft.flipX = true;
|
||||
this.add.existing(this.arrowLeft);
|
||||
|
||||
|
||||
this.Rectangle = this.add.rectangle(this.cameras.main.worldView.x + this.cameras.main.width / 2, this.cameras.main.worldView.y + this.cameras.main.height / 2, 32, 33)
|
||||
this.Rectangle = this.add.rectangle(this.cameras.main.worldView.x + this.cameras.main.width / 2, this.cameras.main.worldView.y + this.cameras.main.height / 3, 32, 33)
|
||||
this.Rectangle.setStrokeStyle(2, 0xFFFFFF);
|
||||
this.add.existing(this.Rectangle);
|
||||
|
||||
this.arrowDown = new Image(this, this.game.renderer.width - 30, 100, CustomizeTextures.arrowUp);
|
||||
this.arrowDown.flipY = true;
|
||||
this.add.existing(this.arrowDown);
|
||||
|
||||
this.arrowUp = new Image(this, this.game.renderer.width - 30, 60, CustomizeTextures.arrowUp);
|
||||
this.add.existing(this.arrowUp);
|
||||
|
||||
this.createCustomizeLayer(0, 0, 0);
|
||||
this.createCustomizeLayer(0, 0, 1);
|
||||
this.createCustomizeLayer(0, 0, 2);
|
||||
@ -103,19 +93,10 @@ export class CustomizeScene extends AbstractCharacterScene {
|
||||
|
||||
this.moveLayers();
|
||||
this.input.keyboard.on('keyup-ENTER', () => {
|
||||
const layers: string[] = [];
|
||||
let i = 0;
|
||||
for (const layerItem of this.selectedLayers) {
|
||||
if (layerItem !== undefined) {
|
||||
layers.push(this.layers[i][layerItem].name);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
gameManager.setCharacterLayers(layers);
|
||||
|
||||
this.scene.sleep(CustomizeSceneName);
|
||||
gameManager.tryResumingGame(this, EnableCameraSceneName);
|
||||
this.nextSceneToCamera();
|
||||
});
|
||||
this.input.keyboard.on('keyup-BACKSPACE', () => {
|
||||
this.backToPreviousScene();
|
||||
});
|
||||
|
||||
this.input.keyboard.on('keyup-RIGHT', () => this.moveCursorHorizontally(1));
|
||||
@ -145,6 +126,27 @@ export class CustomizeScene extends AbstractCharacterScene {
|
||||
}
|
||||
|
||||
private moveCursorVertically(index:number): void {
|
||||
|
||||
if(index === -1 && this.activeRow === 5){
|
||||
const button = this.customizeSceneElement.getChildByID('customizeSceneFormSubmit') as HTMLButtonElement;
|
||||
button.innerHTML = `Next <img src="resources/objects/arrow_up.png"/>`;
|
||||
}
|
||||
|
||||
if(index === 1 && this.activeRow === 4){
|
||||
const button = this.customizeSceneElement.getChildByID('customizeSceneFormSubmit') as HTMLButtonElement;
|
||||
button.innerText = 'Finish';
|
||||
}
|
||||
|
||||
if(index === -1 && this.activeRow === 1){
|
||||
const button = this.customizeSceneElement.getChildByID('customizeSceneFormBack') as HTMLButtonElement;
|
||||
button.innerText = `Back`;
|
||||
}
|
||||
|
||||
if(index === 1 && this.activeRow === 0){
|
||||
const button = this.customizeSceneElement.getChildByID('customizeSceneFormBack') as HTMLButtonElement;
|
||||
button.innerHTML = `Back <img src="resources/objects/arrow_up.png"/>`;
|
||||
}
|
||||
|
||||
this.activeRow += index;
|
||||
if (this.activeRow < 0) {
|
||||
this.activeRow = 0
|
||||
@ -159,11 +161,6 @@ export class CustomizeScene extends AbstractCharacterScene {
|
||||
localUserStore.setCustomCursorPosition(this.activeRow, this.selectedLayers);
|
||||
}
|
||||
|
||||
update(time: number, delta: number): void {
|
||||
super.update(time, delta);
|
||||
this.enterField.setVisible(!!(Math.floor(time / 500) % 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x, the layer's vertical position
|
||||
* @param y, the layer's horizontal position
|
||||
@ -221,7 +218,7 @@ export class CustomizeScene extends AbstractCharacterScene {
|
||||
*/
|
||||
private moveLayers(): void {
|
||||
const screenCenterX = this.cameras.main.worldView.x + this.cameras.main.width / 2;
|
||||
const screenCenterY = this.cameras.main.worldView.y + this.cameras.main.height / 2;
|
||||
const screenCenterY = this.cameras.main.worldView.y + this.cameras.main.height / 3;
|
||||
const screenWidth = this.game.renderer.width;
|
||||
const screenHeight = this.game.renderer.height;
|
||||
for (let i = 0; i < this.containersRow.length; i++) {
|
||||
@ -260,27 +257,63 @@ export class CustomizeScene extends AbstractCharacterScene {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
this.moveLayers();
|
||||
|
||||
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 / 2;
|
||||
this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 3;
|
||||
|
||||
this.textField.x = this.game.renderer.width/2;
|
||||
|
||||
this.logo.x = this.game.renderer.width - 30;
|
||||
this.logo.y = this.game.renderer.height - 20;
|
||||
|
||||
this.arrowUp.x = this.game.renderer.width - 30;
|
||||
this.arrowUp.y = 60;
|
||||
|
||||
this.arrowDown.x = this.game.renderer.width - 30;
|
||||
this.arrowDown.y = 100;
|
||||
|
||||
this.arrowLeft.x = this.game.renderer.width/9;
|
||||
this.arrowLeft.y = this.game.renderer.height/2;
|
||||
|
||||
this.arrowRight.x = this.game.renderer.width*0.9;
|
||||
this.arrowRight.y = this.game.renderer.height/2;
|
||||
const middleX = this.getMiddleX();
|
||||
this.tweens.add({
|
||||
targets: this.customizeSceneElement,
|
||||
x: middleX,
|
||||
duration: 1000,
|
||||
ease: 'Power3'
|
||||
});
|
||||
}
|
||||
|
||||
protected getMiddleX() : number{
|
||||
return (this.game.renderer.width / 2) -
|
||||
(
|
||||
this.customizeSceneElement
|
||||
&& this.customizeSceneElement.node
|
||||
&& this.customizeSceneElement.node.getBoundingClientRect().width > 0
|
||||
? (this.customizeSceneElement.node.getBoundingClientRect().width / 4)
|
||||
: 150
|
||||
);
|
||||
}
|
||||
|
||||
private nextSceneToCamera(){
|
||||
const layers: string[] = [];
|
||||
let i = 0;
|
||||
for (const layerItem of this.selectedLayers) {
|
||||
if (layerItem !== undefined) {
|
||||
layers.push(this.layers[i][layerItem].name);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (!areCharacterLayersValid(layers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
gameManager.setCharacterLayers(layers);
|
||||
this.scene.sleep(CustomizeSceneName);
|
||||
this.scene.remove(SelectCharacterSceneName);
|
||||
gameManager.tryResumingGame(this, EnableCameraSceneName);
|
||||
}
|
||||
|
||||
private backToPreviousScene(){
|
||||
this.scene.sleep(CustomizeSceneName);
|
||||
this.scene.run(SelectCharacterSceneName);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,16 @@
|
||||
import {gameManager} from "../Game/GameManager";
|
||||
import {TextField} from "../Components/TextField";
|
||||
import Image = Phaser.GameObjects.Image;
|
||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||
import {mediaManager} from "../../WebRtc/MediaManager";
|
||||
import {RESOLUTION} from "../../Enum/EnvironmentVariable";
|
||||
import {SoundMeter} from "../Components/SoundMeter";
|
||||
import {SoundMeterSprite} from "../Components/SoundMeterSprite";
|
||||
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
|
||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||
import {PinchManager} from "../UserInput/PinchManager";
|
||||
import Zone = Phaser.GameObjects.Zone;
|
||||
import { MenuScene } from "../Menu/MenuScene";
|
||||
|
||||
export const EnableCameraSceneName = "EnableCameraScene";
|
||||
enum LoginTextures {
|
||||
@ -16,12 +21,11 @@ enum LoginTextures {
|
||||
arrowUp = "arrow_up"
|
||||
}
|
||||
|
||||
const enableCameraSceneKey = 'enableCameraScene';
|
||||
|
||||
export class EnableCameraScene extends Phaser.Scene {
|
||||
private textField!: TextField;
|
||||
private pressReturnField!: TextField;
|
||||
private cameraNameField!: TextField;
|
||||
private logo!: Image;
|
||||
private arrowLeft!: Image;
|
||||
private arrowRight!: Image;
|
||||
private arrowDown!: Image;
|
||||
@ -35,6 +39,9 @@ export class EnableCameraScene extends Phaser.Scene {
|
||||
private microphoneNameField!: TextField;
|
||||
private repositionCallback!: (this: Window, ev: UIEvent) => void;
|
||||
|
||||
private enableCameraSceneElement!: Phaser.GameObjects.DOMElement;
|
||||
|
||||
private mobileTapZone!: Zone;
|
||||
constructor() {
|
||||
super({
|
||||
key: EnableCameraSceneName
|
||||
@ -43,8 +50,10 @@ export class EnableCameraScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
preload() {
|
||||
|
||||
this.load.html(enableCameraSceneKey, 'resources/html/EnableCameraScene.html');
|
||||
|
||||
this.load.image(LoginTextures.playButton, "resources/objects/play_button.png");
|
||||
this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png");
|
||||
this.load.image(LoginTextures.arrowRight, "resources/objects/arrow_right.png");
|
||||
this.load.image(LoginTextures.arrowUp, "resources/objects/arrow_up.png");
|
||||
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
||||
@ -52,9 +61,29 @@ export class EnableCameraScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
create() {
|
||||
this.textField = new TextField(this, this.game.renderer.width / 2, 20, 'Turn on your camera and microphone');
|
||||
|
||||
this.pressReturnField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 30, 'Press enter to start');
|
||||
const middleX = this.getMiddleX();
|
||||
this.enableCameraSceneElement = this.add.dom(middleX, 0).createFromCache(enableCameraSceneKey);
|
||||
MenuScene.revealMenusAfterInit(this.enableCameraSceneElement, enableCameraSceneKey);
|
||||
|
||||
const continuingButton = this.enableCameraSceneElement.getChildByID('enableCameraSceneFormSubmit') as HTMLButtonElement;
|
||||
continuingButton.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.login();
|
||||
});
|
||||
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
new PinchManager(this);
|
||||
}
|
||||
|
||||
/* FIX ME */
|
||||
this.textField = new TextField(this, this.game.renderer.width / 2, 20, '');
|
||||
|
||||
// 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)
|
||||
.setInteractive().on("pointerdown", () => {
|
||||
this.login();
|
||||
});
|
||||
|
||||
this.cameraNameField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 60, '');
|
||||
|
||||
@ -82,9 +111,6 @@ export class EnableCameraScene extends Phaser.Scene {
|
||||
this.arrowDown.setInteractive().on('pointerdown', this.nextMic.bind(this));
|
||||
this.add.existing(this.arrowDown);
|
||||
|
||||
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, LoginTextures.icon);
|
||||
this.add.existing(this.logo);
|
||||
|
||||
this.input.keyboard.on('keyup-ENTER', () => {
|
||||
this.login();
|
||||
});
|
||||
@ -195,10 +221,9 @@ export class EnableCameraScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
this.textField.x = this.game.renderer.width / 2;
|
||||
this.mobileTapZone.x = this.game.renderer.width / 2;
|
||||
this.cameraNameField.x = this.game.renderer.width / 2;
|
||||
this.microphoneNameField.x = this.game.renderer.width / 2;
|
||||
this.pressReturnField.x = this.game.renderer.width / 2;
|
||||
this.pressReturnField.x = this.game.renderer.width / 2;
|
||||
|
||||
this.cameraNameField.y = bounds.top / RESOLUTION - 8;
|
||||
|
||||
@ -218,18 +243,20 @@ export class EnableCameraScene extends Phaser.Scene {
|
||||
|
||||
this.arrowUp.x = this.microphoneNameField.x - this.microphoneNameField.width / 2 - 16;
|
||||
this.arrowUp.y = this.microphoneNameField.y;
|
||||
|
||||
this.pressReturnField.y = Math.max(this.game.renderer.height - 30, this.microphoneNameField.y + 20);
|
||||
this.logo.x = this.game.renderer.width - 30;
|
||||
this.logo.y = Math.max(this.game.renderer.height - 20, this.microphoneNameField.y + 30);
|
||||
}
|
||||
|
||||
update(time: number, delta: number): void {
|
||||
this.pressReturnField.setVisible(!!(Math.floor(time / 500) % 2));
|
||||
|
||||
this.soundMeterSprite.setVolume(this.soundMeter.getVolume());
|
||||
|
||||
mediaManager.setLastUpdateScene();
|
||||
|
||||
const middleX = this.getMiddleX();
|
||||
this.tweens.add({
|
||||
targets: this.enableCameraSceneElement,
|
||||
x: middleX,
|
||||
duration: 1000,
|
||||
ease: 'Power3'
|
||||
});
|
||||
}
|
||||
|
||||
private login(): void {
|
||||
@ -255,4 +282,15 @@ export class EnableCameraScene extends Phaser.Scene {
|
||||
}
|
||||
this.updateWebCamName();
|
||||
}
|
||||
|
||||
private getMiddleX() : number{
|
||||
return (this.game.renderer.width / 2) -
|
||||
(
|
||||
this.enableCameraSceneElement
|
||||
&& this.enableCameraSceneElement.node
|
||||
&& this.enableCameraSceneElement.node.getBoundingClientRect().width > 0
|
||||
? (this.enableCameraSceneElement.node.getBoundingClientRect().width / 4)
|
||||
: (300 / 2)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,17 @@
|
||||
import {gameManager} from "../Game/GameManager";
|
||||
import {TextField} from "../Components/TextField";
|
||||
import {TextInput} from "../Components/TextInput";
|
||||
import Image = Phaser.GameObjects.Image;
|
||||
import {SelectCharacterSceneName} from "./SelectCharacterScene";
|
||||
import {ResizableScene} from "./ResizableScene";
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import {MenuScene} from "../Menu/MenuScene";
|
||||
import { isUserNameValid } from "../../Connexion/LocalUser";
|
||||
|
||||
//todo: put this constants in a dedicated file
|
||||
export const LoginSceneName = "LoginScene";
|
||||
enum LoginTextures {
|
||||
icon = "icon",
|
||||
mainFont = "main_font"
|
||||
}
|
||||
|
||||
const loginSceneKey = 'loginScene';
|
||||
|
||||
export class LoginScene extends ResizableScene {
|
||||
private nameInput!: TextInput;
|
||||
private textField!: TextField;
|
||||
private infoTextField!: TextField;
|
||||
private pressReturnField!: TextField;
|
||||
private logo!: Image;
|
||||
|
||||
private loginSceneElement!: Phaser.GameObjects.DOMElement;
|
||||
private name: string = '';
|
||||
|
||||
constructor() {
|
||||
@ -28,58 +22,82 @@ export class LoginScene extends ResizableScene {
|
||||
}
|
||||
|
||||
preload() {
|
||||
//this.load.image(LoginTextures.playButton, "resources/objects/play_button.png");
|
||||
this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png");
|
||||
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
||||
this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||
this.load.html(loginSceneKey, 'resources/html/loginScene.html');
|
||||
}
|
||||
|
||||
create() {
|
||||
const middleX = this.getMiddleX();
|
||||
this.loginSceneElement = this.add.dom((middleX/2), 0).createFromCache(loginSceneKey);
|
||||
MenuScene.revealMenusAfterInit(this.loginSceneElement, loginSceneKey);
|
||||
|
||||
this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Enter your name:');
|
||||
this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, 8, this.name,(text: string) => {
|
||||
this.name = text;
|
||||
});
|
||||
|
||||
this.pressReturnField = new TextField(this, this.game.renderer.width / 2, 130, 'Press enter to start');
|
||||
|
||||
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, LoginTextures.icon);
|
||||
this.add.existing(this.logo);
|
||||
|
||||
const infoText = "Commands: \n - Arrows or Z,Q,S,D to move\n - SHIFT to run";
|
||||
this.infoTextField = new TextField(this, 10, this.game.renderer.height - 35, infoText, false);
|
||||
|
||||
this.input.keyboard.on('keyup-ENTER', () => {
|
||||
if (this.name === '') {
|
||||
return
|
||||
const pErrorElement = this.loginSceneElement.getChildByID('errorLoginScene') as HTMLInputElement;
|
||||
const inputElement = this.loginSceneElement.getChildByID('loginSceneName') as HTMLInputElement;
|
||||
inputElement.value = localUserStore.getName() ?? '';
|
||||
inputElement.focus();
|
||||
inputElement.addEventListener('keypress', (event: KeyboardEvent) => {
|
||||
if(inputElement.value.length > 7){
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
this.login(this.name);
|
||||
pErrorElement.innerHTML = '';
|
||||
if(inputElement.value && !isUserNameValid(inputElement.value)){
|
||||
pErrorElement.innerHTML = 'Invalid user name: only letters and numbers are allowed. No spaces.';
|
||||
}
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
this.login(inputElement);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
const continuingButton = this.loginSceneElement.getChildByID('loginSceneFormSubmit') as HTMLButtonElement;
|
||||
continuingButton.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.login(inputElement);
|
||||
});
|
||||
}
|
||||
|
||||
update(time: number, delta: number): void {
|
||||
if (this.name == '') {
|
||||
this.pressReturnField?.setVisible(false);
|
||||
} else {
|
||||
this.pressReturnField?.setVisible(!!(Math.floor(time / 500) % 2));
|
||||
private login(inputElement: HTMLInputElement): void {
|
||||
const pErrorElement = this.loginSceneElement.getChildByID('errorLoginScene') as HTMLInputElement;
|
||||
this.name = inputElement.value;
|
||||
if (this.name === '') {
|
||||
pErrorElement.innerHTML = 'The name is empty';
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
private login(name: string): void {
|
||||
gameManager.setPlayerName(name);
|
||||
if(!isUserNameValid(this.name)){
|
||||
pErrorElement.innerHTML = 'Invalid user name: only letters and numbers are allowed. No spaces.';
|
||||
return
|
||||
}
|
||||
if (this.name === '') return
|
||||
gameManager.setPlayerName(this.name);
|
||||
|
||||
this.scene.stop(LoginSceneName)
|
||||
gameManager.tryResumingGame(this, SelectCharacterSceneName);
|
||||
this.scene.remove(LoginSceneName)
|
||||
}
|
||||
|
||||
public onResize(ev: UIEvent): void {
|
||||
this.textField.x = this.game.renderer.width / 2;
|
||||
this.nameInput.setX(this.game.renderer.width / 2 - 64);
|
||||
this.pressReturnField.x = this.game.renderer.width / 2;
|
||||
this.logo.x = this.game.renderer.width - 30;
|
||||
this.logo.y = this.game.renderer.height - 20;
|
||||
this.infoTextField.y = this.game.renderer.height - 35;
|
||||
update(time: number, delta: number): void {
|
||||
const middleX = this.getMiddleX();
|
||||
this.tweens.add({
|
||||
targets: this.loginSceneElement,
|
||||
x: (middleX/2),
|
||||
duration: 1000,
|
||||
ease: 'Power3'
|
||||
});
|
||||
}
|
||||
|
||||
public onResize(ev: UIEvent): void {
|
||||
const middleX = this.getMiddleX();
|
||||
this.tweens.add({
|
||||
targets: this.loginSceneElement,
|
||||
x: (middleX/2),
|
||||
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 : 0);
|
||||
}
|
||||
}
|
||||
|
77
front/src/Phaser/Login/SelectCharacterMobileScene.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import {gameManager} from "../Game/GameManager";
|
||||
import {TextField} from "../Components/TextField";
|
||||
import Image = Phaser.GameObjects.Image;
|
||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||
import {EnableCameraSceneName} from "./EnableCameraScene";
|
||||
import {CustomizeSceneName} from "./CustomizeScene";
|
||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||
import {loadAllDefaultModels} from "../Entity/PlayerTexturesLoadingManager";
|
||||
import {addLoader} from "../Components/Loader";
|
||||
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
||||
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
|
||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||
import {PinchManager} from "../UserInput/PinchManager";
|
||||
import {MenuScene} from "../Menu/MenuScene";
|
||||
import { SelectCharacterScene, SelectCharacterSceneName } from "./SelectCharacterScene";
|
||||
|
||||
export class SelectCharacterMobileScene extends SelectCharacterScene {
|
||||
|
||||
create(){
|
||||
super.create();
|
||||
this.selectedRectangle.destroy();
|
||||
}
|
||||
|
||||
protected defineSetupPlayer(numero: number){
|
||||
const deltaX = 30;
|
||||
const deltaY = 2;
|
||||
let [playerX, playerY] = this.getCharacterPosition();
|
||||
let playerVisible = true;
|
||||
let playerScale = 1.5;
|
||||
let playserOpactity = 1;
|
||||
|
||||
if( this.currentSelectUser !== numero ){
|
||||
playerVisible = false;
|
||||
}
|
||||
if( numero === (this.currentSelectUser + 1) ){
|
||||
playerY -= deltaY;
|
||||
playerX += deltaX;
|
||||
playerScale = 0.8;
|
||||
playserOpactity = 0.6;
|
||||
playerVisible = true;
|
||||
}
|
||||
if( numero === (this.currentSelectUser + 2) ){
|
||||
playerY -= deltaY;
|
||||
playerX += (deltaX * 2);
|
||||
playerScale = 0.8;
|
||||
playserOpactity = 0.6;
|
||||
playerVisible = true;
|
||||
}
|
||||
if( numero === (this.currentSelectUser - 1) ){
|
||||
playerY -= deltaY;
|
||||
playerX -= deltaX;
|
||||
playerScale = 0.8;
|
||||
playserOpactity = 0.6;
|
||||
playerVisible = true;
|
||||
}
|
||||
if( numero === (this.currentSelectUser - 2) ){
|
||||
playerY -= deltaY;
|
||||
playerX -= (deltaX * 2);
|
||||
playerScale = 0.8;
|
||||
playserOpactity = 0.6;
|
||||
playerVisible = true;
|
||||
}
|
||||
return {playerX, playerY, playerScale, playserOpactity, playerVisible}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns pixel position by on column and row number
|
||||
*/
|
||||
protected getCharacterPosition(): [number, number] {
|
||||
return [
|
||||
this.game.renderer.width / 2,
|
||||
this.game.renderer.height / 3
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -1,254 +1,275 @@
|
||||
import {gameManager} from "../Game/GameManager";
|
||||
import {TextField} from "../Components/TextField";
|
||||
import Image = Phaser.GameObjects.Image;
|
||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||
import {EnableCameraSceneName} from "./EnableCameraScene";
|
||||
import {CustomizeSceneName} from "./CustomizeScene";
|
||||
import {ResizableScene} from "./ResizableScene";
|
||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||
import {loadAllDefaultModels, loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager";
|
||||
import {loadAllDefaultModels} from "../Entity/PlayerTexturesLoadingManager";
|
||||
import {addLoader} from "../Components/Loader";
|
||||
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
||||
|
||||
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
|
||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||
import {PinchManager} from "../UserInput/PinchManager";
|
||||
import {MenuScene} from "../Menu/MenuScene";
|
||||
import { SelectCharacterMobileScene } from "./SelectCharacterMobileScene";
|
||||
|
||||
//todo: put this constants in a dedicated file
|
||||
export const SelectCharacterSceneName = "SelectCharacterScene";
|
||||
enum LoginTextures {
|
||||
playButton = "play_button",
|
||||
icon = "icon",
|
||||
mainFont = "main_font",
|
||||
customizeButton = "customize_button",
|
||||
customizeButtonSelected = "customize_button_selected"
|
||||
}
|
||||
|
||||
const selectCharacterKey = 'selectCharacterScene';
|
||||
|
||||
export class SelectCharacterScene extends AbstractCharacterScene {
|
||||
private readonly nbCharactersPerRow = 6;
|
||||
private textField!: TextField;
|
||||
private pressReturnField!: TextField;
|
||||
private logo!: Image;
|
||||
private customizeButton!: Image;
|
||||
private customizeButtonSelected!: Image;
|
||||
protected readonly nbCharactersPerRow = 6;
|
||||
protected selectedPlayer!: Phaser.Physics.Arcade.Sprite|null; // null if we are selecting the "customize" option
|
||||
protected players: Array<Phaser.Physics.Arcade.Sprite> = new Array<Phaser.Physics.Arcade.Sprite>();
|
||||
protected playerModels!: BodyResourceDescriptionInterface[];
|
||||
|
||||
private selectedRectangle!: Rectangle;
|
||||
private selectedRectangleXPos = 0; // Number of the character selected in the rows
|
||||
private selectedRectangleYPos = 0; // Number of the character selected in the columns
|
||||
private selectedPlayer!: Phaser.Physics.Arcade.Sprite|null; // null if we are selecting the "customize" option
|
||||
private players: Array<Phaser.Physics.Arcade.Sprite> = new Array<Phaser.Physics.Arcade.Sprite>();
|
||||
private playerModels!: BodyResourceDescriptionInterface[];
|
||||
protected selectedRectangle!: Rectangle;
|
||||
|
||||
protected selectCharacterSceneElement!: Phaser.GameObjects.DOMElement;
|
||||
protected currentSelectUser = 0;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
key: SelectCharacterSceneName
|
||||
key: SelectCharacterSceneName,
|
||||
});
|
||||
}
|
||||
|
||||
preload() {
|
||||
addLoader(this);
|
||||
|
||||
this.load.html(selectCharacterKey, 'resources/html/selectCharacterScene.html');
|
||||
|
||||
this.loadSelectSceneCharacters().then((bodyResourceDescriptions) => {
|
||||
bodyResourceDescriptions.forEach((bodyResourceDescription) => {
|
||||
this.playerModels.push(bodyResourceDescription);
|
||||
});
|
||||
})
|
||||
|
||||
this.load.image(LoginTextures.playButton, "resources/objects/play_button.png");
|
||||
this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png");
|
||||
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
||||
this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||
this.playerModels = loadAllDefaultModels(this.load);
|
||||
this.load.image(LoginTextures.customizeButton, 'resources/objects/customize.png');
|
||||
this.load.image(LoginTextures.customizeButtonSelected, 'resources/objects/customize_selected.png');
|
||||
|
||||
addLoader(this);
|
||||
}
|
||||
|
||||
create() {
|
||||
this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Select your character');
|
||||
this.pressReturnField = new TextField(
|
||||
this,
|
||||
this.game.renderer.width / 2,
|
||||
90 + 32 * Math.ceil( this.playerModels.length / this.nbCharactersPerRow) + 40,
|
||||
'Press enter to start');
|
||||
|
||||
const middleX = this.getMiddleX();
|
||||
this.selectCharacterSceneElement = this.add.dom(middleX, 0).createFromCache(selectCharacterKey);
|
||||
MenuScene.revealMenusAfterInit(this.selectCharacterSceneElement, selectCharacterKey);
|
||||
|
||||
this.selectCharacterSceneElement.addListener('click');
|
||||
this.selectCharacterSceneElement.on('click', (event:MouseEvent) => {
|
||||
event.preventDefault();
|
||||
if((event?.target as HTMLInputElement).id === 'selectCharacterButtonLeft') {
|
||||
this.moveToLeft();
|
||||
}else if((event?.target as HTMLInputElement).id === 'selectCharacterButtonRight') {
|
||||
this.moveToRight();
|
||||
}else if((event?.target as HTMLInputElement).id === 'selectCharacterSceneFormSubmit') {
|
||||
this.nextSceneToCameraScene();
|
||||
}else if((event?.target as HTMLInputElement).id === 'selectCharacterSceneFormCustomYourOwnSubmit') {
|
||||
this.nextSceneToCustomizeScene();
|
||||
}
|
||||
});
|
||||
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
new PinchManager(this);
|
||||
}
|
||||
|
||||
const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16;
|
||||
|
||||
this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xFFFFFF);
|
||||
|
||||
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, LoginTextures.icon);
|
||||
this.add.existing(this.logo);
|
||||
|
||||
this.input.keyboard.on('keyup-ENTER', () => {
|
||||
return this.nextScene();
|
||||
});
|
||||
|
||||
this.input.keyboard.on('keydown-RIGHT', () => {
|
||||
if(this.selectedRectangleYPos * this.nbCharactersPerRow + (this.selectedRectangleXPos + 2))
|
||||
if (
|
||||
this.selectedRectangleXPos < this.nbCharactersPerRow - 1
|
||||
&& ((this.selectedRectangleYPos * this.nbCharactersPerRow) + (this.selectedRectangleXPos + 1) + 1) <= this.playerModels.length
|
||||
) {
|
||||
this.selectedRectangleXPos++;
|
||||
}
|
||||
this.updateSelectedPlayer();
|
||||
});
|
||||
this.input.keyboard.on('keydown-LEFT', () => {
|
||||
if (
|
||||
this.selectedRectangleXPos > 0
|
||||
&& ((this.selectedRectangleYPos * this.nbCharactersPerRow) + (this.selectedRectangleXPos - 1) + 1) <= this.playerModels.length
|
||||
) {
|
||||
this.selectedRectangleXPos--;
|
||||
}
|
||||
this.updateSelectedPlayer();
|
||||
});
|
||||
this.input.keyboard.on('keydown-DOWN', () => {
|
||||
if (
|
||||
this.selectedRectangleYPos < Math.ceil(this.playerModels.length / this.nbCharactersPerRow)
|
||||
&& (
|
||||
(((this.selectedRectangleYPos + 1) * this.nbCharactersPerRow) + this.selectedRectangleXPos + 1) <= this.playerModels.length // check if player isn't empty
|
||||
|| (this.selectedRectangleYPos + 1) === Math.ceil(this.playerModels.length / this.nbCharactersPerRow) // check if is custom rectangle
|
||||
)
|
||||
) {
|
||||
this.selectedRectangleYPos++;
|
||||
}
|
||||
this.updateSelectedPlayer();
|
||||
});
|
||||
this.input.keyboard.on('keydown-UP', () => {
|
||||
if (
|
||||
this.selectedRectangleYPos > 0
|
||||
&& (((this.selectedRectangleYPos - 1) * this.nbCharactersPerRow) + this.selectedRectangleXPos + 1) <= this.playerModels.length
|
||||
) {
|
||||
this.selectedRectangleYPos--;
|
||||
}
|
||||
this.updateSelectedPlayer();
|
||||
});
|
||||
this.selectedRectangle.setDepth(2);
|
||||
|
||||
/*create user*/
|
||||
this.createCurrentPlayer();
|
||||
|
||||
const playerNumber = localUserStore.getPlayerCharacterIndex();
|
||||
if (playerNumber && playerNumber !== -1) {
|
||||
this.selectedRectangleXPos = playerNumber % this.nbCharactersPerRow;
|
||||
this.selectedRectangleYPos = Math.floor(playerNumber / this.nbCharactersPerRow);
|
||||
this.updateSelectedPlayer();
|
||||
} else if (playerNumber === -1) {
|
||||
this.selectedRectangleYPos = Math.ceil(this.playerModels.length / this.nbCharactersPerRow);
|
||||
this.updateSelectedPlayer();
|
||||
|
||||
this.input.keyboard.on('keyup-ENTER', () => {
|
||||
return this.nextSceneToCameraScene();
|
||||
});
|
||||
|
||||
this.input.keyboard.on('keydown-RIGHT', () => {
|
||||
this.moveToRight();
|
||||
});
|
||||
this.input.keyboard.on('keydown-LEFT', () => {
|
||||
this.moveToLeft();
|
||||
});
|
||||
this.input.keyboard.on('keydown-UP', () => {
|
||||
this.moveToUp();
|
||||
});
|
||||
this.input.keyboard.on('keydown-DOWN', () => {
|
||||
this.moveToDown();
|
||||
});
|
||||
}
|
||||
|
||||
protected nextSceneToCameraScene(): void {
|
||||
if (this.selectedPlayer !== null && !areCharacterLayersValid([this.selectedPlayer.texture.key])) {
|
||||
return;
|
||||
}
|
||||
if(!this.selectedPlayer){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
update(time: number, delta: number): void {
|
||||
this.pressReturnField.setVisible(!!(Math.floor(time / 500) % 2));
|
||||
}
|
||||
|
||||
private nextScene(): void {
|
||||
this.scene.stop(SelectCharacterSceneName);
|
||||
if (this.selectedPlayer !== null) {
|
||||
gameManager.setCharacterLayers([this.selectedPlayer.texture.key]);
|
||||
gameManager.tryResumingGame(this, EnableCameraSceneName);
|
||||
} else {
|
||||
this.scene.run(CustomizeSceneName);
|
||||
}
|
||||
gameManager.setCharacterLayers([this.selectedPlayer.texture.key]);
|
||||
gameManager.tryResumingGame(this, EnableCameraSceneName);
|
||||
this.scene.remove(SelectCharacterSceneName);
|
||||
}
|
||||
|
||||
protected nextSceneToCustomizeScene(): void {
|
||||
if (this.selectedPlayer !== null && !areCharacterLayersValid([this.selectedPlayer.texture.key])) {
|
||||
return;
|
||||
}
|
||||
this.scene.sleep(SelectCharacterSceneName);
|
||||
this.scene.run(CustomizeSceneName);
|
||||
}
|
||||
|
||||
createCurrentPlayer(): void {
|
||||
for (let i = 0; i <this.playerModels.length; i++) {
|
||||
const playerResource = this.playerModels[i];
|
||||
|
||||
const col = i % this.nbCharactersPerRow;
|
||||
const row = Math.floor(i / this.nbCharactersPerRow);
|
||||
|
||||
const [x, y] = this.getCharacterPosition(col, row);
|
||||
const player = this.physics.add.sprite(x, y, playerResource.name, 0);
|
||||
player.setBounce(0.2);
|
||||
player.setCollideWorldBounds(true);
|
||||
const [middleX, middleY] = this.getCharacterPosition();
|
||||
const player = this.physics.add.sprite(middleX, middleY, playerResource.name, 0);
|
||||
this.setUpPlayer(player, i);
|
||||
this.anims.create({
|
||||
key: playerResource.name,
|
||||
frames: this.anims.generateFrameNumbers(playerResource.name, {start: 0, end: 2,}),
|
||||
frameRate: 10,
|
||||
frames: this.anims.generateFrameNumbers(playerResource.name, {start: 0, end: 11}),
|
||||
frameRate: 8,
|
||||
repeat: -1
|
||||
});
|
||||
player.setInteractive().on("pointerdown", () => {
|
||||
this.selectedRectangleXPos = col;
|
||||
this.selectedRectangleYPos = row;
|
||||
this.updateSelectedPlayer();
|
||||
if(this.currentSelectUser === i){
|
||||
return;
|
||||
}
|
||||
this.currentSelectUser = i;
|
||||
this.moveUser();
|
||||
});
|
||||
this.players.push(player);
|
||||
}
|
||||
|
||||
const maxRow = Math.ceil( this.playerModels.length / this.nbCharactersPerRow);
|
||||
this.customizeButton = new Image(this, this.game.renderer.width / 2, 90 + 32 * maxRow + 6, LoginTextures.customizeButton);
|
||||
this.customizeButton.setOrigin(0.5, 0.5);
|
||||
this.add.existing(this.customizeButton);
|
||||
this.customizeButtonSelected = new Image(this, this.game.renderer.width / 2, 90 + 32 * maxRow + 6, LoginTextures.customizeButtonSelected);
|
||||
this.customizeButtonSelected.setOrigin(0.5, 0.5);
|
||||
this.customizeButtonSelected.setVisible(false);
|
||||
this.add.existing(this.customizeButtonSelected);
|
||||
this.selectedPlayer = this.players[this.currentSelectUser];
|
||||
this.selectedPlayer.play(this.playerModels[this.currentSelectUser].name);
|
||||
}
|
||||
|
||||
this.customizeButton.setInteractive().on("pointerdown", () => {
|
||||
this.selectedRectangleYPos = Math.ceil(this.playerModels.length / this.nbCharactersPerRow);
|
||||
this.updateSelectedPlayer();
|
||||
});
|
||||
protected moveUser(){
|
||||
for(let i = 0; i < this.players.length; i++){
|
||||
const player = this.players[i];
|
||||
this.setUpPlayer(player, i);
|
||||
}
|
||||
this.updateSelectedPlayer();
|
||||
}
|
||||
|
||||
this.selectedPlayer = this.players[0];
|
||||
this.selectedPlayer.play(this.playerModels[0].name);
|
||||
protected moveToLeft(){
|
||||
if(this.currentSelectUser === 0){
|
||||
return;
|
||||
}
|
||||
this.currentSelectUser -= 1;
|
||||
this.moveUser();
|
||||
}
|
||||
|
||||
protected moveToRight(){
|
||||
if(this.currentSelectUser === (this.players.length - 1)){
|
||||
return;
|
||||
}
|
||||
this.currentSelectUser += 1;
|
||||
this.moveUser();
|
||||
}
|
||||
|
||||
protected moveToUp(){
|
||||
if(this.currentSelectUser < this.nbCharactersPerRow){
|
||||
return;
|
||||
}
|
||||
this.currentSelectUser -= this.nbCharactersPerRow;
|
||||
this.moveUser();
|
||||
}
|
||||
|
||||
protected moveToDown(){
|
||||
if((this.currentSelectUser + this.nbCharactersPerRow) > (this.players.length - 1)){
|
||||
return;
|
||||
}
|
||||
this.currentSelectUser += this.nbCharactersPerRow;
|
||||
this.moveUser();
|
||||
}
|
||||
|
||||
protected defineSetupPlayer(numero: number){
|
||||
const deltaX = 32;
|
||||
const deltaY = 32;
|
||||
let [playerX, playerY] = this.getCharacterPosition(); // player X and player y are middle of the
|
||||
|
||||
playerX = ( (playerX - (deltaX * 2.5)) + ((deltaX) * (numero % this.nbCharactersPerRow)) ); // calcul position on line users
|
||||
playerY = ( (playerY - (deltaY * 2)) + ((deltaY) * ( Math.floor(numero / this.nbCharactersPerRow) )) ); // calcul position on column users
|
||||
|
||||
const playerVisible = true;
|
||||
const playerScale = 1;
|
||||
const playserOpactity = 1;
|
||||
|
||||
// if selected
|
||||
if( numero === this.currentSelectUser ){
|
||||
this.selectedRectangle.setX(playerX);
|
||||
this.selectedRectangle.setY(playerY);
|
||||
}
|
||||
|
||||
return {playerX, playerY, playerScale, playserOpactity, playerVisible}
|
||||
}
|
||||
|
||||
protected setUpPlayer(player: Phaser.Physics.Arcade.Sprite, numero: number){
|
||||
|
||||
const {playerX, playerY, playerScale, playserOpactity, playerVisible} = this.defineSetupPlayer(numero);
|
||||
player.setBounce(0.2);
|
||||
player.setCollideWorldBounds(true);
|
||||
player.setVisible( playerVisible );
|
||||
player.setScale(playerScale, playerScale);
|
||||
player.setAlpha(playserOpactity);
|
||||
player.setX(playerX);
|
||||
player.setY(playerY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns pixel position by on column and row number
|
||||
*/
|
||||
private getCharacterPosition(x: number, y: number): [number, number] {
|
||||
protected getCharacterPosition(): [number, number] {
|
||||
return [
|
||||
this.game.renderer.width / 2 + 16 + (x - this.nbCharactersPerRow / 2) * 32,
|
||||
y * 32 + 90
|
||||
this.game.renderer.width / 2,
|
||||
this.game.renderer.height / 2.5
|
||||
];
|
||||
}
|
||||
|
||||
private updateSelectedPlayer(): void {
|
||||
this.selectedPlayer?.anims.pause();
|
||||
// If we selected the customize button
|
||||
if (this.selectedRectangleYPos === Math.ceil(this.playerModels.length / this.nbCharactersPerRow)) {
|
||||
this.selectedPlayer = null;
|
||||
this.selectedRectangle.setVisible(false);
|
||||
this.customizeButtonSelected.setVisible(true);
|
||||
this.customizeButton.setVisible(false);
|
||||
localUserStore.setPlayerCharacterIndex(-1);
|
||||
return;
|
||||
}
|
||||
this.customizeButtonSelected.setVisible(false);
|
||||
this.customizeButton.setVisible(true);
|
||||
const [x, y] = this.getCharacterPosition(this.selectedRectangleXPos, this.selectedRectangleYPos);
|
||||
this.selectedRectangle.setVisible(true);
|
||||
this.selectedRectangle.setX(x);
|
||||
this.selectedRectangle.setY(y);
|
||||
this.selectedRectangle.setSize(32, 32);
|
||||
const playerNumber = this.selectedRectangleXPos + this.selectedRectangleYPos * this.nbCharactersPerRow;
|
||||
const player = this.players[playerNumber];
|
||||
player.play(this.playerModels[playerNumber].name);
|
||||
protected updateSelectedPlayer(): void {
|
||||
this.selectedPlayer?.anims.pause(this.selectedPlayer?.anims.currentAnim.frames[0]);
|
||||
const player = this.players[this.currentSelectUser];
|
||||
player.play(this.playerModels[this.currentSelectUser].name);
|
||||
this.selectedPlayer = player;
|
||||
localUserStore.setPlayerCharacterIndex(playerNumber);
|
||||
localUserStore.setPlayerCharacterIndex(this.currentSelectUser);
|
||||
}
|
||||
|
||||
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 {
|
||||
this.textField.x = this.game.renderer.width / 2;
|
||||
this.pressReturnField.x = this.game.renderer.width / 2;
|
||||
this.logo.x = this.game.renderer.width - 30;
|
||||
this.logo.y = this.game.renderer.height - 20;
|
||||
this.customizeButton.x = this.game.renderer.width / 2;
|
||||
this.customizeButtonSelected.x = this.game.renderer.width / 2;
|
||||
//move position of user
|
||||
this.moveUser();
|
||||
|
||||
for (let i = 0; i <this.playerModels.length; i++) {
|
||||
const player = this.players[i];
|
||||
|
||||
const col = i % this.nbCharactersPerRow;
|
||||
const row = Math.floor(i / this.nbCharactersPerRow);
|
||||
|
||||
const [x, y] = this.getCharacterPosition(col, row);
|
||||
player.x = x;
|
||||
player.y = y;
|
||||
}
|
||||
this.updateSelectedPlayer();
|
||||
const middleX = this.getMiddleX();
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
252
front/src/Phaser/Login/SelectCompanionScene.ts
Normal file
@ -0,0 +1,252 @@
|
||||
import Image = Phaser.GameObjects.Image;
|
||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||
import { addLoader } from "../Components/Loader";
|
||||
import { gameManager} from "../Game/GameManager";
|
||||
import { ResizableScene } from "./ResizableScene";
|
||||
import { EnableCameraSceneName } from "./EnableCameraScene";
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures";
|
||||
import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingManager";
|
||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||
import {PinchManager} from "../UserInput/PinchManager";
|
||||
import { MenuScene } from "../Menu/MenuScene";
|
||||
|
||||
export const SelectCompanionSceneName = "SelectCompanionScene";
|
||||
|
||||
const selectCompanionSceneKey = 'selectCompanionScene';
|
||||
|
||||
export class SelectCompanionScene extends ResizableScene {
|
||||
private selectedCompanion!: Phaser.Physics.Arcade.Sprite;
|
||||
private companions: Array<Phaser.Physics.Arcade.Sprite> = new Array<Phaser.Physics.Arcade.Sprite>();
|
||||
private companionModels: Array<CompanionResourceDescriptionInterface> = [];
|
||||
|
||||
private selectCompanionSceneElement!: Phaser.GameObjects.DOMElement;
|
||||
private currentCompanion = 0;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
key: SelectCompanionSceneName
|
||||
});
|
||||
}
|
||||
|
||||
preload() {
|
||||
addLoader(this);
|
||||
|
||||
this.load.html(selectCompanionSceneKey, 'resources/html/SelectCompanionScene.html');
|
||||
|
||||
getAllCompanionResources(this.load).forEach(model => {
|
||||
this.companionModels.push(model);
|
||||
});
|
||||
|
||||
addLoader(this);
|
||||
}
|
||||
|
||||
create() {
|
||||
|
||||
const middleX = this.getMiddleX();
|
||||
this.selectCompanionSceneElement = this.add.dom(middleX, 0).createFromCache(selectCompanionSceneKey);
|
||||
MenuScene.revealMenusAfterInit(this.selectCompanionSceneElement, selectCompanionSceneKey);
|
||||
|
||||
this.selectCompanionSceneElement.addListener('click');
|
||||
this.selectCompanionSceneElement.on('click', (event:MouseEvent) => {
|
||||
event.preventDefault();
|
||||
if((event?.target as HTMLInputElement).id === 'selectCharacterButtonLeft') {
|
||||
this.moveToLeft();
|
||||
}else if((event?.target as HTMLInputElement).id === 'selectCharacterButtonRight') {
|
||||
this.moveToRight();
|
||||
}else if((event?.target as HTMLInputElement).id === 'selectCompanionSceneFormSubmit') {
|
||||
this.nextScene();
|
||||
}else if((event?.target as HTMLInputElement).id === 'selectCompanionSceneFormBack') {
|
||||
this._nextScene();
|
||||
}
|
||||
});
|
||||
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
new PinchManager(this);
|
||||
}
|
||||
|
||||
// input events
|
||||
this.input.keyboard.on('keyup-ENTER', this.nextScene.bind(this));
|
||||
|
||||
this.input.keyboard.on('keydown-RIGHT', this.moveToRight.bind(this));
|
||||
this.input.keyboard.on('keydown-LEFT', this.moveToLeft.bind(this));
|
||||
|
||||
if(localUserStore.getCompanion()){
|
||||
const companionIndex = this.companionModels.findIndex((companion) => companion.name === localUserStore.getCompanion());
|
||||
if(companionIndex > -1 || companionIndex < this.companions.length){
|
||||
this.currentCompanion = companionIndex;
|
||||
this.selectedCompanion = this.companions[companionIndex];
|
||||
}
|
||||
}
|
||||
|
||||
localUserStore.setCompanion(null);
|
||||
gameManager.setCompanion(null);
|
||||
|
||||
this.createCurrentCompanion();
|
||||
this.updateSelectedCompanion();
|
||||
}
|
||||
|
||||
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 {
|
||||
localUserStore.setCompanion(this.companionModels[this.currentCompanion].name);
|
||||
gameManager.setCompanion(this.companionModels[this.currentCompanion].name);
|
||||
|
||||
this._nextScene();
|
||||
}
|
||||
|
||||
private _nextScene(){
|
||||
// next scene
|
||||
this.scene.stop(SelectCompanionSceneName);
|
||||
gameManager.tryResumingGame(this, EnableCameraSceneName);
|
||||
this.scene.remove(SelectCompanionSceneName);
|
||||
}
|
||||
|
||||
private createCurrentCompanion(): void {
|
||||
for (let i = 0; i < this.companionModels.length; i++) {
|
||||
const companionResource = this.companionModels[i]
|
||||
const [middleX, middleY] = this.getCompanionPosition();
|
||||
const companion = this.physics.add.sprite(middleX, middleY, companionResource.name, 0);
|
||||
this.setUpCompanion(companion, i);
|
||||
this.anims.create({
|
||||
key: companionResource.name,
|
||||
frames: this.anims.generateFrameNumbers(companionResource.name, {start: 0, end: 2,}),
|
||||
frameRate: 10,
|
||||
repeat: -1
|
||||
});
|
||||
|
||||
companion.setInteractive().on("pointerdown", () => {
|
||||
this.currentCompanion = i;
|
||||
this.updateSelectedCompanion();
|
||||
});
|
||||
|
||||
this.companions.push(companion);
|
||||
}
|
||||
this.selectedCompanion = this.companions[this.currentCompanion];
|
||||
}
|
||||
|
||||
public onResize(ev: UIEvent): void {
|
||||
this.moveCompanion();
|
||||
|
||||
const middleX = this.getMiddleX();
|
||||
this.tweens.add({
|
||||
targets: this.selectCompanionSceneElement,
|
||||
x: middleX,
|
||||
duration: 1000,
|
||||
ease: 'Power3'
|
||||
});
|
||||
}
|
||||
|
||||
private updateSelectedCompanion(): void {
|
||||
this.selectedCompanion?.anims.pause();
|
||||
const companion = this.companions[this.currentCompanion];
|
||||
companion.play(this.companionModels[this.currentCompanion].name);
|
||||
this.selectedCompanion = companion;
|
||||
}
|
||||
|
||||
private moveCompanion(){
|
||||
for(let i = 0; i < this.companions.length; i++){
|
||||
const companion = this.companions[i];
|
||||
this.setUpCompanion(companion, i);
|
||||
}
|
||||
this.updateSelectedCompanion();
|
||||
}
|
||||
|
||||
private moveToLeft(){
|
||||
if(this.currentCompanion === 0){
|
||||
return;
|
||||
}
|
||||
this.currentCompanion -= 1;
|
||||
this.moveCompanion();
|
||||
}
|
||||
|
||||
private moveToRight(){
|
||||
if(this.currentCompanion === (this.companions.length - 1)){
|
||||
return;
|
||||
}
|
||||
this.currentCompanion += 1;
|
||||
this.moveCompanion();
|
||||
}
|
||||
|
||||
private defineSetupCompanion(numero: number){
|
||||
const deltaX = 30;
|
||||
const deltaY = 2;
|
||||
let [companionX, companionY] = this.getCompanionPosition();
|
||||
let companionVisible = true;
|
||||
let companionScale = 1.5;
|
||||
let companionOpactity = 1;
|
||||
if( this.currentCompanion !== numero ){
|
||||
companionVisible = false;
|
||||
}
|
||||
if( numero === (this.currentCompanion + 1) ){
|
||||
companionY -= deltaY;
|
||||
companionX += deltaX;
|
||||
companionScale = 0.8;
|
||||
companionOpactity = 0.6;
|
||||
companionVisible = true;
|
||||
}
|
||||
if( numero === (this.currentCompanion + 2) ){
|
||||
companionY -= deltaY;
|
||||
companionX += (deltaX * 2);
|
||||
companionScale = 0.8;
|
||||
companionOpactity = 0.6;
|
||||
companionVisible = true;
|
||||
}
|
||||
if( numero === (this.currentCompanion - 1) ){
|
||||
companionY -= deltaY;
|
||||
companionX -= deltaX;
|
||||
companionScale = 0.8;
|
||||
companionOpactity = 0.6;
|
||||
companionVisible = true;
|
||||
}
|
||||
if( numero === (this.currentCompanion - 2) ){
|
||||
companionY -= deltaY;
|
||||
companionX -= (deltaX * 2);
|
||||
companionScale = 0.8;
|
||||
companionOpactity = 0.6;
|
||||
companionVisible = true;
|
||||
}
|
||||
return {companionX, companionY, companionScale, companionOpactity, companionVisible}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns pixel position by on column and row number
|
||||
*/
|
||||
private getCompanionPosition(): [number, number] {
|
||||
return [
|
||||
this.game.renderer.width / 2,
|
||||
this.game.renderer.height / 3
|
||||
];
|
||||
}
|
||||
|
||||
private setUpCompanion(companion: Phaser.Physics.Arcade.Sprite, numero: number){
|
||||
|
||||
const {companionX, companionY, companionScale, companionOpactity, companionVisible} = this.defineSetupCompanion(numero);
|
||||
companion.setBounce(0.2);
|
||||
companion.setCollideWorldBounds(true);
|
||||
companion.setVisible( companionVisible );
|
||||
companion.setScale(companionScale, companionScale);
|
||||
companion.setAlpha(companionOpactity);
|
||||
companion.setX(companionX);
|
||||
companion.setY(companionY);
|
||||
}
|
||||
|
||||
private getMiddleX() : number{
|
||||
return (this.game.renderer.width / 2) -
|
||||
(
|
||||
this.selectCompanionSceneElement
|
||||
&& this.selectCompanionSceneElement.node
|
||||
&& this.selectCompanionSceneElement.node.getBoundingClientRect().width > 0
|
||||
? (this.selectCompanionSceneElement.node.getBoundingClientRect().width / 4)
|
||||
: 150
|
||||
);
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ export interface ITiledMap {
|
||||
* Map orientation (orthogonal)
|
||||
*/
|
||||
orientation: string;
|
||||
properties: ITiledMapLayerProperty[];
|
||||
properties?: ITiledMapLayerProperty[];
|
||||
|
||||
/**
|
||||
* Render order (right-down)
|
||||
@ -24,6 +24,11 @@ export interface ITiledMap {
|
||||
tilewidth: number;
|
||||
tilesets: ITiledTileSet[];
|
||||
version: number;
|
||||
compressionlevel?: number;
|
||||
infinite?: boolean;
|
||||
nextlayerid?: number;
|
||||
tiledversion?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
export interface ITiledMapLayerProperty {
|
||||
@ -38,19 +43,35 @@ export interface ITiledMapLayerProperty {
|
||||
value: boolean
|
||||
}*/
|
||||
|
||||
export interface ITiledMapLayer {
|
||||
export type ITiledMapLayer = ITiledMapGroupLayer | ITiledMapObjectLayer | ITiledMapTileLayer;
|
||||
|
||||
export interface ITiledMapGroupLayer {
|
||||
id?: number,
|
||||
name: string;
|
||||
opacity: number;
|
||||
properties?: ITiledMapLayerProperty[];
|
||||
|
||||
type: "group";
|
||||
visible: boolean;
|
||||
x: number;
|
||||
y: number;
|
||||
/**
|
||||
* Layers for group layer
|
||||
*/
|
||||
layers: ITiledMapLayer[];
|
||||
}
|
||||
|
||||
export interface ITiledMapTileLayer {
|
||||
id?: number,
|
||||
data: number[]|string;
|
||||
height: number;
|
||||
name: string;
|
||||
opacity: number;
|
||||
properties: ITiledMapLayerProperty[];
|
||||
encoding: string;
|
||||
properties?: ITiledMapLayerProperty[];
|
||||
encoding?: string;
|
||||
compression?: string;
|
||||
|
||||
/**
|
||||
* Type of layer (tilelayer, objectgroup)
|
||||
*/
|
||||
type: string;
|
||||
type: "tilelayer";
|
||||
visible: boolean;
|
||||
width: number;
|
||||
x: number;
|
||||
@ -59,7 +80,28 @@ export interface ITiledMapLayer {
|
||||
/**
|
||||
* Draw order (topdown (default), index)
|
||||
*/
|
||||
draworder: string;
|
||||
draworder?: string;
|
||||
}
|
||||
|
||||
export interface ITiledMapObjectLayer {
|
||||
id?: number,
|
||||
height: number;
|
||||
name: string;
|
||||
opacity: number;
|
||||
properties?: ITiledMapLayerProperty[];
|
||||
encoding?: string;
|
||||
compression?: string;
|
||||
|
||||
type: "objectgroup";
|
||||
visible: boolean;
|
||||
width: number;
|
||||
x: number;
|
||||
y: number;
|
||||
|
||||
/**
|
||||
* Draw order (topdown (default), index)
|
||||
*/
|
||||
draworder?: string;
|
||||
objects: ITiledMapObject[];
|
||||
}
|
||||
|
||||
@ -94,6 +136,20 @@ export interface ITiledMapObject {
|
||||
* Polyline points
|
||||
*/
|
||||
polyline: {x: number, y: number}[];
|
||||
|
||||
text?: ITiledText
|
||||
}
|
||||
|
||||
export interface ITiledText {
|
||||
text: string,
|
||||
wrap?: boolean,
|
||||
fontfamily?: string,
|
||||
pixelsize?: number,
|
||||
color?: string,
|
||||
underline?: boolean,
|
||||
italic?: boolean,
|
||||
strikeout?: boolean,
|
||||
halign?: "center"|"right"|"justify"|"left"
|
||||
}
|
||||
|
||||
export interface ITiledTileSet {
|
||||
|
44
front/src/Phaser/Map/LayersIterator.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import {ITiledMap, ITiledMapLayer} from "./ITiledMap";
|
||||
|
||||
/**
|
||||
* Iterates over the layers of a map, flattening the grouped layers
|
||||
*/
|
||||
export class LayersIterator implements IterableIterator<ITiledMapLayer> {
|
||||
|
||||
private layers: ITiledMapLayer[] = [];
|
||||
private pointer: number = 0;
|
||||
|
||||
constructor(private map: ITiledMap) {
|
||||
this.initLayersList(map.layers, '');
|
||||
}
|
||||
|
||||
private initLayersList(layers : ITiledMapLayer[], prefix : string) {
|
||||
for (const layer of layers) {
|
||||
if (layer.type === 'group') {
|
||||
this.initLayersList(layer.layers, prefix + layer.name + '/');
|
||||
} else {
|
||||
const layerWithNewName = { ...layer };
|
||||
layerWithNewName.name = prefix+layerWithNewName.name;
|
||||
this.layers.push(layerWithNewName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public next(): IteratorResult<ITiledMapLayer> {
|
||||
if (this.pointer < this.layers.length) {
|
||||
return {
|
||||
done: false,
|
||||
value: this.layers[this.pointer++]
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
done: true,
|
||||
value: null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Symbol.iterator](): IterableIterator<ITiledMapLayer> {
|
||||
return new LayersIterator(this.map);
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ export class HelpCameraSettingsScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
private createHelpCameraSettings() : void {
|
||||
const middleX = (window.innerWidth / 3) - (370*0.85);
|
||||
const middleX = this.getMiddleX();
|
||||
this.helpCameraSettingsElement = this.add.dom(middleX, -800, undefined, {overflow: 'scroll'}).createFromCache(helpCameraSettings);
|
||||
this.revealMenusAfterInit(this.helpCameraSettingsElement, helpCameraSettings);
|
||||
this.helpCameraSettingsElement.addListener('click');
|
||||
@ -46,19 +46,13 @@ export class HelpCameraSettingsScene extends Phaser.Scene {
|
||||
private openHelpCameraSettingsOpened(): void{
|
||||
HtmlUtils.getElementByIdOrFail<HTMLDivElement>('webRtcSetup').style.display = 'none';
|
||||
this.helpCameraSettingsOpened = true;
|
||||
let middleY = (window.innerHeight / 3) - (495);
|
||||
if(middleY < 0){
|
||||
middleY = 0;
|
||||
}
|
||||
let middleX = (window.innerWidth / 3) - (370*0.85);
|
||||
if(middleX < 0){
|
||||
middleX = 0;
|
||||
}
|
||||
if(window.navigator.userAgent.includes('Firefox')){
|
||||
HtmlUtils.getElementByIdOrFail<HTMLParagraphElement>('browserHelpSetting').innerHTML ='<img src="/resources/objects/help-setting-camera-permission-firefox.png"/>';
|
||||
}else if(window.navigator.userAgent.includes('Chrome')){
|
||||
HtmlUtils.getElementByIdOrFail<HTMLParagraphElement>('browserHelpSetting').innerHTML ='<img src="/resources/objects/help-setting-camera-permission-chrome.png"/>';
|
||||
}
|
||||
const middleY = this.getMiddleY();
|
||||
const middleX = this.getMiddleX();
|
||||
this.tweens.add({
|
||||
targets: this.helpCameraSettingsElement,
|
||||
y: middleY,
|
||||
@ -70,6 +64,7 @@ export class HelpCameraSettingsScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
private closeHelpCameraSettingsOpened(): void{
|
||||
const middleX = this.getMiddleX();
|
||||
const helpCameraSettingsInfo = this.helpCameraSettingsElement.getChildByID('helpCameraSettings') as HTMLParagraphElement;
|
||||
helpCameraSettingsInfo.innerText = '';
|
||||
helpCameraSettingsInfo.style.display = 'none';
|
||||
@ -77,6 +72,7 @@ export class HelpCameraSettingsScene extends Phaser.Scene {
|
||||
this.tweens.add({
|
||||
targets: this.helpCameraSettingsElement,
|
||||
y: -400,
|
||||
x: middleX,
|
||||
duration: 1000,
|
||||
ease: 'Power3',
|
||||
overflow: 'scroll'
|
||||
@ -91,5 +87,49 @@ export class HelpCameraSettingsScene extends Phaser.Scene {
|
||||
}, 250);
|
||||
}
|
||||
|
||||
update(time: number, delta: number): void {
|
||||
const middleX = this.getMiddleX();
|
||||
const middleY = this.getMiddleY();
|
||||
this.tweens.add({
|
||||
targets: this.helpCameraSettingsElement,
|
||||
x: middleX,
|
||||
y: middleY,
|
||||
duration: 1000,
|
||||
ease: 'Power3'
|
||||
});
|
||||
}
|
||||
|
||||
public onResize(ev: UIEvent): void {
|
||||
const middleX = this.getMiddleX();
|
||||
const middleY = this.getMiddleY();
|
||||
this.tweens.add({
|
||||
targets: this.helpCameraSettingsElement,
|
||||
x: middleX,
|
||||
y: middleY,
|
||||
duration: 1000,
|
||||
ease: 'Power3'
|
||||
});
|
||||
}
|
||||
|
||||
private getMiddleX() : number{
|
||||
return (this.game.renderer.width / 2) -
|
||||
(
|
||||
this.helpCameraSettingsElement
|
||||
&& this.helpCameraSettingsElement.node
|
||||
&& this.helpCameraSettingsElement.node.getBoundingClientRect().width > 0
|
||||
? (this.helpCameraSettingsElement.node.getBoundingClientRect().width / 4)
|
||||
: (400 / 2)
|
||||
);
|
||||
}
|
||||
|
||||
private getMiddleY() : number{
|
||||
const middleY = ((window.innerHeight) - (
|
||||
(this.helpCameraSettingsElement
|
||||
&& this.helpCameraSettingsElement.node
|
||||
&& this.helpCameraSettingsElement.node.getBoundingClientRect().height > 0
|
||||
? this.helpCameraSettingsElement.node.getBoundingClientRect().height : 400 /*FIXME to use a const will be injected in HTMLElement*/)*2)) / 2;
|
||||
return (middleY > 0 ? middleY / 2 : 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import {LoginScene, LoginSceneName} from "../Login/LoginScene";
|
||||
import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCharacterScene";
|
||||
import {SelectCompanionScene, SelectCompanionSceneName} from "../Login/SelectCompanionScene";
|
||||
import {gameManager} from "../Game/GameManager";
|
||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||
import {mediaManager} from "../../WebRtc/MediaManager";
|
||||
@ -15,7 +16,7 @@ const gameMenuIconKey = 'gameMenuIcon';
|
||||
const gameSettingsMenuKey = 'gameSettingsMenu';
|
||||
const gameShare = 'gameShare';
|
||||
|
||||
const closedSideMenuX = -200;
|
||||
const closedSideMenuX = -1000;
|
||||
const openedSideMenuX = 0;
|
||||
|
||||
/**
|
||||
@ -90,7 +91,7 @@ export class MenuScene extends Phaser.Scene {
|
||||
|
||||
this.menuElement.addListener('click');
|
||||
this.menuElement.on('click', this.onMenuClick.bind(this));
|
||||
|
||||
|
||||
worldFullWarningStream.stream.subscribe(() => this.showWorldCapacityWarning());
|
||||
}
|
||||
|
||||
@ -117,7 +118,8 @@ export class MenuScene extends Phaser.Scene {
|
||||
this.closeAll();
|
||||
this.sideMenuOpened = true;
|
||||
this.menuButton.getChildByID('openMenuButton').innerHTML = 'X';
|
||||
if (gameManager.getCurrentGameScene(this).connection && gameManager.getCurrentGameScene(this).connection.isAdmin()) {
|
||||
const connection = gameManager.getCurrentGameScene(this).connection;
|
||||
if (connection && connection.isAdmin()) {
|
||||
const adminSection = this.menuElement.getChildByID('adminConsoleSection') as HTMLElement;
|
||||
adminSection.hidden = false;
|
||||
}
|
||||
@ -133,7 +135,7 @@ export class MenuScene extends Phaser.Scene {
|
||||
ease: 'Power3'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private showWorldCapacityWarning() {
|
||||
if (!this.warningContainer) {
|
||||
this.warningContainer = new WarningContainer(this);
|
||||
@ -146,7 +148,7 @@ export class MenuScene extends Phaser.Scene {
|
||||
this.warningContainer = null
|
||||
this.warningContainerTimeout = null
|
||||
}, 120000);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private closeSideMenu(): void {
|
||||
@ -282,6 +284,10 @@ export class MenuScene extends Phaser.Scene {
|
||||
this.closeSideMenu();
|
||||
gameManager.leaveGame(this, SelectCharacterSceneName, new SelectCharacterScene());
|
||||
break;
|
||||
case 'changeCompanionButton':
|
||||
this.closeSideMenu();
|
||||
gameManager.leaveGame(this, SelectCompanionSceneName, new SelectCompanionScene());
|
||||
break;
|
||||
case 'closeButton':
|
||||
this.closeSideMenu();
|
||||
break;
|
||||
@ -291,6 +297,9 @@ export class MenuScene extends Phaser.Scene {
|
||||
case 'editGameSettingsButton':
|
||||
this.openGameSettingsMenu();
|
||||
break;
|
||||
case 'toggleFullscreen':
|
||||
this.toggleFullscreen();
|
||||
break;
|
||||
case 'adminConsoleButton':
|
||||
gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.activeMessageConsole();
|
||||
break;
|
||||
@ -330,4 +339,15 @@ export class MenuScene extends Phaser.Scene {
|
||||
this.closeGameShare();
|
||||
this.gameReportElement.close();
|
||||
}
|
||||
|
||||
private toggleFullscreen() {
|
||||
const body = document.querySelector('body')
|
||||
if (body) {
|
||||
if (document.fullscreenElement ?? document.fullscreen) {
|
||||
document.exitFullscreen()
|
||||
} else {
|
||||
body.requestFullscreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,11 +7,11 @@ export const gameReportRessource = 'resources/html/gameReport.html';
|
||||
|
||||
export class ReportMenu extends Phaser.GameObjects.DOMElement {
|
||||
private opened: boolean = false;
|
||||
|
||||
|
||||
private userId!: number;
|
||||
private userName!: string|undefined;
|
||||
private anonymous: boolean;
|
||||
|
||||
|
||||
constructor(scene: Phaser.Scene, anonymous: boolean) {
|
||||
super(scene, -2000, -2000);
|
||||
this.anonymous = anonymous;
|
||||
@ -23,7 +23,7 @@ export class ReportMenu extends Phaser.GameObjects.DOMElement {
|
||||
const textToHide = this.getChildByID('askActionP') as HTMLElement;
|
||||
textToHide.hidden = true;
|
||||
}
|
||||
|
||||
|
||||
scene.add.existing(this);
|
||||
MenuScene.revealMenusAfterInit(this, gameReportKey);
|
||||
|
||||
@ -45,10 +45,10 @@ export class ReportMenu extends Phaser.GameObjects.DOMElement {
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.userId = userId;
|
||||
this.userName = userName;
|
||||
|
||||
|
||||
const mainEl = this.getChildByID('gameReport') as HTMLElement;
|
||||
this.x = this.getCenteredX(mainEl);
|
||||
this.y = this.getHiddenY(mainEl);
|
||||
@ -82,7 +82,7 @@ export class ReportMenu extends Phaser.GameObjects.DOMElement {
|
||||
ease: 'Power3'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//todo: into a parent class?
|
||||
private getCenteredX(mainEl: HTMLElement): number {
|
||||
return window.innerWidth / 4 - mainEl.clientWidth / 2;
|
||||
@ -93,7 +93,7 @@ export class ReportMenu extends Phaser.GameObjects.DOMElement {
|
||||
private getCenteredY(mainEl: HTMLElement): number {
|
||||
return window.innerHeight / 4 - mainEl.clientHeight / 2;
|
||||
}
|
||||
|
||||
|
||||
private toggleBlock(): void {
|
||||
!blackListManager.isBlackListed(this.userId) ? blackListManager.blackList(this.userId) : blackListManager.cancelBlackList(this.userId);
|
||||
this.close();
|
||||
@ -109,10 +109,10 @@ export class ReportMenu extends Phaser.GameObjects.DOMElement {
|
||||
gamePError.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
gameManager.getCurrentGameScene(this.scene).connection.emitReportPlayerMessage(
|
||||
gameManager.getCurrentGameScene(this.scene).connection?.emitReportPlayerMessage(
|
||||
this.userId,
|
||||
gameTextArea.value
|
||||
);
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ import {GameScene} from "../Game/GameScene";
|
||||
import {UserInputEvent, UserInputManager} from "../UserInput/UserInputManager";
|
||||
import {Character} from "../Entity/Character";
|
||||
|
||||
|
||||
export const hasMovedEventName = "hasMoved";
|
||||
export interface CurrentGamerInterface extends Character{
|
||||
moveUser(delta: number) : void;
|
||||
@ -22,12 +21,18 @@ export class Player extends Character implements CurrentGamerInterface {
|
||||
texturesPromise: Promise<string[]>,
|
||||
direction: PlayerAnimationDirections,
|
||||
moving: boolean,
|
||||
private userInputManager: UserInputManager
|
||||
private userInputManager: UserInputManager,
|
||||
companion: string|null,
|
||||
companionTexturePromise?: Promise<string>
|
||||
) {
|
||||
super(Scene, x, y, texturesPromise, name, direction, moving, 1);
|
||||
|
||||
//the current player model should be push away by other players to prevent conflict
|
||||
this.getBody().setImmovable(false);
|
||||
|
||||
if (typeof companion === 'string') {
|
||||
this.addCompanion(companion, companionTexturePromise);
|
||||
}
|
||||
}
|
||||
|
||||
moveUser(delta: number): void {
|
||||
@ -42,7 +47,7 @@ export class Player extends Character implements CurrentGamerInterface {
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
if (activeEvents.get(UserInputEvent.MoveUp)) {
|
||||
y = - moveAmount;
|
||||
y = -moveAmount;
|
||||
direction = PlayerAnimationDirections.Up;
|
||||
moving = true;
|
||||
} else if (activeEvents.get(UserInputEvent.MoveDown)) {
|
||||
@ -59,15 +64,18 @@ export class Player extends Character implements CurrentGamerInterface {
|
||||
direction = PlayerAnimationDirections.Right;
|
||||
moving = true;
|
||||
}
|
||||
moving = moving || activeEvents.get(UserInputEvent.JoystickMove);
|
||||
|
||||
if (x !== 0 || y !== 0) {
|
||||
this.move(x, y);
|
||||
this.emit(hasMovedEventName, {moving, direction, x: this.x, y: this.y});
|
||||
} else {
|
||||
if (this.wasMoving) {
|
||||
//direction = PlayerAnimationNames.None;
|
||||
this.stop();
|
||||
this.emit(hasMovedEventName, {moving, direction: this.previousDirection, x: this.x, y: this.y});
|
||||
}
|
||||
} else if (this.wasMoving && moving) {
|
||||
// slow joystick movement
|
||||
this.move(0, 0);
|
||||
this.emit(hasMovedEventName, {moving, direction: this.previousDirection, x: this.x, y: this.y});
|
||||
} else if (this.wasMoving && !moving) {
|
||||
this.stop();
|
||||
this.emit(hasMovedEventName, {moving, direction: this.previousDirection, x: this.x, y: this.y});
|
||||
}
|
||||
|
||||
if (direction !== null) {
|
||||
|
22
front/src/Phaser/UserInput/PinchManager.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import {Pinch} from "phaser3-rex-plugins/plugins/gestures.js";
|
||||
|
||||
export class PinchManager {
|
||||
private scene: Phaser.Scene;
|
||||
private pinch!: any; // eslint-disable-line
|
||||
|
||||
constructor(scene: Phaser.Scene) {
|
||||
this.scene = scene;
|
||||
this.pinch = new Pinch(scene);
|
||||
|
||||
this.pinch.on('pinch', (pinch:any) => { // eslint-disable-line
|
||||
let newZoom = this.scene.cameras.main.zoom * pinch.scaleFactor;
|
||||
if (newZoom < 0.25) {
|
||||
newZoom = 0.25;
|
||||
} else if(newZoom > 2) {
|
||||
newZoom = 2;
|
||||
}
|
||||
this.scene.cameras.main.setZoom(newZoom);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
import { Direction } from "../../types";
|
||||
import {GameScene} from "../Game/GameScene";
|
||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||
import {MobileJoystick} from "../Components/MobileJoystick";
|
||||
|
||||
interface UserInputManagerDatum {
|
||||
keyInstance: Phaser.Input.Keyboard.Key;
|
||||
@ -13,17 +16,25 @@ export enum UserInputEvent {
|
||||
SpeedUp,
|
||||
Interact,
|
||||
Shout,
|
||||
JoystickMove,
|
||||
}
|
||||
|
||||
//we cannot the map structure so we have to create a replacment
|
||||
|
||||
//we cannot use a map structure so we have to create a replacment
|
||||
export class ActiveEventList {
|
||||
private KeysCode : Map<UserInputEvent, boolean> = new Map<UserInputEvent, boolean>();
|
||||
private eventMap : Map<UserInputEvent, boolean> = new Map<UserInputEvent, boolean>();
|
||||
|
||||
get(event: UserInputEvent): boolean {
|
||||
return this.KeysCode.get(event) || false;
|
||||
return this.eventMap.get(event) || false;
|
||||
}
|
||||
set(event: UserInputEvent, value: boolean): void {
|
||||
this.KeysCode.set(event, value);
|
||||
this.eventMap.set(event, value);
|
||||
}
|
||||
forEach(callback: (value: boolean, key: UserInputEvent) => void): void {
|
||||
this.eventMap.forEach(callback);
|
||||
}
|
||||
any(): boolean {
|
||||
return Array.from(this.eventMap.values()).reduce((accu, curr) => accu || curr, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,10 +43,46 @@ export class UserInputManager {
|
||||
private KeysCode!: UserInputManagerDatum[];
|
||||
private Scene: GameScene;
|
||||
private isInputDisabled : boolean;
|
||||
constructor(Scene : GameScene) {
|
||||
|
||||
private joystick!: MobileJoystick;
|
||||
private joystickEvents = new ActiveEventList();
|
||||
private joystickForceThreshold = 60;
|
||||
private joystickForceAccuX = 0;
|
||||
private joystickForceAccuY = 0;
|
||||
|
||||
constructor(Scene: GameScene) {
|
||||
this.Scene = Scene;
|
||||
this.initKeyBoardEvent();
|
||||
this.isInputDisabled = false;
|
||||
this.initKeyBoardEvent();
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
this.initVirtualJoystick();
|
||||
}
|
||||
}
|
||||
|
||||
initVirtualJoystick() {
|
||||
this.joystick = new MobileJoystick(this.Scene);
|
||||
this.joystick.on("update", () => {
|
||||
this.joystickForceAccuX = this.joystick.forceX ? this.joystickForceAccuX : 0;
|
||||
this.joystickForceAccuY = this.joystick.forceY ? this.joystickForceAccuY : 0;
|
||||
const cursorKeys = this.joystick.createCursorKeys();
|
||||
for (const name in cursorKeys) {
|
||||
const key = cursorKeys[name as Direction];
|
||||
switch (name) {
|
||||
case "up":
|
||||
this.joystickEvents.set(UserInputEvent.MoveUp, key.isDown);
|
||||
break;
|
||||
case "left":
|
||||
this.joystickEvents.set(UserInputEvent.MoveLeft, key.isDown);
|
||||
break;
|
||||
case "down":
|
||||
this.joystickEvents.set(UserInputEvent.MoveDown, key.isDown);
|
||||
break;
|
||||
case "right":
|
||||
this.joystickEvents.set(UserInputEvent.MoveRight, key.isDown);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initKeyBoardEvent(){
|
||||
@ -64,6 +111,7 @@ export class UserInputManager {
|
||||
this.Scene.input.keyboard.removeAllListeners();
|
||||
}
|
||||
|
||||
//todo: should we also disable the joystick?
|
||||
disableControls(){
|
||||
this.Scene.input.keyboard.removeAllKeys();
|
||||
this.isInputDisabled = true;
|
||||
@ -78,11 +126,33 @@ export class UserInputManager {
|
||||
if (this.isInputDisabled) {
|
||||
return eventsMap;
|
||||
}
|
||||
this.joystickEvents.forEach((value, key) => {
|
||||
if (value) {
|
||||
switch (key) {
|
||||
case UserInputEvent.MoveUp:
|
||||
case UserInputEvent.MoveDown:
|
||||
this.joystickForceAccuY += this.joystick.forceY;
|
||||
if (Math.abs(this.joystickForceAccuY) > this.joystickForceThreshold) {
|
||||
eventsMap.set(key, value);
|
||||
this.joystickForceAccuY = 0;
|
||||
}
|
||||
break;
|
||||
case UserInputEvent.MoveLeft:
|
||||
case UserInputEvent.MoveRight:
|
||||
this.joystickForceAccuX += this.joystick.forceX;
|
||||
if (Math.abs(this.joystickForceAccuX) > this.joystickForceThreshold) {
|
||||
eventsMap.set(key, value);
|
||||
this.joystickForceAccuX = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
eventsMap.set(UserInputEvent.JoystickMove, this.joystickEvents.any());
|
||||
this.KeysCode.forEach(d => {
|
||||
if (d. keyInstance.isDown) {
|
||||
if (d.keyInstance.isDown) {
|
||||
eventsMap.set(d.event, true);
|
||||
}
|
||||
|
||||
});
|
||||
return eventsMap;
|
||||
}
|
||||
|
16
front/src/Touch/TouchScreenManager.ts
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
class TouchScreenManager {
|
||||
|
||||
readonly supportTouchScreen:boolean;
|
||||
|
||||
constructor() {
|
||||
this.supportTouchScreen = this.detectTouchscreen();
|
||||
}
|
||||
|
||||
//found here: https://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript#4819886
|
||||
detectTouchscreen(): boolean {
|
||||
return (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0));
|
||||
}
|
||||
}
|
||||
|
||||
export const touchScreenManager = new TouchScreenManager();
|
@ -32,7 +32,7 @@ class CoWebsiteManager {
|
||||
private resizing: boolean = false;
|
||||
private cowebsiteMainDom: HTMLDivElement;
|
||||
private cowebsiteAsideDom: HTMLDivElement;
|
||||
|
||||
|
||||
get width(): number {
|
||||
return this.cowebsiteDiv.clientWidth;
|
||||
}
|
||||
@ -74,7 +74,7 @@ class CoWebsiteManager {
|
||||
|
||||
private initResizeListeners() {
|
||||
const movecallback = (event:MouseEvent) => {
|
||||
this.verticalMode ? this.height -= event.movementY / this.getDevicePixelRatio() : this.width -= event.movementX / this.getDevicePixelRatio();
|
||||
this.verticalMode ? this.height += event.movementY / this.getDevicePixelRatio() : this.width -= event.movementX / this.getDevicePixelRatio();
|
||||
this.fire();
|
||||
}
|
||||
|
||||
|
@ -340,14 +340,10 @@ class LayoutManager {
|
||||
const mainContainer = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('main-container');
|
||||
mainContainer.appendChild(div);
|
||||
|
||||
const callBackFunctionTrigger = (() => {
|
||||
console.log('user click on space => ', id);
|
||||
callBack();
|
||||
});
|
||||
|
||||
//add trigger action
|
||||
this.actionButtonTrigger.set(id, callBackFunctionTrigger);
|
||||
userInputManager.addSpaceEventListner(callBackFunctionTrigger);
|
||||
div.onpointerdown = () => callBack();
|
||||
this.actionButtonTrigger.set(id, callBack);
|
||||
userInputManager.addSpaceEventListner(callBack);
|
||||
}
|
||||
|
||||
public removeActionButton(id: string, userInputManager: UserInputManager){
|
||||
|
@ -14,6 +14,12 @@ let videoConstraint: boolean|MediaTrackConstraints = {
|
||||
resizeMode: 'crop-and-scale',
|
||||
aspectRatio: 1.777777778
|
||||
};
|
||||
const audioConstraint: boolean|MediaTrackConstraints = {
|
||||
//TODO: make these values configurable in the game settings menu and store them in localstorage
|
||||
autoGainControl: false,
|
||||
echoCancellation: true,
|
||||
noiseSuppression: false
|
||||
};
|
||||
|
||||
export type UpdatedLocalStreamCallback = (media: MediaStream|null) => void;
|
||||
export type StartScreenSharingCallback = (media: MediaStream) => void;
|
||||
@ -36,7 +42,7 @@ export class MediaManager {
|
||||
webrtcInAudio: HTMLAudioElement;
|
||||
private webrtcOutAudio: HTMLAudioElement;
|
||||
constraintsMedia : MediaStreamConstraints = {
|
||||
audio: true,
|
||||
audio: audioConstraint,
|
||||
video: videoConstraint
|
||||
};
|
||||
updatedLocalStreamCallBacks : Set<UpdatedLocalStreamCallback> = new Set<UpdatedLocalStreamCallback>();
|
||||
@ -234,7 +240,7 @@ export class MediaManager {
|
||||
}
|
||||
|
||||
public enableMicrophone() {
|
||||
this.constraintsMedia.audio = true;
|
||||
this.constraintsMedia.audio = audioConstraint;
|
||||
|
||||
this.getCamera().then((stream) => {
|
||||
//TODO show error message tooltip upper of camera button
|
||||
@ -309,12 +315,17 @@ export class MediaManager {
|
||||
}
|
||||
|
||||
private enableScreenSharing() {
|
||||
this.monitorClose.style.display = "none";
|
||||
this.monitor.style.display = "block";
|
||||
this.monitorBtn.classList.add("enabled");
|
||||
this.getScreenMedia().then((stream) => {
|
||||
this.triggerStartedScreenSharingCallbacks(stream);
|
||||
this.monitorClose.style.display = "none";
|
||||
this.monitor.style.display = "block";
|
||||
this.monitorBtn.classList.add("enabled");
|
||||
}, () => {
|
||||
this.monitorClose.style.display = "block";
|
||||
this.monitor.style.display = "none";
|
||||
this.monitorBtn.classList.remove("enabled");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private disableScreenSharing() {
|
||||
|
@ -2,10 +2,11 @@ import 'phaser';
|
||||
import GameConfig = Phaser.Types.Core.GameConfig;
|
||||
import "../dist/resources/style/index.scss";
|
||||
|
||||
import {DEBUG_MODE, JITSI_URL, RESOLUTION} from "./Enum/EnvironmentVariable";
|
||||
import {DEBUG_MODE, isMobile, RESOLUTION} from "./Enum/EnvironmentVariable";
|
||||
import {LoginScene} from "./Phaser/Login/LoginScene";
|
||||
import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene";
|
||||
import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene";
|
||||
import {SelectCompanionScene} from "./Phaser/Login/SelectCompanionScene";
|
||||
import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene";
|
||||
import {CustomizeScene} from "./Phaser/Login/CustomizeScene";
|
||||
import {ResizableScene} from "./Phaser/Login/ResizableScene";
|
||||
@ -16,7 +17,7 @@ import {HelpCameraSettingsScene} from "./Phaser/Menu/HelpCameraSettingsScene";
|
||||
import {localUserStore} from "./Connexion/LocalUserStore";
|
||||
import {ErrorScene} from "./Phaser/Reconnecting/ErrorScene";
|
||||
import {iframeListener} from "./Api/IframeListener";
|
||||
import {discussionManager} from "./WebRtc/DiscussionManager";
|
||||
import { SelectCharacterMobileScene } from './Phaser/Login/SelectCharacterMobileScene';
|
||||
|
||||
const {width, height} = coWebsiteManager.getGameSize();
|
||||
|
||||
@ -67,14 +68,22 @@ switch (phaserMode) {
|
||||
throw new Error('phaserMode parameter must be one of "auto", "canvas" or "webgl"');
|
||||
}
|
||||
|
||||
|
||||
const config: GameConfig = {
|
||||
type: mode,
|
||||
title: "WorkAdventure",
|
||||
width: width / RESOLUTION,
|
||||
height: height / RESOLUTION,
|
||||
parent: "game",
|
||||
scene: [EntryScene, LoginScene, SelectCharacterScene, EnableCameraScene, ReconnectingScene, ErrorScene, CustomizeScene, MenuScene, HelpCameraSettingsScene],
|
||||
scene: [EntryScene,
|
||||
LoginScene,
|
||||
isMobile() ? SelectCharacterMobileScene : SelectCharacterScene,
|
||||
SelectCompanionScene,
|
||||
EnableCameraScene,
|
||||
ReconnectingScene,
|
||||
ErrorScene,
|
||||
CustomizeScene,
|
||||
MenuScene,
|
||||
HelpCameraSettingsScene],
|
||||
zoom: RESOLUTION,
|
||||
fps: fps,
|
||||
dom: {
|
||||
|
12
front/src/rex-plugins.d.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
declare module 'phaser3-rex-plugins/plugins/virtualjoystick.js' {
|
||||
const content: any; // eslint-disable-line
|
||||
export default content;
|
||||
}
|
||||
declare module 'phaser3-rex-plugins/plugins/gestures-plugin.js' {
|
||||
const content: any; // eslint-disable-line
|
||||
export default content;
|
||||
}
|
||||
declare module 'phaser3-rex-plugins/plugins/gestures.js' {
|
||||
export const Pinch: any; // eslint-disable-line
|
||||
}
|
24
front/src/types.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import Phaser from "phaser";
|
||||
|
||||
export type CursorKey = {
|
||||
isDown: boolean
|
||||
}
|
||||
|
||||
export type Direction = 'left' | 'right' | 'up' | 'down'
|
||||
|
||||
export interface CursorKeys extends Record<Direction, CursorKey> {
|
||||
left: CursorKey;
|
||||
right: CursorKey;
|
||||
up: CursorKey;
|
||||
down: CursorKey;
|
||||
}
|
||||
|
||||
export interface IVirtualJoystick extends Phaser.GameObjects.GameObject {
|
||||
y: number;
|
||||
x: number;
|
||||
forceX: number;
|
||||
forceY: number;
|
||||
visible: boolean;
|
||||
createCursorKeys: () => CursorKeys;
|
||||
}
|
||||
|
45
front/tests/Phaser/Connexion/LocalUserTest.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import "jasmine";
|
||||
import {areCharacterLayersValid, isUserNameValid, maxUserNameLength} from "../../../src/Connexion/LocalUser";
|
||||
|
||||
describe("isUserNameValid()", () => {
|
||||
it("should validate name with letters", () => {
|
||||
expect(isUserNameValid('toto')).toBe(true);
|
||||
});
|
||||
|
||||
it("should not validate empty name", () => {
|
||||
expect(isUserNameValid('')).toBe(false);
|
||||
});
|
||||
it("should not validate string with too many letters", () => {
|
||||
let testString = '';
|
||||
for (let i = 0; i < maxUserNameLength + 2; i++) {
|
||||
testString += 'a';
|
||||
}
|
||||
expect(isUserNameValid(testString)).toBe(false);
|
||||
});
|
||||
it("should not validate spaces", () => {
|
||||
expect(isUserNameValid(' ')).toBe(false);
|
||||
});
|
||||
it("should not validate numbers", () => {
|
||||
expect(isUserNameValid('a12')).toBe(false);
|
||||
});
|
||||
it("should not validate special characters", () => {
|
||||
expect(isUserNameValid('a&-')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("areCharacterLayersValid()", () => {
|
||||
it("should validate default textures array", () => {
|
||||
expect(areCharacterLayersValid(['male1', 'male2'])).toBe(true);
|
||||
});
|
||||
|
||||
it("should not validate an empty array", () => {
|
||||
expect(areCharacterLayersValid([])).toBe(false);
|
||||
});
|
||||
it("should not validate space only strings", () => {
|
||||
expect(areCharacterLayersValid([' ', 'male1'])).toBe(false);
|
||||
});
|
||||
|
||||
it("should not validate empty strings", () => {
|
||||
expect(areCharacterLayersValid(['', 'male1'])).toBe(false);
|
||||
});
|
||||
});
|
147
front/tests/Phaser/Map/LayersIteratorTest.ts
Normal file
@ -0,0 +1,147 @@
|
||||
import "jasmine";
|
||||
import {Room} from "../../../src/Connexion/Room";
|
||||
import {LayersIterator} from "../../../src/Phaser/Map/LayersIterator";
|
||||
|
||||
describe("Layers iterator", () => {
|
||||
it("should iterate maps with no group", () => {
|
||||
const layersIterator = new LayersIterator({
|
||||
"compressionlevel":-1,
|
||||
"height":2,
|
||||
"infinite":false,
|
||||
"layers":[
|
||||
{
|
||||
"data":[0, 0, 0, 0],
|
||||
"height":2,
|
||||
"id":1,
|
||||
"name":"Tile Layer 1",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":2,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"data":[0, 0, 0, 0],
|
||||
"height":2,
|
||||
"id":1,
|
||||
"name":"Tile Layer 2",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":2,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
"nextlayerid":2,
|
||||
"nextobjectid":1,
|
||||
"orientation":"orthogonal",
|
||||
"renderorder":"right-down",
|
||||
"tiledversion":"2021.03.23",
|
||||
"tileheight":32,
|
||||
"tilesets":[],
|
||||
"tilewidth":32,
|
||||
"type":"map",
|
||||
"version":1.5,
|
||||
"width":2
|
||||
})
|
||||
|
||||
const layers = [];
|
||||
for (const layer of layersIterator) {
|
||||
layers.push(layer.name);
|
||||
}
|
||||
expect(layers).toEqual(['Tile Layer 1', 'Tile Layer 2']);
|
||||
});
|
||||
|
||||
it("should iterate maps with recursive groups", () => {
|
||||
const layersIterator = new LayersIterator({
|
||||
"compressionlevel":-1,
|
||||
"height":2,
|
||||
"infinite":false,
|
||||
"layers":[
|
||||
{
|
||||
"id":6,
|
||||
"layers":[
|
||||
{
|
||||
"id":5,
|
||||
"layers":[
|
||||
{
|
||||
"data":[0, 0, 0, 0],
|
||||
"height":2,
|
||||
"id":10,
|
||||
"name":"Tile3",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":2,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"data":[0, 0, 0, 0],
|
||||
"height":2,
|
||||
"id":9,
|
||||
"name":"Tile2",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":2,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
"name":"Group 3",
|
||||
"opacity":1,
|
||||
"type":"group",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"id":7,
|
||||
"layers":[
|
||||
{
|
||||
"data":[0, 0, 0, 0],
|
||||
"height":2,
|
||||
"id":8,
|
||||
"name":"Tile1",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":2,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
"name":"Group 2",
|
||||
"opacity":1,
|
||||
"type":"group",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
"name":"Group 1",
|
||||
"opacity":1,
|
||||
"type":"group",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
"nextlayerid":11,
|
||||
"nextobjectid":1,
|
||||
"orientation":"orthogonal",
|
||||
"renderorder":"right-down",
|
||||
"tiledversion":"2021.03.23",
|
||||
"tileheight":32,
|
||||
"tilesets":[],
|
||||
"tilewidth":32,
|
||||
"type":"map",
|
||||
"version":1.5,
|
||||
"width":2
|
||||
})
|
||||
|
||||
const layers = [];
|
||||
for (const layer of layersIterator) {
|
||||
layers.push(layer.name);
|
||||
}
|
||||
expect(layers).toEqual(['Group 1/Group 3/Tile3', 'Group 1/Group 3/Tile2', 'Group 1/Group 2/Tile1']);
|
||||
});
|
||||
});
|
@ -9,17 +9,18 @@
|
||||
"downlevelIteration": true,
|
||||
"jsx": "react",
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true,
|
||||
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
"strictNullChecks": true, /* Enable strict null checks. */
|
||||
"strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
"strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
"strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
"noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
"alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
"strictNullChecks": true, /* Enable strict null checks. */
|
||||
"strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
"strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
"strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
"noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
"alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */
|
||||
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */
|
||||
}
|
||||
}
|
||||
|
@ -1819,6 +1819,11 @@ eventemitter3@^2.0.3:
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba"
|
||||
integrity sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=
|
||||
|
||||
eventemitter3@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
|
||||
integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==
|
||||
|
||||
eventemitter3@^4.0.0, eventemitter3@^4.0.4:
|
||||
version "4.0.7"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
||||
@ -3064,6 +3069,11 @@ loglevel@^1.6.8:
|
||||
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0"
|
||||
integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==
|
||||
|
||||
lokijs@^1.5.11:
|
||||
version "1.5.11"
|
||||
resolved "https://registry.yarnpkg.com/lokijs/-/lokijs-1.5.11.tgz#2b2ea82ec66050e4b112c6cfc588dac22d362b13"
|
||||
integrity sha512-YYyuBPxMn/oS0tFznQDbIX5XL1ltMcwFqCboDr8voYE4VCDzR5vAsrvQDhlnua4lBeqMqHmLvUXRTmRUzUKH1Q==
|
||||
|
||||
lower-case@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7"
|
||||
@ -3625,6 +3635,11 @@ pako@~1.0.5:
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
||||
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
||||
|
||||
papaparse@^5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.3.0.tgz#ab1702feb96e79ab4309652f36db9536563ad05a"
|
||||
integrity sha512-Lb7jN/4bTpiuGPrYy4tkKoUS8sTki8zacB5ke1p5zolhcSE4TlWgrlsxjrDTbG/dFVh07ck7X36hUf/b5V68pg==
|
||||
|
||||
parallel-transform@^1.1.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc"
|
||||
@ -3752,6 +3767,16 @@ pbkdf2@^3.0.3:
|
||||
safe-buffer "^5.0.1"
|
||||
sha.js "^2.4.8"
|
||||
|
||||
phaser3-rex-plugins@^1.1.42:
|
||||
version "1.1.44"
|
||||
resolved "https://registry.yarnpkg.com/phaser3-rex-plugins/-/phaser3-rex-plugins-1.1.44.tgz#83588ab2801c5b3a80a18be4f0ae37f1f32096b0"
|
||||
integrity sha512-JPr3+UQv4+uv4etZr80aFhYxw2dk4UYG9/gQ4uMSSXQuc8gXQkXCr3J3zm8LJqH+1FXeK8nncpC7TKa4uKLgQw==
|
||||
dependencies:
|
||||
eventemitter3 "^3.1.2"
|
||||
lokijs "^1.5.11"
|
||||
papaparse "^5.3.0"
|
||||
webfontloader "^1.6.28"
|
||||
|
||||
phaser@3.24.1:
|
||||
version "3.24.1"
|
||||
resolved "https://registry.yarnpkg.com/phaser/-/phaser-3.24.1.tgz#376e0c965d2a35af37c06ee78627dafbde5be017"
|
||||
@ -5144,6 +5169,11 @@ wbuf@^1.1.0, wbuf@^1.7.3:
|
||||
dependencies:
|
||||
minimalistic-assert "^1.0.0"
|
||||
|
||||
webfontloader@^1.6.28:
|
||||
version "1.6.28"
|
||||
resolved "https://registry.yarnpkg.com/webfontloader/-/webfontloader-1.6.28.tgz#db786129253cb6e8eae54c2fb05f870af6675bae"
|
||||
integrity sha1-23hhKSU8tujq5UwvsF+HCvZnW64=
|
||||
|
||||
webpack-cli@^3.3.11:
|
||||
version "3.3.12"
|
||||
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a"
|
||||
|
@ -355,7 +355,7 @@
|
||||
"value":"scriptTuto.js"
|
||||
}],
|
||||
"renderorder":"right-down",
|
||||
"tiledversion":"2021.03.23",
|
||||
"tiledversion":"1.5.0",
|
||||
"tileheight":32,
|
||||
"tilesets":[
|
||||
{
|
||||
|
102
maps/tests/grouped_map.json
Normal file
@ -0,0 +1,102 @@
|
||||
{ "compressionlevel":-1,
|
||||
"height":10,
|
||||
"infinite":false,
|
||||
"layers":[
|
||||
{
|
||||
"id":7,
|
||||
"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
|
||||
}],
|
||||
"name":"Group 2",
|
||||
"opacity":1,
|
||||
"type":"group",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"id":6,
|
||||
"layers":[
|
||||
{
|
||||
"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, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 0, 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":5,
|
||||
"name":"jitsiConf",
|
||||
"opacity":1,
|
||||
"properties":[
|
||||
{
|
||||
"name":"jitsiRoom",
|
||||
"type":"string",
|
||||
"value":"myRoom"
|
||||
}],
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":10,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
"name":"Group 1",
|
||||
"opacity":1,
|
||||
"type":"group",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"draworder":"topdown",
|
||||
"id":3,
|
||||
"name":"floorLayer",
|
||||
"objects":[],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
"nextlayerid":8,
|
||||
"nextobjectid":1,
|
||||
"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
|
||||
}
|
179
maps/tests/text_object.json
Normal file
@ -0,0 +1,179 @@
|
||||
{ "compressionlevel":-1,
|
||||
"height":10,
|
||||
"infinite":false,
|
||||
"layers":[
|
||||
{
|
||||
"data":[1, 1, 1, 1, 1, 1, 23, 23, 23, 23, 1, 1, 1, 1, 1, 1, 23, 23, 23, 23, 1, 1, 1, 1, 1, 1, 23, 23, 23, 23, 1, 1, 1, 1, 1, 1, 23, 23, 23, 23, 1, 1, 1, 1, 1, 1, 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],
|
||||
"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":154.708861498919,
|
||||
"id":1,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"text":
|
||||
{
|
||||
"color":"#ffff00",
|
||||
"fontfamily":"Sans Serif",
|
||||
"text":"A yellow text that wraps automatically.\n\nIt is 16px high.",
|
||||
"wrap":true
|
||||
},
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":127.701300455634,
|
||||
"x":192.052147832817,
|
||||
"y":5.45414451778726
|
||||
},
|
||||
{
|
||||
"height":19,
|
||||
"id":3,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"text":
|
||||
{
|
||||
"text":"Default text",
|
||||
"wrap":true
|
||||
},
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":113.494863163736,
|
||||
"x":1.76065884397454,
|
||||
"y":8.44497342134471
|
||||
},
|
||||
{
|
||||
"height":19,
|
||||
"id":5,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"text":
|
||||
{
|
||||
"text":"A very long text with no world wrap so it keeps going"
|
||||
},
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":349.4375,
|
||||
"x":1.01295161808517,
|
||||
"y":168.828173374613
|
||||
},
|
||||
{
|
||||
"height":19,
|
||||
"id":6,
|
||||
"name":"",
|
||||
"rotation":45,
|
||||
"text":
|
||||
{
|
||||
"text":"A rotated text"
|
||||
},
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":117.607252906128,
|
||||
"x":62.3249441410129,
|
||||
"y":41.3440913604766
|
||||
},
|
||||
{
|
||||
"height":48.1605818096851,
|
||||
"id":7,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"text":
|
||||
{
|
||||
"fontfamily":"Sans Serif",
|
||||
"italic":true,
|
||||
"pixelsize":27,
|
||||
"strikeout":true,
|
||||
"text":"Italic 27px",
|
||||
"underline":true
|
||||
},
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":337.807030930545,
|
||||
"x":6.6207558122554,
|
||||
"y":209.952070798528
|
||||
},
|
||||
{
|
||||
"height":40,
|
||||
"id":9,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"text":
|
||||
{
|
||||
"fontfamily":"Sans Serif",
|
||||
"halign":"center",
|
||||
"pixelsize":15,
|
||||
"text":"This text should appear below the plant and be centered",
|
||||
"wrap":true
|
||||
},
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":317.4375,
|
||||
"x":0.78125,
|
||||
"y":269.5
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
"visible":true,
|
||||
"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, 78, 79, 80, 0, 0, 0, 0, 0, 0, 0, 89, 90, 91, 0, 0, 0, 0, 0, 0, 0, 100, 101, 102, 0, 0, 0, 0],
|
||||
"height":10,
|
||||
"id":8,
|
||||
"name":"over",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":10,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
"nextlayerid":9,
|
||||
"nextobjectid":10,
|
||||
"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
|
||||
}
|
@ -36,6 +36,10 @@ message CharacterLayerMessage {
|
||||
string name = 2;
|
||||
}
|
||||
|
||||
message CompanionMessage {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
/*********** CLIENT TO SERVER MESSAGES *************/
|
||||
|
||||
message PingMessage {
|
||||
@ -141,6 +145,7 @@ message UserJoinedMessage {
|
||||
string name = 2;
|
||||
repeated CharacterLayerMessage characterLayers = 3;
|
||||
PositionMessage position = 4;
|
||||
CompanionMessage companion = 5;
|
||||
}
|
||||
|
||||
message UserLeftMessage {
|
||||
@ -251,6 +256,7 @@ message JoinRoomMessage {
|
||||
string roomId = 5;
|
||||
repeated string tag = 6;
|
||||
string IPAddress = 7;
|
||||
CompanionMessage companion = 8;
|
||||
}
|
||||
|
||||
message UserJoinedZoneMessage {
|
||||
@ -259,6 +265,7 @@ message UserJoinedZoneMessage {
|
||||
repeated CharacterLayerMessage characterLayers = 3;
|
||||
PositionMessage position = 4;
|
||||
Zone fromZone = 5;
|
||||
CompanionMessage companion = 6;
|
||||
}
|
||||
|
||||
message UserLeftZoneMessage {
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
WebRtcSignalToServerMessage,
|
||||
PlayGlobalMessage,
|
||||
ReportPlayerMessage,
|
||||
QueryJitsiJwtMessage, SendUserMessage, ServerToClientMessage
|
||||
QueryJitsiJwtMessage, SendUserMessage, ServerToClientMessage, CompanionMessage
|
||||
} from "../Messages/generated/messages_pb";
|
||||
import {UserMovesMessage} from "../Messages/generated/messages_pb";
|
||||
import {TemplatedApp} from "uWebSockets.js"
|
||||
@ -138,6 +138,14 @@ export class IoSocketController {
|
||||
const left = Number(query.left);
|
||||
const right = Number(query.right);
|
||||
const name = query.name;
|
||||
|
||||
let companion: CompanionMessage|undefined = undefined;
|
||||
|
||||
if (typeof query.companion === 'string') {
|
||||
companion = new CompanionMessage();
|
||||
companion.setName(query.companion);
|
||||
}
|
||||
|
||||
if (typeof name !== 'string') {
|
||||
throw new Error('Expecting name');
|
||||
}
|
||||
@ -221,6 +229,7 @@ export class IoSocketController {
|
||||
IPAddress,
|
||||
roomId,
|
||||
name,
|
||||
companion,
|
||||
characterLayers: characterLayerObjs,
|
||||
messages: memberMessages,
|
||||
tags: memberTags,
|
||||
@ -350,6 +359,7 @@ export class IoSocketController {
|
||||
client.tags = ws.tags;
|
||||
client.textures = ws.textures;
|
||||
client.characterLayers = ws.characterLayers;
|
||||
client.companion = ws.companion;
|
||||
client.roomId = ws.roomId;
|
||||
client.listenedZones = new Set<Zone>();
|
||||
return client;
|
||||
|
@ -3,6 +3,7 @@ import {Identificable} from "./Identificable";
|
||||
import {ViewportInterface} from "_Model/Websocket/ViewportMessage";
|
||||
import {
|
||||
BatchMessage,
|
||||
CompanionMessage,
|
||||
PusherToBackMessage,
|
||||
ServerToClientMessage,
|
||||
SubMessage
|
||||
@ -29,6 +30,7 @@ export interface ExSocketInterface extends WebSocket, Identificable {
|
||||
characterLayers: CharacterLayer[];
|
||||
position: PointInterface;
|
||||
viewport: ViewportInterface;
|
||||
companion?: CompanionMessage;
|
||||
/**
|
||||
* Pushes an event that will be sent in the next batch of events
|
||||
*/
|
||||
|
@ -5,7 +5,8 @@ import {
|
||||
CharacterLayerMessage, GroupLeftZoneMessage, GroupUpdateMessage, GroupUpdateZoneMessage,
|
||||
PointMessage, PositionMessage, UserJoinedMessage,
|
||||
UserJoinedZoneMessage, UserLeftZoneMessage, UserMovedMessage,
|
||||
ZoneMessage
|
||||
ZoneMessage,
|
||||
CompanionMessage
|
||||
} from "../Messages/generated/messages_pb";
|
||||
import * as messages_pb from "../Messages/generated/messages_pb";
|
||||
import {ClientReadableStream} from "grpc";
|
||||
@ -30,7 +31,7 @@ export type MovesCallback = (thing: Movable, position: PositionInterface, listen
|
||||
export type LeavesCallback = (thing: Movable, listener: User) => void;*/
|
||||
|
||||
export class UserDescriptor {
|
||||
private constructor(public readonly userId: number, private name: string, private characterLayers: CharacterLayerMessage[], private position: PositionMessage) {
|
||||
private constructor(public readonly userId: number, private name: string, private characterLayers: CharacterLayerMessage[], private position: PositionMessage, private companion?: CompanionMessage) {
|
||||
if (!Number.isInteger(this.userId)) {
|
||||
throw new Error('UserDescriptor.userId is not an integer: '+this.userId);
|
||||
}
|
||||
@ -41,7 +42,7 @@ export class UserDescriptor {
|
||||
if (position === undefined) {
|
||||
throw new Error('Missing position');
|
||||
}
|
||||
return new UserDescriptor(message.getUserid(), message.getName(), message.getCharacterlayersList(), position);
|
||||
return new UserDescriptor(message.getUserid(), message.getName(), message.getCharacterlayersList(), position, message.getCompanion());
|
||||
}
|
||||
|
||||
public update(userMovedMessage: UserMovedMessage) {
|
||||
@ -59,6 +60,7 @@ export class UserDescriptor {
|
||||
userJoinedMessage.setName(this.name);
|
||||
userJoinedMessage.setCharacterlayersList(this.characterLayers);
|
||||
userJoinedMessage.setPosition(this.position);
|
||||
userJoinedMessage.setCompanion(this.companion)
|
||||
|
||||
return userJoinedMessage;
|
||||
}
|
||||
|
@ -153,6 +153,8 @@ export class SocketManager implements ZoneEventListener {
|
||||
joinRoomMessage.setName(client.name);
|
||||
joinRoomMessage.setPositionmessage(ProtobufUtils.toPositionMessage(client.position));
|
||||
joinRoomMessage.setTagList(client.tags);
|
||||
joinRoomMessage.setCompanion(client.companion);
|
||||
|
||||
for (const characterLayer of client.characterLayers) {
|
||||
const characterLayerMessage = new CharacterLayerMessage();
|
||||
characterLayerMessage.setName(characterLayer.name);
|
||||
@ -362,6 +364,10 @@ export class SocketManager implements ZoneEventListener {
|
||||
}
|
||||
|
||||
emitPlayGlobalMessage(client: ExSocketInterface, playglobalmessage: PlayGlobalMessage) {
|
||||
if (!client.tags.includes('admin')) {
|
||||
//In case of xss injection, we just kill the connection.
|
||||
throw 'Client is not an admin!';
|
||||
}
|
||||
const pusherToBackMessage = new PusherToBackMessage();
|
||||
pusherToBackMessage.setPlayglobalmessage(playglobalmessage);
|
||||
|
||||
|