implement main menu, implement textfield widget, implement create world screen, implement splash screen

This commit is contained in:
LabyStudio
2022-05-13 08:41:28 +02:00
parent 70144259d1
commit d3e4e749d5
35 changed files with 705 additions and 197 deletions
-1
View File
@@ -11,7 +11,6 @@
<div id="content">
<div id="canvas-container" class="fullscreen"></div>
<span id="pre-status">Loading scripts...</span>
</div>
</body>
+9 -8
View File
@@ -2,10 +2,6 @@ import Minecraft from './net/minecraft/client/Minecraft.js';
class Start {
constructor(preStatusElementId) {
this.preStatusElement = document.getElementById(preStatusElementId);
}
loadTextures(textures) {
let resources = [];
let index = 0;
@@ -37,10 +33,15 @@ class Start {
"terrain/terrain.png",
"terrain/sun.png",
"terrain/moon.png",
"char.png"
"char.png",
"gui/title/minecraft.png",
"gui/title/background/panorama_0.png",
"gui/title/background/panorama_1.png",
"gui/title/background/panorama_2.png",
"gui/title/background/panorama_3.png",
"gui/title/background/panorama_4.png",
"gui/title/background/panorama_5.png"
]).then((resources) => {
this.preStatusElement.remove();
// Launch actual game on canvas
window.app = new Minecraft(canvasWrapperId, resources);
});
@@ -48,4 +49,4 @@ class Start {
}
// Launch game
new Start("pre-status").launch("canvas-container");
new Start().launch("canvas-container");
+60 -25
View File
@@ -1,5 +1,6 @@
import GuiIngameMenu from "./gui/screens/GuiIngameMenu.js";
import Keyboard from "../util/Keyboard.js";
import Minecraft from "./Minecraft.js";
export default class GameWindow {
@@ -29,17 +30,16 @@ export default class GameWindow {
this.canvasItems = document.createElement('canvas');
this.wrapper.appendChild(this.canvasItems);
// On resize
let scope = this;
let mouseDownInterval = null;
// Request focus
document.onclick = function () {
if (scope.minecraft.currentScreen === null) {
scope.requestFocus();
document.onclick = () => {
if (this.minecraft.currentScreen === null) {
this.requestFocus();
}
}
window.addEventListener('resize', _ => scope.updateWindowSize(), false);
window.addEventListener('resize', _ => this.updateWindowSize(), false);
// Focus listener
document.addEventListener('pointerlockchange', _ => this.onFocusChanged(), false);
@@ -50,7 +50,7 @@ export default class GameWindow {
// Handle mouse move on screen
if (!(minecraft.currentScreen === null)) {
minecraft.currentScreen.mouseDragged(event.x / scope.scaleFactor, event.y / scope.scaleFactor, event.code);
minecraft.currentScreen.mouseDragged(event.x / this.scaleFactor, event.y / this.scaleFactor, event.code);
}
}, false);
@@ -58,43 +58,51 @@ export default class GameWindow {
document.addEventListener('mouseup', event => {
// Handle mouse release on screen
if (!(minecraft.currentScreen === null)) {
minecraft.currentScreen.mouseReleased(event.x / scope.scaleFactor, event.y / scope.scaleFactor, event.code);
minecraft.currentScreen.mouseReleased(event.x / this.scaleFactor, event.y / this.scaleFactor, event.code);
}
clearInterval(mouseDownInterval);
}, false);
// Losing focus event
this.canvas.addEventListener("mouseout", function () {
this.canvas.addEventListener("mouseout", () => {
if (minecraft.currentScreen === null) {
minecraft.displayScreen(new GuiIngameMenu());
}
clearInterval(mouseDownInterval);
});
// Mouse buttons
document.addEventListener('mousedown', function (event) {
document.addEventListener('mousedown', event => {
// 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 (!scope.isMobile) {
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 / scope.scaleFactor, event.y / scope.scaleFactor, event.code);
minecraft.currentScreen.mouseClicked(event.x / this.scaleFactor, event.y / this.scaleFactor, event.code);
}
}, false);
// Mouse scroll
document.addEventListener('wheel', function (event) {
document.addEventListener('wheel', (event) => {
let delta = Math.sign(event.deltaY);
minecraft.onMouseScroll(delta);
}, false);
// Keyboard interaction with screen
window.addEventListener('keydown', function (event) {
window.addEventListener('keydown', (event) => {
if (event.code === "F11") {
return; // Toggle fullscreen
}
@@ -104,7 +112,7 @@ export default class GameWindow {
if (!(minecraft.currentScreen === null)) {
// Handle key type on screen
minecraft.currentScreen.keyTyped(event.code);
minecraft.currentScreen.keyTyped(event.code, event.key);
} else if (event.code === 'Escape') {
minecraft.displayScreen(new GuiIngameMenu());
} else {
@@ -112,24 +120,35 @@ export default class GameWindow {
}
});
// 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', function (event) {
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 > scope.wrapper.offsetWidth / 2;
let isRightHand = x > this.wrapper.offsetWidth / 2;
if (isRightHand) {
touchStart = Date.now();
} else {
let tileSize = scope.wrapper.offsetWidth / 8;
let tileSize = this.wrapper.offsetWidth / 8;
let tileX = 0;
let tileY = scope.wrapper.offsetHeight - tileSize * 3;
let tileY = this.wrapper.offsetHeight - tileSize * 3;
let relX = x - tileX;
let relY = y - tileY;
@@ -169,7 +188,7 @@ export default class GameWindow {
// Touch movement
let prevTouch;
window.addEventListener('touchmove', function (event) {
window.addEventListener('touchmove', (event) => {
for (let i = 0; i < event.touches.length; i++) {
let touch = event.touches[i];
@@ -177,20 +196,20 @@ export default class GameWindow {
let y = touch.pageY;
// Right hand
let isRightHand = x > scope.wrapper.offsetWidth / 2;
let isRightHand = x > this.wrapper.offsetWidth / 2;
if (isRightHand) {
// Player movement
if (prevTouch) {
scope.mouseMotionX = (x - prevTouch.pageX) * 10;
scope.mouseMotionY = -(y - prevTouch.pageY) * 10;
this.mouseMotionX = (x - prevTouch.pageX) * 10;
this.mouseMotionY = -(y - prevTouch.pageY) * 10;
}
prevTouch = touch;
}
}
});
window.addEventListener('touchend', function (event) {
window.addEventListener('touchend', (event) => {
// Break block
if (!prevTouch && touchStart && (Date.now() - touchStart) < 1000) {
minecraft.onMouseClicked(2);
@@ -204,7 +223,7 @@ export default class GameWindow {
let touch = event.changedTouches[i];
// Left hand
let isLeftHand = touch.pageX < scope.wrapper.offsetWidth / 2;
let isLeftHand = touch.pageX < this.wrapper.offsetWidth / 2;
// Release all keys
if (isLeftHand) {
@@ -326,4 +345,20 @@ export default class GameWindow {
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();
}
}
+119 -44
View File
@@ -5,18 +5,22 @@ import WorldRenderer from "./render/WorldRenderer.js";
import ScreenRenderer from "./render/gui/ScreenRenderer.js";
import ItemRenderer from "./render/gui/ItemRenderer.js";
import IngameOverlay from "./gui/IngameOverlay.js";
import GuiLoadingScreen from "./gui/screens/GuiLoadingScreen.js";
import PlayerEntity from "./entity/PlayerEntity.js";
import SoundManager from "./sound/SoundManager.js";
import World from "./world/World.js";
import Block from "./world/block/Block.js";
import BoundingBox from "../util/BoundingBox.js";
import {BlockRegistry} from "./world/block/BlockRegistry.js";
import FontRenderer from "./render/gui/FontRenderer.js";
import GrassColorizer from "./render/GrassColorizer.js";
import GuiMainMenu from "./gui/screens/GuiMainMenu.js";
import GuiLoadingScreen from "./gui/screens/GuiLoadingScreen.js";
import * as THREE from "../../../../../libraries/three.module.js";
export default class Minecraft {
static VERSION = "1.0.0"
static URL_GITHUB = "https://github.com/labystudio/js-minecraft";
/**
* Create Minecraft instance and render it on a canvas
*/
@@ -25,6 +29,8 @@ export default class Minecraft {
this.currentScreen = null;
this.loadingScreen = null;
this.world = null;
this.player = null;
this.fps = 0;
@@ -45,10 +51,6 @@ export default class Minecraft {
// Create current screen and overlay
this.ingameOverlay = new IngameOverlay(this, this.window);
// Display loading screen
this.loadingScreen = new GuiLoadingScreen();
this.loadingScreen.setTitle("Building terrain...");
this.frames = 0;
this.lastTime = Date.now();
@@ -66,18 +68,10 @@ export default class Minecraft {
// Update window size
this.window.updateWindowSize();
// Create world
this.world = new World(this);
this.worldRenderer.scene.add(this.world.group);
// Create sound manager
this.soundManager = new SoundManager();
// Create player
this.player = new PlayerEntity(this, this.world);
this.world.addEntity(this.player);
this.displayScreen(this.loadingScreen);
this.displayScreen(new GuiMainMenu());
// Initialize
this.init();
@@ -87,23 +81,53 @@ export default class Minecraft {
// Start render loop
this.running = true;
this.requestNextFrame();
}
// Load spawn chunks and respawn player
this.world.findSpawn();
this.world.loadSpawnChunks();
this.player.respawn();
loadWorld(world) {
if (world === null) {
this.worldRenderer.reset();
this.itemRenderer.reset();
this.world = null;
this.player = null;
this.loadingScreen = null;
this.displayScreen(new GuiMainMenu());
} else {
// Display loading screen
this.loadingScreen = new GuiLoadingScreen();
this.loadingScreen.setTitle("Building terrain...");
this.displayScreen(this.loadingScreen);
// Create world
this.world = world;
this.worldRenderer.scene.add(this.world.group);
// Create player
this.player = new PlayerEntity(this, this.world);
this.world.addEntity(this.player);
// Load spawn chunks and respawn player
this.world.findSpawn();
this.world.loadSpawnChunks();
this.player.respawn();
}
}
hasInGameFocus() {
return this.window.mouseLocked && this.currentScreen === null;
}
isInGame() {
return this.world !== null && this.worldRenderer !== null && this.player !== null;
}
requestNextFrame() {
let scope = this;
requestAnimationFrame(function () {
if (scope.running) {
scope.requestNextFrame();
scope.onLoop();
requestAnimationFrame(() => {
if (this.running) {
this.requestNextFrame();
this.onLoop();
} else {
this.window.close();
}
});
}
@@ -132,30 +156,34 @@ export default class Minecraft {
}
onRender(partialTicks) {
// Player rotation
if (!this.isPaused()) {
this.player.turn(this.window.mouseMotionX, this.window.mouseMotionY);
if (this.isInGame()) {
// Player rotation
if (!this.isPaused()) {
this.player.turn(this.window.mouseMotionX, this.window.mouseMotionY);
this.window.mouseMotionX = 0;
this.window.mouseMotionY = 0;
this.window.mouseMotionX = 0;
this.window.mouseMotionY = 0;
}
// Update lights
while (this.world.updateLights()) {
// Empty
}
// Render the game
if (this.hasInGameFocus()) {
this.worldRenderer.render(partialTicks);
}
}
// Update lights
while (this.world.updateLights()) {
// Empty
}
// Render the game
if (this.hasInGameFocus()) {
this.worldRenderer.render(partialTicks);
}
// Render current screen
this.screenRenderer.render(partialTicks);
this.itemRenderer.render(partialTicks);
}
displayScreen(screen) {
if (typeof screen === "undefined") {
console.log("Tried to display an undefined screen");
console.error("Tried to display an undefined screen");
return;
}
@@ -183,7 +211,7 @@ export default class Minecraft {
}
onTick() {
if (!this.isPaused()) {
if (this.isInGame() && !this.isPaused()) {
// Tick world
this.world.onTick();
@@ -194,13 +222,35 @@ export default class Minecraft {
this.player.onUpdate();
}
// Tick the screen
if (this.currentScreen !== null) {
this.currentScreen.updateScreen();
}
// Update loading progress
if (!(this.loadingScreen === null)) {
let progress = Math.max(0, 1 - this.world.lightUpdateQueue.length / 10000);
if (this.loadingScreen !== null && this.isInGame()) {
let cameraChunkX = Math.floor(this.player.x) >> 4;
let cameraChunkZ = Math.floor(this.player.z) >> 4;
let renderDistance = WorldRenderer.RENDER_DISTANCE;
let requiredChunks = Math.pow(renderDistance * 2 - 1, 2);
let loadedChunks = this.world.chunks.size;
// Load chunks and count
setTimeout(() => {
for (let x = -renderDistance + 1; x < renderDistance; x++) {
for (let z = -renderDistance + 1; z < renderDistance; z++) {
this.world.getChunkAt(cameraChunkX + x, cameraChunkZ + z);
}
}
}, 0);
// Update progress
let progress = 1 / requiredChunks * Math.max(0, loadedChunks - this.world.lightUpdateQueue.length / 1000);
this.loadingScreen.setProgress(progress);
// Finish loading
if (progress >= 1) {
if (progress >= 0.99) {
this.loadingScreen = null;
this.displayScreen(null);
}
@@ -308,10 +358,35 @@ export default class Minecraft {
}
onMouseScroll(delta) {
this.player.inventory.shiftSelectedSlot(delta);
if (this.isInGame()) {
this.player.inventory.shiftSelectedSlot(delta);
}
}
isPaused() {
return !this.hasInGameFocus() && this.loadingScreen === null;
}
stop() {
this.running = false;
this.worldRenderer.reset();
this.itemRenderer.reset();
this.screenRenderer.reset();
}
getThreeTexture(id) {
if (!(id in this.resources)) {
console.error("Texture not found: " + id);
return;
}
let image = this.resources[id];
let canvas = document.createElement('canvas');
let context = canvas.getContext("2d");
canvas.width = image.width;
canvas.height = image.height;
context.imageSmoothingEnabled = false;
context.drawImage(image, 0, 0, image.width, image.height);
return new THREE.CanvasTexture(canvas);
}
}
+5 -1
View File
@@ -16,6 +16,10 @@ export default class Gui {
this.minecraft.fontRenderer.drawString(stack, string, x - this.getStringWidth(stack, string) / 2, y, color);
}
drawRightString(stack, string, x, y, color = -1) {
this.minecraft.fontRenderer.drawString(stack, string, x - this.getStringWidth(stack, string), y, color);
}
drawString(stack, string, x, y, color = -1) {
this.minecraft.fontRenderer.drawString(stack, string, x, y, color);
}
@@ -43,7 +47,7 @@ export default class Gui {
drawBackground(stack, texture, width, height, scale = 2) {
let pattern = stack.createPattern(texture, "repeat");
stack.save();
stack.filter = "brightness(50%)";
stack.filter = "brightness(28%)";
stack.scale(scale, scale);
stack.rect(0, 0, width / scale, height / scale);
stack.fillStyle = pattern;
+31 -2
View File
@@ -12,6 +12,7 @@ export default class GuiScreen extends Gui {
this.minecraft = minecraft;
this.width = width;
this.height = height;
this.textureBackground = this.getTexture("gui/background.png");
this.init();
}
@@ -32,7 +33,15 @@ export default class GuiScreen extends Gui {
}
}
keyTyped(key) {
updateScreen() {
for (let i in this.buttonList) {
let button = this.buttonList[i];
button.onTick();
}
}
keyTyped(key, character) {
if (key === "Escape") {
this.minecraft.displayScreen(null);
return true;
@@ -41,7 +50,17 @@ export default class GuiScreen extends Gui {
for (let i in this.buttonList) {
let button = this.buttonList[i];
button.keyTyped(key);
button.keyTyped(key, character);
}
return false;
}
keyReleased(key) {
for (let i in this.buttonList) {
let button = this.buttonList[i];
button.keyReleased(key);
}
return false;
@@ -72,4 +91,14 @@ export default class GuiScreen extends Gui {
button.mouseDragged(mouseX, mouseY, mouseButton);
}
}
drawDefaultBackground(stack) {
if (this.minecraft.isInGame()) {
// Render transparent background
this.drawRect(stack, 0, 0, this.width, this.height, 'black', 0.6);
} else {
// Render dirt background
this.drawBackground(stack, this.textureBackground, this.width, this.height);
}
}
}
@@ -36,7 +36,6 @@ export default class IngameOverlay extends Gui {
// Debug
this.drawString(stack, fps + " fps," + " " + lightUpdates + " light updates," + " " + chunkUpdates + " chunk updates", 1, 1);
this.drawString(stack, x + ", " + y + ", " + z + " (" + (x >> 4) + ", " + (y >> 4) + ", " + (z >> 4) + ")", 1, 1 + 9);
this.drawString(stack, "Light: " + lightLevel, 1, 1 + 9 + 10);
}
renderCrosshair(stack, x, y) {
@@ -16,33 +16,33 @@ export default class GuiControls extends GuiScreen {
let settings = this.minecraft.settings;
let scope = this;
this.buttonList.push(new GuiSliderButton("Mouse Sensitivity", settings.sensitivity, 50, 150, this.width / 2 - 100, this.height / 2 - 55, 200, 20, function (value) {
let y = this.height / 2 - 50;
this.buttonList.push(new GuiSliderButton("Mouse Sensitivity", settings.sensitivity, 50, 150, this.width / 2 - 100, y, 200, 20, value => {
settings.sensitivity = value;
}).setDisplayNameBuilder(function (name, value) {
return name + ": " + value + "%";
}));
this.buttonList.push(new GuiKeyButton("Crouch", settings.crouching, this.width / 2 - 100, this.height / 2 - 30, 200, 20, function (key) {
this.buttonList.push(new GuiKeyButton("Crouch", settings.crouching, this.width / 2 - 100, y + 24, 200, 20, key => {
settings.crouching = key;
}));
this.buttonList.push(new GuiKeyButton("Sprint", settings.sprinting, this.width / 2 - 100, this.height / 2 - 5, 200, 20, function (key) {
this.buttonList.push(new GuiKeyButton("Sprint", settings.sprinting, this.width / 2 - 100, y + 24 * 2, 200, 20, key => {
settings.sprinting = key;
}));
this.buttonList.push(new GuiKeyButton("Toggle Perspective", settings.togglePerspective, this.width / 2 - 100, this.height / 2 + 20, 200, 20, function (key) {
this.buttonList.push(new GuiKeyButton("Toggle Perspective", settings.togglePerspective, this.width / 2 - 100, y + 24 * 3, 200, 20, key => {
settings.togglePerspective = key;
}));
this.buttonList.push(new GuiButton("Done", this.width / 2 - 100, this.height / 2 + 70, 200, 20, function () {
scope.minecraft.displayScreen(scope.previousScreen);
this.buttonList.push(new GuiButton("Done", this.width / 2 - 100, y + 110, 200, 20, () => {
this.minecraft.displayScreen(this.previousScreen);
}));
}
drawScreen(stack, mouseX, mouseY, partialTicks) {
// Background
this.drawRect(stack, 0, 0, this.width, this.height, 'black', 0.6);
this.drawDefaultBackground(stack);
// Title
this.drawCenteredString(stack, "Controls", this.width / 2, 50);
@@ -0,0 +1,56 @@
import GuiScreen from "../GuiScreen.js";
import GuiButton from "../widgets/GuiButton.js";
import World from "../../world/World.js";
import GuiTextField from "../widgets/GuiTextField.js";
import Random from "../../../util/Random.js";
export default class GuiCreateWorld extends GuiScreen {
constructor(previousScreen) {
super();
this.previousScreen = previousScreen;
}
init() {
super.init();
let y = this.height / 2 - 50;
this.fieldSeed = new GuiTextField(this.width / 2 - 100, y + 30, 200, 20)
this.fieldSeed.maxLength = 30;
this.buttonList.push(this.fieldSeed);
this.buttonList.push(new GuiButton("Create New World", this.width / 2 - 155, y + 110, 150, 20, () => {
let seed = this.fieldSeed.getText();
if (seed.length === 0) {
seed = new Random().nextLong();
}
this.minecraft.loadWorld(new World(this.minecraft, seed));
}));
this.buttonList.push(new GuiButton("Cancel", this.width / 2 + 5, y + 110, 150, 20, () => {
this.minecraft.displayScreen(this.previousScreen);
}));
}
drawScreen(stack, mouseX, mouseY, partialTicks) {
// Background
this.drawDefaultBackground(stack);
// Title
this.drawCenteredString(stack, "Create New World", this.width / 2, 50);
let y = this.height / 2 - 50;
// Seed
this.drawString(stack, "Seed for the World Generator", this.width / 2 - 100, y + 17, -6250336);
this.drawString(stack, "Leave blank for a random seed", this.width / 2 - 100, y + 55, -6250336);
super.drawScreen(stack, mouseX, mouseY, partialTicks);
}
onClose() {
}
}
@@ -1,7 +1,6 @@
import GuiButton from "../widgets/GuiButton.js";
import GuiControls from "./GuiControls.js";
import GuiScreen from "../GuiScreen.js";
import GuiSettings from "./GuiSettings.js";
import GuiOptions from "./GuiOptions.js";
export default class GuiIngameMenu extends GuiScreen {
@@ -12,17 +11,17 @@ export default class GuiIngameMenu extends GuiScreen {
init() {
super.init();
let scope = this;
this.buttonList.push(new GuiButton("Back to game", this.width / 2 - 100, this.height / 2 - 20, 200, 20, function () {
scope.minecraft.displayScreen(null);
let y = this.height / 2 - 30;
this.buttonList.push(new GuiButton("Back to game", this.width / 2 - 100, y, 200, 20, () => {
this.minecraft.displayScreen(null);
}));
this.buttonList.push(new GuiButton("Settings...", this.width / 2 - 100, this.height / 2 + 20, 98, 20, function () {
scope.minecraft.displayScreen(new GuiSettings(scope));
this.buttonList.push(new GuiButton("Options...", this.width / 2 - 100, y + 24, 200, 20, () => {
this.minecraft.displayScreen(new GuiOptions(this));
}));
this.buttonList.push(new GuiButton("Controls...", this.width / 2 + 2, this.height / 2 + 20, 98, 20, function () {
scope.minecraft.displayScreen(new GuiControls(scope));
this.buttonList.push(new GuiButton("Save and Quit to Title", this.width / 2 - 100, y + 70, 200, 20, () => {
this.minecraft.loadWorld(null);
}));
}
@@ -8,7 +8,6 @@ export default class GuiLoadingScreen extends GuiScreen {
init() {
super.init();
this.textureBackground = this.getTexture("gui/background.png");
}
drawScreen(stack, mouseX, mouseY, partialTicks) {
@@ -0,0 +1,163 @@
import GuiScreen from "../GuiScreen.js";
import GuiButton from "../widgets/GuiButton.js";
import GuiOptions from "./GuiOptions.js";
import * as THREE from "../../../../../../../libraries/three.module.js";
import {BackSide} from "../../../../../../../libraries/three.module.js";
import MathHelper from "../../../util/MathHelper.js";
import Minecraft from "../../Minecraft.js";
import GuiCreateWorld from "./GuiCreateWorld.js";
export default class GuiMainMenu extends GuiScreen {
constructor() {
super();
this.panoramaTimer = 0;
this.splashText = "Minecraft written in JavaScript!";
}
init() {
super.init();
this.textureLogo = this.getTexture("gui/title/minecraft.png");
let y = this.height / 4 + 48;
this.buttonList.push(new GuiButton("Singleplayer", this.width / 2 - 100, y, 200, 20, () => {
this.minecraft.displayScreen(new GuiCreateWorld(this));
}));
this.buttonList.push(new GuiButton("Multiplayer", this.width / 2 - 100, y + 24, 200, 20, () => {
}).setEnabled(false));
this.buttonList.push(new GuiButton("Minecraft Realms", this.width / 2 - 100, y + 24 * 2, 200, 20, () => {
}).setEnabled(false));
this.buttonList.push(new GuiButton("Options...", this.width / 2 - 100, y + 72 + 12, 98, 20, () => {
this.minecraft.displayScreen(new GuiOptions(this));
}));
this.buttonList.push(new GuiButton("Quit Game", this.width / 2 + 2, y + 72 + 12, 98, 20, () => {
this.minecraft.stop();
}));
this.initPanoramaRenderer();
}
drawScreen(stack, mouseX, mouseY, partialTicks) {
let logoWidth = 274;
let x = this.width / 2 - logoWidth / 2;
let y = 30;
// Draw logo
this.drawLogo(stack, x, y);
// Draw version
this.drawString(stack, "minecraft-js " + Minecraft.VERSION, 2, this.height - 10, 0xFFFFFF);
// Draw copyright
let mouseOver = mouseX > this.width / 2 + 70 && mouseY > this.height - 20;
this.drawRightString(stack, "GitHub @LabyStudio/js-minecraft", this.width - 2, this.height - 10, mouseOver ? 0x0000FF : 0xFFFFFF);
// Draw buttons
super.drawScreen(stack, mouseX, mouseY, partialTicks);
// Draw splash text
this.drawSplash(stack);
let rotationX = Math.sin((this.panoramaTimer + partialTicks) / 400.0) * 25.0 + 20.0;
let rotationY = -(this.panoramaTimer + partialTicks) * 0.1;
this.camera.aspect = this.width / this.height;
this.camera.rotation.x = -MathHelper.toRadians(rotationX + 180);
this.camera.rotation.y = -MathHelper.toRadians(rotationY - 180);
this.camera.updateProjectionMatrix();
this.minecraft.worldRenderer.webRenderer.render(this.scene, this.camera);
}
updateScreen() {
this.panoramaTimer++;
}
drawLogo(stack, x, y) {
this.drawSprite(stack, this.textureLogo, 0, 0, 155, 44, x, y, 155, 44);
this.drawSprite(stack, this.textureLogo, 0, 45, 155, 44, x + 155, y, 155, 44);
}
drawSplash(stack) {
let f = 1.8 - Math.abs(Math.sin((new Date().getTime() % 1000) / 1000.0 * Math.PI * 2.0) * 0.1);
f = f * 100.0 / (this.getStringWidth(stack, this.splashText) + 32);
stack.save();
stack.translate((this.width / 2 + 90), 70.0, 0.0);
stack.rotate(MathHelper.toRadians(-20));
stack.scale(f, f, f);
this.drawCenteredString(stack, this.splashText, 0, -8, -256);
stack.restore();
}
keyTyped(key) {
// Cancel key inputs
}
mouseClicked(mouseX, mouseY, mouseButton) {
super.mouseClicked(mouseX, mouseY, mouseButton);
// Click on GitHub text
let mouseOver = mouseX > this.width / 2 + 70 && mouseY > this.height - 20;
if (mouseOver) {
this.minecraft.window.openUrl(Minecraft.URL_GITHUB, true);
}
}
initPanoramaRenderer() {
this.scene = new THREE.Scene();
// Create cube
let geometry = new THREE.BoxBufferGeometry(1, 1, 1);
let materials = [
new THREE.MeshBasicMaterial({
side: BackSide,
map: this.minecraft.getThreeTexture("gui/title/background/panorama_1.png")
}),
new THREE.MeshBasicMaterial({
side: BackSide,
map: this.minecraft.getThreeTexture("gui/title/background/panorama_3.png")
}),
new THREE.MeshBasicMaterial({
side: BackSide,
map: this.minecraft.getThreeTexture("gui/title/background/panorama_4.png")
}),
new THREE.MeshBasicMaterial({
side: BackSide,
map: this.minecraft.getThreeTexture("gui/title/background/panorama_5.png")
}),
new THREE.MeshBasicMaterial({
side: BackSide,
map: this.minecraft.getThreeTexture("gui/title/background/panorama_0.png")
}),
new THREE.MeshBasicMaterial({
side: BackSide,
map: this.minecraft.getThreeTexture("gui/title/background/panorama_2.png")
})
];
materials.forEach(material => {
material.map.minFilter = THREE.LinearFilter;
material.map.magFilter = THREE.LinearFilter;
});
let cube = new THREE.Mesh(geometry, materials);
cube.scale.set(-1, -1, -1);
this.scene.add(cube);
this.camera = new THREE.PerspectiveCamera(120, 1, 0.1, 1);
this.camera.rotation.order = 'ZYX';
// Apply blur
this.minecraft.window.canvas2d.style.backdropFilter = "saturate(80%) blur(10px)";
}
onClose() {
// Remove blur
this.minecraft.window.canvas2d.style.backdropFilter = "";
}
}
@@ -2,8 +2,9 @@ import GuiScreen from "../GuiScreen.js";
import GuiButton from "../widgets/GuiButton.js";
import GuiSwitchButton from "../widgets/GuiSwitchButton.js";
import GuiSliderButton from "../widgets/GuiSliderButton.js";
import GuiControls from "./GuiControls.js";
export default class GuiSettings extends GuiScreen {
export default class GuiOptions extends GuiScreen {
constructor(previousScreen) {
super();
@@ -16,26 +17,29 @@ export default class GuiSettings extends GuiScreen {
let settings = this.minecraft.settings;
let scope = this;
this.buttonList.push(new GuiSwitchButton("Ambient Occlusion", settings.ambientOcclusion, this.width / 2 - 100, this.height / 2 - 30, 200, 20, function (value) {
let y = this.height / 2 - 50;
this.buttonList.push(new GuiSwitchButton("Ambient Occlusion", settings.ambientOcclusion, this.width / 2 - 100, y, 200, 20, value => {
settings.ambientOcclusion = value;
scope.minecraft.worldRenderer.rebuildAll();
this.minecraft.worldRenderer.rebuildAll();
}));
this.buttonList.push(new GuiSwitchButton("View Bobbing", settings.viewBobbing, this.width / 2 - 100, this.height / 2 - 5, 200, 20, function (value) {
this.buttonList.push(new GuiSwitchButton("View Bobbing", settings.viewBobbing, this.width / 2 - 100, y + 24, 200, 20, value => {
settings.viewBobbing = value;
}));
this.buttonList.push(new GuiSliderButton("FOV", settings.fov, 50, 100, this.width / 2 - 100, this.height / 2 + 20, 200, 20, function (value) {
this.buttonList.push(new GuiSliderButton("FOV", settings.fov, 50, 100, this.width / 2 - 100, y + 24 * 2, 200, 20, value => {
settings.fov = value;
}));
this.buttonList.push(new GuiButton("Controls...", this.width / 2 - 100, y + 24 * 3, 200, 20, () => {
this.minecraft.displayScreen(new GuiControls(this));
}));
this.buttonList.push(new GuiButton("Done", this.width / 2 - 100, this.height / 2 + 70, 200, 20, function () {
scope.minecraft.displayScreen(scope.previousScreen);
this.buttonList.push(new GuiButton("Done", this.width / 2 - 100, y + 110, 200, 20, () => {
this.minecraft.displayScreen(this.previousScreen);
}));
}
drawScreen(stack, mouseX, mouseY, partialTicks) {
// Background
this.drawRect(stack, 0, 0, this.width, this.height, 'black', 0.6);
this.drawDefaultBackground(stack);
// Title
this.drawCenteredString(stack, "Settings", this.width / 2, 50);
@@ -28,6 +28,10 @@ export default class GuiButton extends Gui {
}
}
onTick() {
}
mouseClicked(mouseX, mouseY, mouseButton) {
this.onPress();
}
@@ -40,7 +44,11 @@ export default class GuiButton extends Gui {
}
keyTyped(key) {
keyTyped(key, character) {
}
keyReleased(key) {
}
@@ -56,4 +64,9 @@ export default class GuiButton extends Gui {
this.drawSprite(stack, textureGui, 200 - width / 2, spriteY, width / 2, 20, x + width / 2, y, width / 2, height);
}
setEnabled(enabled) {
this.enabled = enabled;
return this;
}
}
@@ -0,0 +1,93 @@
import GuiButton from "./GuiButton.js";
export default class GuiTextField extends GuiButton {
constructor(x, y, width, height) {
super("", x, y, width, height);
this.text = "";
this.isFocused = false;
this.cursorCounter = 0;
this.maxLength = 80;
}
render(stack, mouseX, mouseY, partialTicks) {
let cursorVisible = this.isFocused && Math.floor(this.cursorCounter / 6) % 2 === 0;
let textColor = this.enabled ? 14737632 : 7368816;
// Draw background
this.drawRect(stack, this.x - 1, this.y - 1, this.x + this.width + 1, this.y + this.height + 1, '#5f5f60');
this.drawRect(stack, this.x, this.y, this.x + this.width, this.y + this.height, 'black');
// Draw text
this.drawString(stack, this.text, this.x + 2, this.y + this.height / 2 - 4, textColor);
// Draw cursor
if (cursorVisible) {
this.drawString(stack, "_", this.x + 2 + this.getStringWidth(stack, this.text), this.y + this.height / 2 - 4, textColor);
}
}
onTick() {
this.cursorCounter++;
}
mouseClicked(mouseX, mouseY, mouseButton) {
this.isFocused = true;
}
onPress() {
}
keyTyped(key, character) {
if (key === "Backspace") {
if (this.text.length > 0) {
this.text = this.text.substring(0, this.text.length - 1);
}
return;
}
if (key === "ShiftLeft") {
this.shiftPressed = true;
return;
}
if (key === "ControlLeft") {
this.controlPressed = true;
return;
}
if (key === "KeyV" && this.controlPressed) {
this.minecraft.window.getClipboardText().then(text => {
this.text += text;
});
return;
}
if (key === "KeyA" && this.controlPressed) {
this.text = ""; // TODO: Select all
return;
}
if (this.text.length < this.maxLength) {
this.text += character;
}
}
keyReleased(key) {
if (key === "ShiftLeft") {
this.shiftPressed = false;
return;
}
if (key === "ControlLeft") {
this.controlPressed = false;
return;
}
}
getText() {
return this.text;
}
}
@@ -21,17 +21,17 @@ export default class WorldRenderer {
this.tessellator = new Tessellator();
// Load terrain texture
this.textureTerrain = new THREE.TextureLoader().load('src/resources/terrain/terrain.png');
this.textureTerrain = minecraft.getThreeTexture('terrain/terrain.png');
this.textureTerrain.magFilter = THREE.NearestFilter;
this.textureTerrain.minFilter = THREE.NearestFilter;
// Load sun texture
this.textureSun = new THREE.TextureLoader().load('src/resources/terrain/sun.png');
this.textureSun = minecraft.getThreeTexture('terrain/sun.png');
this.textureSun.magFilter = THREE.NearestFilter;
this.textureSun.minFilter = THREE.NearestFilter;
// Load moon texture
this.textureMoon = new THREE.TextureLoader().load('src/resources/terrain/moon.png');
this.textureMoon = minecraft.getThreeTexture('terrain/moon.png');
this.textureMoon.magFilter = THREE.NearestFilter;
this.textureMoon.minFilter = THREE.NearestFilter;
@@ -540,13 +540,6 @@ export default class WorldRenderer {
let world = this.minecraft.world;
let renderDistance = WorldRenderer.RENDER_DISTANCE;
// Load chunks
for (let x = -renderDistance + 1; x < renderDistance; x++) {
for (let z = -renderDistance + 1; z < renderDistance; z++) {
world.getChunkAt(cameraChunkX + x, cameraChunkZ + z);
}
}
// Update chunks
for (let [index, chunk] of world.chunks) {
let distanceX = Math.abs(cameraChunkX - chunk.x);
@@ -563,7 +556,7 @@ export default class WorldRenderer {
let chunkSection = chunk.sections[y];
// Is in camera view check
if (this.frustum.intersectsBox(chunkSection.boundingBox)) {
if (this.frustum.intersectsBox(chunkSection.boundingBox) && !chunkSection.isEmpty()) {
// Make section visible
chunkSection.group.visible = true;
@@ -590,7 +583,7 @@ export default class WorldRenderer {
// TODO Implement chunk unloading
//let index = chunk.x + (chunk.z << 16);
//world.chunks.delete(index);
//world.group.add(chunk.group);
//world.group.remove(chunk.group);
}
}
}
@@ -602,8 +595,15 @@ export default class WorldRenderer {
return distance1 - distance2;
});
// Rebuild 16 chunk sections per frame (An entire chunk)
for (let i = 0; i < 16; i++) {
// Update render order of chunks
world.group.children.sort((a, b) => {
let distance1 = Math.floor(Math.pow(a.chunkX - cameraChunkX, 2) + Math.pow(a.chunkZ - cameraChunkZ, 2));
let distance2 = Math.floor(Math.pow(b.chunkX - cameraChunkX, 2) + Math.pow(b.chunkZ - cameraChunkZ, 2));
return distance2 - distance1;
});
// Rebuild 8 chunk sections each frame
for (let i = 0; i < 8; i++) {
if (this.chunkSectionUpdateQueue.length !== 0) {
let chunkSection = this.chunkSectionUpdateQueue.shift();
if (chunkSection != null) {
@@ -612,13 +612,6 @@ export default class WorldRenderer {
}
}
}
// Update render order of chunks
world.group.children.sort((a, b) => {
let distance1 = Math.floor(Math.pow(a.chunkX - cameraChunkX, 2) + Math.pow(a.chunkZ - cameraChunkZ, 2));
let distance2 = Math.floor(Math.pow(b.chunkX - cameraChunkX, 2) + Math.pow(b.chunkZ - cameraChunkZ, 2));
return distance2 - distance1;
});
}
rebuildAll() {
@@ -775,4 +768,12 @@ export default class WorldRenderer {
stack.rotateX(MathHelper.toRadians(Math.abs(Math.cos(walked * Math.PI - 0.2) * yaw) * 5.0));
stack.rotateX(MathHelper.toRadians(pitch));
}
reset() {
if (this.minecraft.world !== null) {
this.scene.remove(this.minecraft.world.group);
}
this.webRenderer.clear();
this.overlay.clear();
}
}
@@ -11,7 +11,7 @@ export default class PlayerRenderer extends EntityRenderer {
this.worldRenderer = worldRenderer;
// Load character texture
this.textureCharacter = new THREE.TextureLoader().load('src/resources/char.png');
this.textureCharacter = worldRenderer.minecraft.getThreeTexture('char.png');
this.textureCharacter.magFilter = THREE.NearestFilter;
this.textureCharacter.minFilter = THREE.NearestFilter;
@@ -1,4 +1,5 @@
import Gui from "../../gui/Gui.js";
import MathHelper from "../../../util/MathHelper.js";
export default class FontRenderer {
@@ -47,17 +48,18 @@ export default class FontRenderer {
}
drawString(stack, string, x, y, color = -1) {
if (!this.isSafari) { // TODO Fix brightness filter on Safari
this.drawStringRaw(stack, string, x + 1, y + 1, (color & 0xFCFCFC) >> 2, true);
if (!this.isSafari) { // TODO Fix filter on Safari
this.drawStringRaw(stack, string, x + 1, y + 1, color, true);
}
this.drawStringRaw(stack, string, x, y, color, false);
this.drawStringRaw(stack, string, x, y, color);
}
drawStringRaw(stack, string, x, y, color = -1, isShadow = true) {
drawStringRaw(stack, string, x, y, color = -1, isShadow = false) {
stack.save();
if (isShadow) {
stack.filter = "brightness(20%)";
// Set color
if (color !== -1 || isShadow) {
this.setColor(stack, color, isShadow);
}
// For each character
@@ -71,7 +73,7 @@ export default class FontRenderer {
let nextCharacter = string[i + 1];
// Change color of string
//this.setColor(this.getColorOfCharacter(nextCharacter), isShadow);
this.setColor(stack, this.getColorOfCharacter(nextCharacter), isShadow);
// Skip the color code for rendering
i += 1;
@@ -138,4 +140,32 @@ export default class FontRenderer {
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
return canvas.getContext('2d').getImageData(0, 0, img.width, img.height).data;
}
setColor(stack, color, isShadow = false) {
if (isShadow) {
color = (color & 0xFCFCFC) >> 2;
}
let r = (color & 0xFF0000) >> 16;
let g = (color & 0x00FF00) >> 8;
let b = (color & 0x0000FF);
let hsv = MathHelper.rgb2hsv(r, g, b);
let hue = hsv[0] + 270;
let saturation = hsv[1];
let brightness = hsv[2] / 255 * 100;
// TODO fix colors
let saturate1 = saturation * 1000;
let saturate2 = saturation * 5000;
let saturate3 = saturation * 100;
if (!this.isSafari) { // TODO Fix filter on Safari
stack.filter = "sepia()"
+ " saturate(" + saturate1 + "%)"
+ " hue-rotate(" + hue + "deg)"
+ " saturate(" + saturate2 + "%)"
+ " brightness(" + brightness + "%)"
+ " saturate(" + saturate3 + "%)";
}
}
}
@@ -87,4 +87,12 @@ export default class ItemRenderer {
}
this.itemInHand = null;
}
reset() {
for (let i in this.items) {
this.scene.remove(this.items[i].group);
}
this.items = [];
this.webRenderer.clear();
}
}
@@ -6,9 +6,11 @@ export default class ScreenRenderer {
}
initialize() {
this.resolution = this.minecraft.isInGame() ? 1 : this.minecraft.window.scaleFactor; // Increase resolution for the splash text
// Update camera size
this.window.canvas2d.width = this.window.width;
this.window.canvas2d.height = this.window.height;
this.window.canvas2d.width = this.window.width * this.resolution;
this.window.canvas2d.height = this.window.height * this.resolution;
// Get context stack of 2d canvas
this.stack2d = this.window.canvas2d.getContext('2d');
@@ -21,18 +23,31 @@ export default class ScreenRenderer {
let mouseX = this.minecraft.window.mouseX;
let mouseY = this.minecraft.window.mouseY;
this.stack2d.save();
this.stack2d.scale(this.resolution, this.resolution, this.resolution);
// Reset 2d canvas
this.stack2d.clearRect(0, 0, this.window.width, this.window.height);
// Render in-game overlay
if (this.minecraft.loadingScreen === null) {
this.minecraft.ingameOverlay.render(this.stack2d, mouseX, mouseY, partialTicks);
try {
// Render in-game overlay
if (this.minecraft.isInGame() && this.minecraft.loadingScreen === null) {
this.minecraft.ingameOverlay.render(this.stack2d, mouseX, mouseY, partialTicks);
}
// Render current screen
if (this.minecraft.currentScreen !== null) {
this.minecraft.currentScreen.drawScreen(this.stack2d, mouseX, mouseY, partialTicks)
}
} catch (e) {
console.error(e);
}
// Render current screen
if (!(this.minecraft.currentScreen === null)) {
this.minecraft.currentScreen.drawScreen(this.stack2d, mouseX, mouseY, partialTicks);
}
this.stack2d.restore();
}
reset() {
this.stack2d.clearRect(0, 0, this.window.width, this.window.height);
}
}
@@ -50,12 +50,12 @@ export default class SoundManager {
sound.setRefDistance(0.1);
sound.setRolloffFactor(6);
sound.setFilter(sound.context.createBiquadFilter());
sound.setVolume(0);
// Load sound
let scope = this;
this.audioLoader.load(path, function (buffer) {
this.audioLoader.load(path, buffer => {
sound.setBuffer(buffer);
scope.scene.add(sound);
this.scene.add(sound);
});
return sound;
@@ -87,6 +87,7 @@ export default class SoundManager {
sound.filters[0].frequency.setValueAtTime(12000 * pitch, sound.context.currentTime);
// Play sound
sound.offset = 0;
sound.play();
}
}
+3 -1
View File
@@ -6,6 +6,8 @@ import * as THREE from "../../../../../../libraries/three.module.js";
export default class Chunk {
static SECTION_AMOUNT = 16;
constructor(world, x, z) {
this.world = world;
this.x = x;
@@ -21,7 +23,7 @@ export default class Chunk {
// Initialize sections
this.sections = [];
for (let y = 0; y < 16; y++) {
for (let y = 0; y < Chunk.SECTION_AMOUNT; y++) {
let section = new ChunkSection(world, this, x, y, z);
this.sections[y] = section;
@@ -24,7 +24,7 @@ export default class ChunkSection {
this.group = new THREE.Object3D();
this.group.matrixAutoUpdate = false;
this.isModified = false;
this.isModified = true;
this.blocks = [];
this.blocksData = [];
@@ -118,8 +118,8 @@ export default class ChunkSection {
getTotalLightAt(x, y, z) {
let index = y << 8 | z << 4 | x;
let skyLight = (!this.empty && index in this.skyLight ? this.skyLight[index] : (this.empty ? 15 : 14)) - this.world.skylightSubtracted;
let blockLight = !this.empty && index in this.blockLight ? this.blockLight[index] : 0;
let skyLight = (index in this.skyLight ? this.skyLight[index] : (this.empty ? 15 : 14)) - this.world.skylightSubtracted;
let blockLight = index in this.blockLight ? this.blockLight[index] : 0;
if (blockLight > skyLight) {
skyLight = blockLight;
}
@@ -129,10 +129,10 @@ export default class ChunkSection {
getLightAt(sourceType, x, y, z) {
let index = y << 8 | z << 4 | x;
if (sourceType === EnumSkyBlock.SKY) {
return !this.empty && index in this.skyLight ? this.skyLight[index] : (this.empty ? 15 : 14);
return index in this.skyLight ? this.skyLight[index] : (this.empty ? 15 : 14);
}
if (sourceType === EnumSkyBlock.BLOCK) {
return !this.empty && index in this.blockLight ? this.blockLight[index] : 0;
return index in this.blockLight ? this.blockLight[index] : 0;
}
return 0;
}
+8 -2
View File
@@ -8,7 +8,6 @@ import EnumBlockFace from "../../util/EnumBlockFace.js";
import Vector3 from "../../util/Vector3.js";
import Vector4 from "../../util/Vector4.js";
import MetadataChunkBlock from "../../util/MetadataChunkBlock.js";
import Long from "../../../../../../libraries/long.js";
import * as THREE from "../../../../../../libraries/three.module.js";
import WorldRenderer from "../render/WorldRenderer.js";
import Random from "../../util/Random.js";
@@ -17,7 +16,7 @@ export default class World {
static TOTAL_HEIGHT = ChunkSection.SIZE * 8 - 1; // ChunkSection.SIZE * 16 - 1;
constructor(minecraft, seed = Long.fromInt(Date.now() % 100000)) {
constructor(minecraft, seed) {
this.minecraft = minecraft;
this.entities = [];
@@ -182,6 +181,13 @@ export default class World {
return;
}
// Skip if section has no blocks
let section1 = this.getChunkSectionAt(x1 >> 4, y1 >> 4, z1 >> 4);
let section2 = this.getChunkSectionAt(x2 >> 4, y2 >> 4, z2 >> 4);
if (section1 === section2 && section1.isEmpty()) {
return;
}
// Add light update region to queue
if (this.lightUpdateQueue.length < 9999) {
this.lightUpdateQueue.push(new MetadataChunkBlock(sourceType, x1, y1, z1, x2, y2, z2));
+5
View File
@@ -94,4 +94,9 @@ export default class MathHelper {
return 0xff000000 | (r << 16) | (g << 8) | (b << 0);
}
static rgb2hsv(r, g, b) {
let v = Math.max(r, g, b), c = v - Math.min(r, g, b);
let h = c && ((v === r) ? (g - b) / c : ((v === g) ? 2 + (b - r) / c : 4 + (r - g) / c));
return [60 * (h < 0 ? h + 6 : h), v && c / v, v];
}
}
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

+9 -38
View File
@@ -1,51 +1,22 @@
@font-face {
font-family: "Minecraftia";
src: url(src/resources/font.ttf);
}
body {
margin: 0;
}
canvas {
position: absolute;
image-rendering: -moz-crisp-edges; /* Firefox */
image-rendering: -moz-crisp-edges; /* Firefox */
image-rendering: -webkit-crisp-edges; /* Webkit (Safari) */
image-rendering: pixelated; /* Chrome */
image-rendering: pixelated; /* Chrome */
}
#background {
background-image: url(src/resources/gui/background.png);
background-size: 128px 128px;
image-rendering: pixelated;
filter: brightness(0.5);
}
#pre-status {
/* Position */
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
/* No select */
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Old versions of Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none;
/* Non-prefixed version, currently
supported by Chrome, Edge, Opera and Firefox */
/* No overflow */
white-space: nowrap;
overflow: hidden;
/* Font */
font-size: 25px;
font-family: "Minecraftia", sans-serif;
text-shadow: 2px 2px #000000;
color: white;
background-image: url(src/resources/gui/title/splash.png);
background-repeat: no-repeat;
background-size: contain;
background-position: center;
image-rendering: -moz-crisp-edges; /* Firefox */
image-rendering: -webkit-crisp-edges; /* Webkit (Safari) */
image-rendering: pixelated; /* Chrome */
}
.fullscreen {