import GuiIngameMenu from "./gui/screens/GuiIngameMenu.js"; import Keyboard from "../util/Keyboard.js"; import Minecraft from "./Minecraft.js"; export default class GameWindow { constructor(minecraft, canvasWrapperId) { this.minecraft = minecraft; this.canvasWrapperId = canvasWrapperId; this.mouseMotionX = 0; this.mouseMotionY = 0; this.mouseLocked = false; this.actualMouseLocked = false; this.isMobile = this.detectTouchDevice(); // Get canvas wrapper this.wrapper = document.getElementById(this.canvasWrapperId); // Remove all children of wrapper while (this.wrapper.firstChild) { this.wrapper.removeChild(this.wrapper.firstChild); } // Create world renderer this.canvas = document.createElement('canvas'); this.wrapper.appendChild(this.canvas); // Create screen renderer this.canvas2d = document.createElement('canvas'); this.wrapper.appendChild(this.canvas2d); // Create screen item renderer this.canvasItems = document.createElement('canvas'); this.wrapper.appendChild(this.canvasItems); let mouseDownInterval = null; // Request focus document.onclick = () => { if (this.minecraft.currentScreen === null) { this.requestFocus(); } } window.addEventListener('resize', _ => this.updateWindowSize(), false); // Focus listener document.addEventListener('pointerlockchange', _ => this.onFocusChanged(), false); // Mouse motion document.addEventListener('mousemove', event => { this.onMouseMove(event); // Handle mouse move on screen if (!(minecraft.currentScreen === null)) { minecraft.currentScreen.mouseDragged(event.x / this.scaleFactor, event.y / this.scaleFactor, event.code); } }, false); // Mouse release document.addEventListener('mouseup', event => { // Handle mouse release on screen if (!(minecraft.currentScreen === null)) { minecraft.currentScreen.mouseReleased(event.x / this.scaleFactor, event.y / this.scaleFactor, event.code); } clearInterval(mouseDownInterval); }, false); // Losing focus event this.canvas.addEventListener("mouseout", () => { if (minecraft.currentScreen === null) { minecraft.displayScreen(new GuiIngameMenu()); } clearInterval(mouseDownInterval); }); // Right click document.addEventListener('contextmenu', event => { event.preventDefault(); }); // Mouse buttons document.addEventListener('mousedown', event => { event.preventDefault(); // Create sound engine (It has to be created after user interaction) if (!minecraft.soundManager.isCreated()) { minecraft.soundManager.create(minecraft.worldRenderer); } // Handle in-game mouse click if (!this.isMobile) { minecraft.onMouseClicked(event.button); // Start interval to repeat the mouse event clearInterval(mouseDownInterval); mouseDownInterval = setInterval(() => minecraft.onMouseClicked(event.button), 250); } // Handle mouse click on screen if (!(minecraft.currentScreen === null)) { minecraft.currentScreen.mouseClicked(event.x / this.scaleFactor, event.y / this.scaleFactor, event.code); } }, false); // Mouse scroll document.addEventListener('wheel', (event) => { let delta = Math.sign(event.deltaY); minecraft.onMouseScroll(delta); }, false); // Keyboard interaction with screen window.addEventListener('keydown', (event) => { if (event.code === "F11") { return; // Toggle fullscreen } // Prevent key event.preventDefault(); if (!(minecraft.currentScreen === null)) { // Handle key type on screen minecraft.currentScreen.keyTyped(event.code, event.key); } else if (event.code === 'Escape') { minecraft.displayScreen(new GuiIngameMenu()); } else { minecraft.onKeyPressed(event.code); } }); // Keyboard interaction with screen window.addEventListener('keyup', (event) => { // Prevent key event.preventDefault(); if (!(minecraft.currentScreen === null)) { // Handle key release on screen minecraft.currentScreen.keyReleased(event.code); } }); // Touch interaction let touchStart; window.addEventListener('touchstart', (event) => { for (let i = 0; i < event.touches.length; i++) { let touch = event.touches[i]; let x = touch.pageX; let y = touch.pageY; let isRightHand = x > this.wrapper.offsetWidth / 2; if (isRightHand) { touchStart = Date.now(); } else { let tileSize = this.wrapper.offsetWidth / 8; let tileX = 0; let tileY = this.wrapper.offsetHeight - tileSize * 3; let relX = x - tileX; let relY = y - tileY; let tileIndex = Math.floor(relX / tileSize) + Math.floor(relY / tileSize) * 3; // Walk buttons switch (tileIndex) { case 0: case 1: case 2: Keyboard.setState("KeyW", true); break; case 3: Keyboard.setState("KeyA", true); break; case 4: Keyboard.setState("Space", true); break; case 5: Keyboard.setState("KeyD", true); break; case 6: case 7: case 8: Keyboard.setState("KeyS", true); break; } } } // Create sound engine (It has to be created after user interaction) if (!minecraft.soundManager.isCreated()) { minecraft.soundManager.create(minecraft.worldRenderer); } }); // Touch movement let prevTouch; window.addEventListener('touchmove', (event) => { for (let i = 0; i < event.touches.length; i++) { let touch = event.touches[i]; let x = touch.pageX; let y = touch.pageY; // Right hand let isRightHand = x > this.wrapper.offsetWidth / 2; if (isRightHand) { // Player movement if (prevTouch) { this.mouseMotionX = (x - prevTouch.pageX) * 10; this.mouseMotionY = -(y - prevTouch.pageY) * 10; } prevTouch = touch; } } }); window.addEventListener('touchend', (event) => { // Break block if (!prevTouch && touchStart && (Date.now() - touchStart) < 1000) { minecraft.onMouseClicked(2); } prevTouch = null; touchStart = null; // Stop pressing keys for (let i = 0; i < event.changedTouches.length; i++) { let touch = event.changedTouches[i]; // Left hand let isLeftHand = touch.pageX < this.wrapper.offsetWidth / 2; // Release all keys if (isLeftHand) { Keyboard.unPressAll(); break; } } }); // Break block listener if (this.isMobile) { setInterval(() => { if (touchStart && (Date.now() - touchStart) > 1000) { touchStart = Date.now(); minecraft.onMouseClicked(0); } }, 200); } // Create keyboard Keyboard.create(); } requestFocus() { if (this.isMobile) { document.body.requestFullscreen(); } else { window.focus(); this.canvas.requestPointerLock(); document.body.style.cursor = 'none'; } this.mouseLocked = true; } exitFocus() { if (this.isMobile) { return; } document.exitPointerLock(); document.body.style.cursor = 'default'; } updateWindowSize() { this.updateScaleFactor(); let wrapperWidth = this.width * this.scaleFactor; let wrapperHeight = this.height * this.scaleFactor; let worldRenderer = this.minecraft.worldRenderer; let itemRenderer = this.minecraft.itemRenderer; // Update world renderer size and camera worldRenderer.camera.aspect = this.width / this.height; worldRenderer.camera.updateProjectionMatrix(); worldRenderer.webRenderer.setSize(wrapperWidth, wrapperHeight); // Update item renderer size and camera itemRenderer.camera.aspect = this.width / this.height; itemRenderer.camera.updateProjectionMatrix(); itemRenderer.webRenderer.setSize(wrapperWidth, wrapperHeight); // Update canvas 2d size this.canvas2d.style.width = wrapperWidth + "px"; this.canvas2d.style.height = wrapperHeight + "px"; // Reinitialize gui this.minecraft.screenRenderer.initialize(); // Reinitialize current screen if (!(this.minecraft.currentScreen === null)) { this.minecraft.currentScreen.setup(this.minecraft, this.width, this.height); } } updateScaleFactor() { let wrapperWidth = this.wrapper.offsetWidth; let wrapperHeight = this.wrapper.offsetHeight; let scale; for (scale = 1; wrapperWidth / (scale + 1) >= 320 && wrapperHeight / (scale + 1) >= 240; scale++) { // Empty } this.scaleFactor = scale; this.width = wrapperWidth / scale; this.height = wrapperHeight / scale; } onFocusChanged() { this.actualMouseLocked = document.pointerLockElement === this.canvas; } onMouseMove(event) { this.mouseX = event.clientX / this.scaleFactor; this.mouseY = event.clientY / this.scaleFactor; if (document.pointerLockElement !== this.canvas) { this.mouseLocked = false; if (this.minecraft.currentScreen === null) { this.requestFocus(); } } if (this.actualMouseLocked || this.mouseLocked) { this.mouseMotionX = event.movementX; this.mouseMotionY = -event.movementY; } } detectTouchDevice() { let match = window.matchMedia || window.msMatchMedia; if (match) { let mq = match("(pointer:coarse)"); return mq.matches; } return false; } close() { this.openUrl(Minecraft.URL_GITHUB); } openUrl(url, newTab) { if (newTab) { window.open(url, '_blank').focus(); } else { window.location = url; } } async getClipboardText() { return navigator.clipboard.readText(); } }