diff --git a/front/src/Phaser/Entity/PlayableCaracter.ts b/front/src/Phaser/Entity/PlayableCaracter.ts index 826bfc6a..e1b774ef 100644 --- a/front/src/Phaser/Entity/PlayableCaracter.ts +++ b/front/src/Phaser/Entity/PlayableCaracter.ts @@ -3,25 +3,25 @@ import {SpeechBubble} from "./SpeechBubble"; import BitmapText = Phaser.GameObjects.BitmapText; export const PLAYER_RESOURCES: Array = [ - {name: "male1", img: "resources/characters/pipoya/Male 01-1.png", x: 32, y: 32}, - {name: "male2", img: "resources/characters/pipoya/Male 02-2.png", x: 64, y: 32}, - {name: "male3", img: "resources/characters/pipoya/Male 03-4.png", x: 96, y: 32}, - {name: "male4", img: "resources/characters/pipoya/Male 09-1.png", x: 128, y: 32}, + {name: "male1", img: "resources/characters/pipoya/Male 01-1.png" /*, x: 32, y: 32*/}, + {name: "male2", img: "resources/characters/pipoya/Male 02-2.png"/*, x: 64, y: 32*/}, + {name: "male3", img: "resources/characters/pipoya/Male 03-4.png"/*, x: 96, y: 32*/}, + {name: "male4", img: "resources/characters/pipoya/Male 09-1.png"/*, x: 128, y: 32*/}, - {name: "male5", img: "resources/characters/pipoya/Male 10-3.png", x: 32, y: 64}, - {name: "male6", img: "resources/characters/pipoya/Male 17-2.png", x: 64, y: 64}, - {name: "male7", img: "resources/characters/pipoya/Male 18-1.png", x: 96, y: 64}, - {name: "male8", img: "resources/characters/pipoya/Male 16-4.png", x: 128, y: 64}, + {name: "male5", img: "resources/characters/pipoya/Male 10-3.png"/*, x: 32, y: 64*/}, + {name: "male6", img: "resources/characters/pipoya/Male 17-2.png"/*, x: 64, y: 64*/}, + {name: "male7", img: "resources/characters/pipoya/Male 18-1.png"/*, x: 96, y: 64*/}, + {name: "male8", img: "resources/characters/pipoya/Male 16-4.png"/*, x: 128, y: 64*/}, - {name: "Female1", img: "resources/characters/pipoya/Female 01-1.png", x: 32, y: 96}, - {name: "Female2", img: "resources/characters/pipoya/Female 02-2.png", x: 64, y: 96}, - {name: "Female3", img: "resources/characters/pipoya/Female 03-4.png", x: 96, y: 96}, - {name: "Female4", img: "resources/characters/pipoya/Female 09-1.png", x: 128, y: 96}, + {name: "Female1", img: "resources/characters/pipoya/Female 01-1.png"/*, x: 32, y: 96*/}, + {name: "Female2", img: "resources/characters/pipoya/Female 02-2.png"/*, x: 64, y: 96*/}, + {name: "Female3", img: "resources/characters/pipoya/Female 03-4.png"/*, x: 96, y: 96*/}, + {name: "Female4", img: "resources/characters/pipoya/Female 09-1.png"/*, x: 128, y: 96*/}, - {name: "Female5", img: "resources/characters/pipoya/Female 10-3.png", x: 32, y: 128}, - {name: "Female6", img: "resources/characters/pipoya/Female 17-2.png", x: 64, y: 128}, - {name: "Female7", img: "resources/characters/pipoya/Female 18-1.png", x: 96, y: 128}, - {name: "Female8", img: "resources/characters/pipoya/Female 16-4.png", x: 128, y: 128} + {name: "Female5", img: "resources/characters/pipoya/Female 10-3.png"/*, x: 32, y: 128*/}, + {name: "Female6", img: "resources/characters/pipoya/Female 17-2.png"/*, x: 64, y: 128*/}, + {name: "Female7", img: "resources/characters/pipoya/Female 18-1.png"/*, x: 96, y: 128*/}, + {name: "Female8", img: "resources/characters/pipoya/Female 16-4.png"/*, x: 128, y: 128*/} ]; export class PlayableCaracter extends Phaser.Physics.Arcade.Sprite { diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts new file mode 100644 index 00000000..56dcb8a5 --- /dev/null +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -0,0 +1,203 @@ +import {gameManager} from "../Game/GameManager"; +import {TextField} from "../Components/TextField"; +import {ClickButton} from "../Components/ClickButton"; +import Image = Phaser.GameObjects.Image; +import Rectangle = Phaser.GameObjects.Rectangle; +import {PLAYER_RESOURCES} from "../Entity/PlayableCaracter"; + +//todo: put this constants in a dedicated file +export const SelectCharacterSceneName = "SelectCharacterScene"; +enum LoginTextures { + playButton = "play_button", + icon = "icon", + mainFont = "main_font" +} + +interface InitDataInterface { + name: string +} + +export class SelectCharacterScene extends Phaser.Scene { + private readonly nbCharactersPerRow = 4; + private textField: TextField; + private pressReturnField: TextField; + private logo: Image; + private loginName: string; + + 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; + private players: Array = new Array(); + + constructor() { + super({ + key: SelectCharacterSceneName + }); + } + + init({ name }: InitDataInterface) { + this.loginName = name; + } + + 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'); + //add player png + PLAYER_RESOURCES.forEach((playerResource: any) => { + this.load.spritesheet( + playerResource.name, + playerResource.img, + {frameWidth: 32, frameHeight: 32} + ); + }); + } + + create() { + this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Select your character'); + this.textField.setOrigin(0.5).setCenterAlign() + + this.pressReturnField = new TextField(this, this.game.renderer.width / 2, 230, 'Press enter to start'); + this.pressReturnField.setOrigin(0.5).setCenterAlign() + + let 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.login(this.loginName); + }); + + this.input.keyboard.on('keydown-RIGHT', () => { + if (this.selectedRectangleXPos < this.nbCharactersPerRow - 1) { + this.selectedRectangleXPos++; + } + this.updateSelectedPlayer(); + }); + this.input.keyboard.on('keydown-LEFT', () => { + if (this.selectedRectangleXPos > 0) { + this.selectedRectangleXPos--; + } + this.updateSelectedPlayer(); + }); + this.input.keyboard.on('keydown-DOWN', () => { + if (this.selectedRectangleYPos < Math.ceil(PLAYER_RESOURCES.length / this.nbCharactersPerRow) - 1) { + this.selectedRectangleYPos++; + } + this.updateSelectedPlayer(); + }); + this.input.keyboard.on('keydown-UP', () => { + if (this.selectedRectangleYPos > 0) { + this.selectedRectangleYPos--; + } + this.updateSelectedPlayer(); + }); + + /*create user*/ + this.createCurrentPlayer(); + } + + update(time: number, delta: number): void { + this.pressReturnField.setVisible(!!(Math.floor(time / 500) % 2)); + } + + private async login(name: string) { + return gameManager.connect(name, this.selectedPlayer.texture.key).then(() => { + // Do we have a start URL in the address bar? If so, let's redirect to this address + let instanceAndMapUrl = this.findMapUrl(); + if (instanceAndMapUrl !== null) { + let [mapUrl, instance] = instanceAndMapUrl; + let key = gameManager.loadMap(mapUrl, this.scene, instance); + this.scene.start(key); + return mapUrl; + } else { + // If we do not have a map address in the URL, let's ask the server for a start map. + return gameManager.loadStartMap().then((scene : any) => { + if (!scene) { + return; + } + let key = gameManager.loadMap(window.location.protocol + "//" + scene.mapUrlStart, this.scene, scene.startInstance); + this.scene.start(key); + return scene; + }).catch((err) => { + console.error(err); + throw err; + }); + } + }).catch((err) => { + console.error(err); + throw err; + }); + } + + /** + * Returns the map URL and the instance from the current URL + */ + private findMapUrl(): [string, string]|null { + let path = window.location.pathname; + if (!path.startsWith('/_/')) { + return null; + } + let instanceAndMap = path.substr(3); + let firstSlash = instanceAndMap.indexOf('/'); + if (firstSlash === -1) { + return null; + } + let instance = instanceAndMap.substr(0, firstSlash); + return [window.location.protocol+'//'+instanceAndMap.substr(firstSlash+1), instance]; + } + + createCurrentPlayer(): void { + for (let i = 0; i { + this.selectedPlayer.anims.pause(); + this.selectedRectangle.setY(player.y); + this.selectedRectangle.setX(player.x); + player.play(playerResource.name); + this.selectedPlayer = player; + }); + this.players.push(player); + } + this.selectedPlayer = this.players[0]; + this.selectedPlayer.play(PLAYER_RESOURCES[0].name); + } + + /** + * Returns pixel position by on column and row number + */ + private getCharacterPosition(x: number, y: number): [number, number] { + return [ + this.game.renderer.width / 2 + 16 + (x - this.nbCharactersPerRow / 2) * 32, + y * 32 + 90 + ]; + } + + private updateSelectedPlayer(): void { + this.selectedPlayer.anims.pause(); + let [x, y] = this.getCharacterPosition(this.selectedRectangleXPos, this.selectedRectangleYPos); + this.selectedRectangle.setX(x); + this.selectedRectangle.setY(y); + let playerNumber = this.selectedRectangleXPos + this.selectedRectangleYPos * this.nbCharactersPerRow; + let player = this.players[playerNumber]; + player.play(PLAYER_RESOURCES[playerNumber].name); + this.selectedPlayer = player; + } +} diff --git a/front/src/index.ts b/front/src/index.ts index 5ece7d56..ab374c2f 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -5,13 +5,14 @@ import {cypressAsserter} from "./Cypress/CypressAsserter"; import {LoginScene} from "./Phaser/Login/LoginScene"; import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene"; import {gameManager} from "./Phaser/Game/GameManager"; +import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene"; const config: GameConfig = { title: "Office game", width: window.innerWidth / RESOLUTION, height: window.innerHeight / RESOLUTION, parent: "game", - scene: [LoginScene, ReconnectingScene], + scene: [SelectCharacterScene, LoginScene, ReconnectingScene], zoom: RESOLUTION, physics: { default: "arcade",