diff --git a/src/js/net/minecraft/client/GameWindow.js b/src/js/net/minecraft/client/GameWindow.js index 80b3c87..1af33a3 100644 --- a/src/js/net/minecraft/client/GameWindow.js +++ b/src/js/net/minecraft/client/GameWindow.js @@ -28,8 +28,23 @@ window.GameWindow = class { // Mouse motion document.addEventListener('mousemove', event => this.onMouseMove(event), false); + // Losing focus event + window.addEventListener("mouseout", function () { + if (minecraft.currentScreen === null) { + minecraft.displayScreen(new GuiIngameMenu()); + } + }); + // Mouse buttons - document.addEventListener('click', event => minecraft.onMouseClicked(event.button), false); + document.addEventListener('click', function (event) { + // Handle in-game mouse click + minecraft.onMouseClicked(event.button); + + // Handle mouse click on screen + if (!(minecraft.currentScreen === null)) { + minecraft.currentScreen.mouseClicked(event.x, event.y, event.code); + } + }, false); // Keyboard interaction with screen window.addEventListener('keydown', function (event) { @@ -41,6 +56,9 @@ window.GameWindow = class { // Cancel browser interaction event.preventDefault(); } + } else if (event.code === 'Escape') { + event.preventDefault(); + minecraft.displayScreen(new GuiIngameMenu()); } }); @@ -49,7 +67,12 @@ window.GameWindow = class { } requestFocus() { - this.renderer.canvasElement.requestPointerLock(); + let scope = this; + + setTimeout(function () { + window.focus(); + scope.renderer.canvasElement.requestPointerLock(); + }, 0); } loadRenderer(renderer) { @@ -64,10 +87,10 @@ window.GameWindow = class { this.onResize(); // Request focus - let minecraft = this.minecraft; + let scope = this; canvas.onclick = function () { - if (minecraft.currentScreen === null) { - canvas.requestPointerLock(); + if (scope.minecraft.currentScreen === null) { + scope.requestFocus(); } } } @@ -90,14 +113,13 @@ window.GameWindow = class { // Reinitialize current screen if (!(this.minecraft.currentScreen === null)) { - this.minecraft.currentScreen.init(this.minecraft, this.width, this.height); + this.minecraft.currentScreen.setup(this.minecraft, this.width, this.height); } } onFocusChanged() { this.mouseLocked = document.pointerLockElement === this.renderer.canvasElement; - // Open in-game menu if (!this.mouseLocked && this.minecraft.currentScreen === null) { this.minecraft.displayScreen(new GuiIngameMenu()); } diff --git a/src/js/net/minecraft/client/Minecraft.js b/src/js/net/minecraft/client/Minecraft.js index 8ec1ec5..d5550bc 100644 --- a/src/js/net/minecraft/client/Minecraft.js +++ b/src/js/net/minecraft/client/Minecraft.js @@ -31,7 +31,7 @@ window.Minecraft = class { this.worldRenderer.scene.add(this.world.group); // Create player - this.player = new Player(this.world); + this.player = new Player(this, this.world); this.pickedBlock = 1; // Initialize @@ -48,6 +48,10 @@ window.Minecraft = class { this.requestNextFrame(); } + hasInGameFocus() { + return this.window.mouseLocked && this.currentScreen === null; + } + requestNextFrame() { let scope = this; requestAnimationFrame(function () { @@ -88,7 +92,7 @@ window.Minecraft = class { onRender(partialTicks) { // Player rotation - if (this.window.mouseLocked && !(this.currentScreen === "null")) { + if (this.hasInGameFocus()) { this.player.turn(this.window.mouseMotionX, this.window.mouseMotionY); this.window.mouseMotionX = 0; @@ -112,7 +116,7 @@ window.Minecraft = class { if (screen === null) { this.window.requestFocus(); } else { - screen.init(this, this.window.width, this.window.height); + screen.setup(this, this.window.width, this.window.height); } } diff --git a/src/js/net/minecraft/client/entity/Player.js b/src/js/net/minecraft/client/entity/Player.js index f397385..21c7b3d 100644 --- a/src/js/net/minecraft/client/entity/Player.js +++ b/src/js/net/minecraft/client/entity/Player.js @@ -1,6 +1,7 @@ window.Player = class { - constructor(world) { + constructor(minecraft, world) { + this.minecraft = minecraft; this.world = world; this.prevX = 0; @@ -309,38 +310,40 @@ window.Player = class { let jumping = false; let sneaking = false; - if (Keyboard.isKeyDown("KeyR")) { // R - this.respawn(); - } - if (Keyboard.isKeyDown("KeyW")) { // W - moveForward++; - } - if (Keyboard.isKeyDown("KeyS")) { // S - moveForward--; - } - if (Keyboard.isKeyDown("KeyA")) { // A - moveStrafe++; - } - if (Keyboard.isKeyDown("KeyD")) { // D - moveStrafe--; - } - if (Keyboard.isKeyDown("Space")) { // Space - jumping = true; - } - if (Keyboard.isKeyDown("ShiftLeft")) { // Shift - if (this.moveForward > 0 && !this.sneaking && !this.sprinting && this.motionX !== 0 && this.motionZ !== 0) { - this.sprinting = true; - - this.updateFOVModifier(); + if (this.minecraft.hasInGameFocus()) { + if (Keyboard.isKeyDown("KeyR")) { // R + this.respawn(); } - } - if (Keyboard.isKeyDown("KeyQ")) { // Q - sneaking = true; - } + if (Keyboard.isKeyDown("KeyW")) { // W + moveForward++; + } + if (Keyboard.isKeyDown("KeyS")) { // S + moveForward--; + } + if (Keyboard.isKeyDown("KeyA")) { // A + moveStrafe++; + } + if (Keyboard.isKeyDown("KeyD")) { // D + moveStrafe--; + } + if (Keyboard.isKeyDown("Space")) { // Space + jumping = true; + } + if (Keyboard.isKeyDown("ShiftLeft")) { // Shift + if (this.moveForward > 0 && !this.sneaking && !this.sprinting && this.motionX !== 0 && this.motionZ !== 0) { + this.sprinting = true; - if (sneaking) { - moveStrafe = moveStrafe * 0.3; - moveForward = moveForward * 0.3; + this.updateFOVModifier(); + } + } + if (Keyboard.isKeyDown("KeyQ")) { // Q + sneaking = true; + } + + if (sneaking) { + moveStrafe = moveStrafe * 0.3; + moveForward = moveForward * 0.3; + } } this.moveForward = moveForward; diff --git a/src/js/net/minecraft/client/gui/Gui.js b/src/js/net/minecraft/client/gui/Gui.js index ae0d777..7d1afd3 100644 --- a/src/js/net/minecraft/client/gui/Gui.js +++ b/src/js/net/minecraft/client/gui/Gui.js @@ -1,5 +1,7 @@ window.Gui = class { + static FONT = "normal 20px Minecraftia"; + drawRect(stack, left, top, right, bottom, color, alpha = 1) { stack.save(); stack.fillStyle = color; @@ -8,18 +10,28 @@ window.Gui = class { stack.restore(); } - drawString(stack, string, x, y, color) { - this._drawString(stack, string, x, y, color, 0, false); + drawCenteredString(stack, string, x, y, color = 'white') { + this._drawString(stack, string, x + this.getStringWidth(stack, string) / 2, y, color, 1); } - _drawString(stack, string, x, y, color, alignment, bold) { - let size = 24; - stack.font = (bold ? "bold" : "normal") + " " + size + "px Minecraftia"; - stack.fillStyle = color; + drawString(stack, string, x, y, color = 'white') { + this._drawString(stack, string, x, y, color, 0); + } + + _drawString(stack, string, x, y, color, alignment) { + stack.font = Gui.FONT; + stack.fillStyle = 'black'; stack.textAlign = alignment === 0 ? "center" : alignment < 0 ? "left" : "right"; + stack.fillText(string, x + 2, y + 2); + stack.fillStyle = color; stack.fillText(string, x, y); } + getStringWidth(stack, string) { + stack.font = Gui.FONT; + return stack.measureText(string).width; + } + drawTexture(stack, texture, x, y, width, height, alpha = 1.0) { this.drawSprite(stack, texture, 0, 0, 256, 256, x, y, width, height, alpha); } @@ -42,7 +54,7 @@ window.Gui = class { stack.restore(); } - loadTexture(path) { + static loadTexture(path) { let img = new Image(); img.src = path; return img; diff --git a/src/js/net/minecraft/client/gui/GuiScreen.js b/src/js/net/minecraft/client/gui/GuiScreen.js index f802d7d..84e62a7 100644 --- a/src/js/net/minecraft/client/gui/GuiScreen.js +++ b/src/js/net/minecraft/client/gui/GuiScreen.js @@ -1,13 +1,28 @@ window.GuiScreen = class extends Gui { - init(minecraft, width, height) { + constructor() { + super(); + + this.buttonList = []; + } + + setup(minecraft, width, height) { this.minecraft = minecraft; this.width = width; this.height = height; + + this.init(); + } + + init() { + this.buttonList = []; } drawScreen(stack, mouseX, mouseY, partialTicks) { - + for (let i in this.buttonList) { + let button = this.buttonList[i]; + button.render(stack, mouseX, mouseY, partialTicks); + } } keyTyped(code) { @@ -18,4 +33,14 @@ window.GuiScreen = class extends Gui { return false; } + mouseClicked(mouseX, mouseY, mouseButton) { + for (let i in this.buttonList) { + let button = this.buttonList[i]; + + if (button.isMouseOver(mouseX, mouseY)) { + button.mouseClicked(mouseX, mouseY, mouseButton); + } + } + } + } \ No newline at end of file diff --git a/src/js/net/minecraft/client/gui/IngameOverlay.js b/src/js/net/minecraft/client/gui/IngameOverlay.js index 6344c60..723ef3a 100644 --- a/src/js/net/minecraft/client/gui/IngameOverlay.js +++ b/src/js/net/minecraft/client/gui/IngameOverlay.js @@ -4,7 +4,7 @@ window.IngameOverlay = class extends Gui { super(); this.window = window; - this.textureCrosshair = this.loadTexture("icons.png"); + this.textureCrosshair = Gui.loadTexture("icons.png"); } render(stack, mouseX, mouseY, partialTicks) { diff --git a/src/js/net/minecraft/client/gui/screens/GuiIngameMenu.js b/src/js/net/minecraft/client/gui/screens/GuiIngameMenu.js index fe80bac..fdab5eb 100644 --- a/src/js/net/minecraft/client/gui/screens/GuiIngameMenu.js +++ b/src/js/net/minecraft/client/gui/screens/GuiIngameMenu.js @@ -4,8 +4,23 @@ window.GuiIngameMenu = class extends GuiScreen { super(); } - drawScreen(stack, mouseX, mouseY, partialTicks) { + init() { + super.init(); + let scope = this; + this.buttonList.push(new GuiButton("Back to game", this.width / 2 - 100 * 3, this.height / 2 - 50 * 3, 200 * 3, 20 * 3, function () { + scope.minecraft.displayScreen(null); + })); + } + + drawScreen(stack, mouseX, mouseY, partialTicks) { + // Background + this.drawRect(stack, 0, 0, this.width, this.height, 'black', 0.6); + + // Title + this.drawCenteredString(stack, "Game paused", this.width / 2, 100); + + super.drawScreen(stack, mouseX, mouseY, partialTicks); } } \ No newline at end of file diff --git a/src/js/net/minecraft/client/gui/screens/GuiLoadingScreen.js b/src/js/net/minecraft/client/gui/screens/GuiLoadingScreen.js index 4007f75..42e9b7a 100644 --- a/src/js/net/minecraft/client/gui/screens/GuiLoadingScreen.js +++ b/src/js/net/minecraft/client/gui/screens/GuiLoadingScreen.js @@ -3,7 +3,7 @@ window.GuiLoadingScreen = class extends GuiScreen { constructor() { super(); - this.textureBackground = this.loadTexture("background.png"); + this.textureBackground = Gui.loadTexture("background.png"); } drawScreen(stack, mouseX, mouseY, partialTicks) { @@ -35,6 +35,8 @@ window.GuiLoadingScreen = class extends GuiScreen { this.height / 2 + progressHeight / 2, '#80ff80', ); + + super.drawScreen(stack, mouseX, mouseY, partialTicks); } setTitle(title) { diff --git a/src/js/net/minecraft/client/gui/widgets/GuiButton.js b/src/js/net/minecraft/client/gui/widgets/GuiButton.js new file mode 100644 index 0000000..952e373 --- /dev/null +++ b/src/js/net/minecraft/client/gui/widgets/GuiButton.js @@ -0,0 +1,30 @@ +window.GuiButton = class extends Gui { + + static textureGui = Gui.loadTexture("gui.png"); + + constructor(string, x, y, width, height, callback) { + super(); + + this.string = string; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.callback = callback; + } + + render(stack, mouseX, mouseY, partialTicks) { + let mouseOver = this.isMouseOver(mouseX, mouseY); + this.drawSprite(stack, GuiButton.textureGui, 0, mouseOver ? 40 : 20, 200, 20, this.x, this.y, this.width, this.height); + this.drawCenteredString(stack, this.string, this.x + this.width / 2, this.y + this.height / 2 + 17); + } + + mouseClicked(mouseX, mouseY, mouseButton) { + this.callback(); + } + + isMouseOver(mouseX, mouseY) { + return mouseX > this.x && mouseX < this.x + this.width && mouseY > this.y && mouseY < this.y + this.height; + } + +} \ No newline at end of file diff --git a/src/js/net/minecraft/client/world/World.js b/src/js/net/minecraft/client/world/World.js index 7d8b3cf..4c64e1c 100644 --- a/src/js/net/minecraft/client/world/World.js +++ b/src/js/net/minecraft/client/world/World.js @@ -13,6 +13,16 @@ window.World = class { // Load world this.generator = new WorldGenerator(this, Date.now() % 100000); + + // Update lights async + let scope = this; + setInterval(function () { + let i = 1000; + while (scope.lightUpdateQueue.length >= 10 && i > 0) { + i--; + scope.lightUpdateQueue.shift().updateBlockLightning(scope); + } + }, 1); } onTick() { @@ -83,22 +93,7 @@ window.World = class { updateLights() { let scope = this; - if (this.lightUpdateQueue.length > 10) { - // Update lights async - setTimeout(function () { - // Update lights in queue - let i = 5000; - while (scope.lightUpdateQueue.length > 0) { - if (i <= 0) { - break; - } - - let meta = scope.lightUpdateQueue.shift(); - meta.updateBlockLightning(scope); - i--; - } - }, 0); - } else { + if (this.lightUpdateQueue.length < 10) { // Update lights in queue let i = 10; while (scope.lightUpdateQueue.length > 0) { diff --git a/src/resources/gui.png b/src/resources/gui.png new file mode 100644 index 0000000..973dd0d Binary files /dev/null and b/src/resources/gui.png differ diff --git a/src/resources/terrain.png b/src/resources/terrain.png index e0e796f..20e058b 100644 Binary files a/src/resources/terrain.png and b/src/resources/terrain.png differ diff --git a/src/start.js b/src/start.js index 84a2ccc..2454473 100644 --- a/src/start.js +++ b/src/start.js @@ -61,6 +61,7 @@ loadScripts([ "src/js/net/minecraft/util/Keyboard.js", "src/js/net/minecraft/client/gui/Gui.js", "src/js/net/minecraft/client/gui/GuiScreen.js", + "src/js/net/minecraft/client/gui/widgets/GuiButton.js", "src/js/net/minecraft/client/gui/IngameOverlay.js", "src/js/net/minecraft/client/gui/screens/GuiLoadingScreen.js", "src/js/net/minecraft/client/gui/screens/GuiIngameMenu.js", diff --git a/style.css b/style.css index 2ce76c1..cba8926 100644 --- a/style.css +++ b/style.css @@ -37,6 +37,7 @@ body { /* Font */ font-size: 25px; font-family: "Minecraftia", sans-serif; + text-shadow: 2px 2px #000000; color: white; }