Files
GameStarter/src/js/net/minecraft/client/world/Chunk.js
T

338 lines
9.3 KiB
JavaScript

import EnumSkyBlock from "../../util/EnumSkyBlock.js";
import Block from "./block/Block.js";
import World from "./World.js";
import ChunkSection from "./ChunkSection.js";
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;
this.z = z;
this.group = new THREE.Object3D();
this.group.matrixAutoUpdate = false;
this.group.chunkX = x;
this.group.chunkZ = z;
this.loaded = false;
this.isTerrainPopulated = false;
// Initialize sections
this.sections = [];
for (let y = 0; y < Chunk.SECTION_AMOUNT; y++) {
let section = new ChunkSection(world, this, x, y, z);
this.sections[y] = section;
this.group.add(section.group);
}
// Create height map
this.heightMap = [];
}
generateSkylightMap() {
// Calculate height map
for (let x = 0; x < 16; x++) {
for (let z = 0; z < 16; z++) {
this.setHeightAt(x, z, 0);
this.updateHeightMap(x, World.TOTAL_HEIGHT, z);
}
}
// Update light of neighbor blocks
for (let x = 0; x < 16; x++) {
for (let z = 0; z < 16; z++) {
this.notifyNeighbors(x, z);
}
}
// Rebuild all sections
this.setModifiedAllSections();
}
generateBlockLightMap() {
let targetY = 32;
for (let x = 0; x < 16; x++) {
for (let z = 0; z < 16; z++) {
for (let y = 0; y < World.TOTAL_HEIGHT; y++) {
let section = this.getSection(y >> 4);
let typeId = section.getBlockAt(x, y & 15, z);
let block = Block.getById(typeId);
let blockLight = typeId === 0 ? 0 : block.getLightValue();
if (blockLight > 0) {
section.setLightAt(EnumSkyBlock.BLOCK, x, y & 15, z, blockLight);
}
}
let level = 15;
for (let y = targetY - 2; y < 128 && level > 0;) {
y++;
let section = this.getSection(y >> 4);
let typeId = section.getBlockAt(x, y & 15, z);
let block = Block.getById(typeId);
let opacity = typeId === 0 ? 0 : block.getOpacity();
let blockLight = typeId === 0 ? 0 : block.getLightValue();
if (opacity === 0) {
opacity = 1;
}
level -= opacity;
if (blockLight > level) {
level = blockLight;
}
if (level < 0) {
level = 0;
}
section.setLightAt(EnumSkyBlock.BLOCK, x, y & 15, z, blockLight);
}
}
}
this.world.updateLight(EnumSkyBlock.BLOCK,
this.x * 16, targetY - 1, this.z * 16,
this.x * 16 + 16, targetY + 1, this.z * 16 + 16
);
this.setModifiedAllSections();
}
updateBlockLight() {
this.setModifiedAllSections();
}
notifyNeighbors(x, z) {
let height = this.getHeightAt(x, z);
let totalX = this.x * 16 + x;
let totalZ = this.z * 16 + z;
this.updateSkyLight(totalX - 1, totalZ, height);
this.updateSkyLight(totalX + 1, totalZ, height);
this.updateSkyLight(totalX, totalZ - 1, height);
this.updateSkyLight(totalX, totalZ + 1, height);
}
updateSkyLight(x, z, y) {
let height = this.world.getHeightAt(x, z);
if (height > y) {
this.world.updateLight(EnumSkyBlock.SKY, x, y, z, x, height, z);
} else if (height < y) {
this.world.updateLight(EnumSkyBlock.SKY, x, height, z, x, y, z);
}
this.setModifiedAllSections();
}
updateHeightMap(relX, y, relZ) {
let currentHighestY = this.getHeightAt(relX, relZ);
let highestY = currentHighestY;
if (y > currentHighestY) {
highestY = y;
}
highestY = this.calculateHeightAt(relX, relZ, highestY);
if (highestY === currentHighestY) {
return;
}
this.setHeightAt(relX, relZ, highestY);
let x = this.x * 16 + relX;
let z = this.z * 16 + relZ;
if (highestY < currentHighestY) {
for (let hy = highestY; hy < currentHighestY; hy++) {
this.setLightAt(EnumSkyBlock.SKY, relX, hy, relZ, 15);
}
} else {
this.world.updateLight(EnumSkyBlock.SKY, x, currentHighestY, z, x, highestY, z);
for (let hy = currentHighestY; hy < highestY; hy++) {
this.setLightAt(EnumSkyBlock.SKY, relX, hy, relZ, 0);
}
}
let lightLevel = 15;
let prevHeight = highestY;
while (highestY > 0 && lightLevel > 0) {
highestY--;
let typeId = this.getBlockID(relX, highestY, relZ);
let block = Block.getById(typeId);
let opacity = Math.floor(typeId === 0 ? 0 : block.getOpacity() * 255);
if (opacity === 0) {
opacity = 1;
}
lightLevel -= opacity;
if (lightLevel < 0) {
lightLevel = 0;
}
this.setLightAt(EnumSkyBlock.SKY, relX, highestY, relZ, lightLevel);
}
highestY = this.calculateHeightAt(relX, relZ, highestY);
if (highestY !== prevHeight) {
this.world.updateLight(EnumSkyBlock.SKY, x - 1, highestY, z - 1, x + 1, prevHeight, z + 1);
}
this.setModifiedAllSections();
}
calculateHeightAt(x, z, startY) {
let y = startY;
while (y > 0) {
let typeId = this.getBlockAt(x, y - 1, z);
let block = Block.getById(typeId);
let opacity = typeId === 0 ? 0 : block.getOpacity();
if (opacity !== 0) {
break;
}
y--;
}
return y;
}
updateHeightMapAt(x, z) {
let y = this.calculateHeightAt(x, z, World.TOTAL_HEIGHT);
this.setHeightAt(x, z, y);
}
setHeightAt(x, z, height) {
this.heightMap[z << 4 | x] = height;
}
/**
* Is the highest solid block or above
*/
isHighestBlock(x, y, z) {
return y >= this.getHighestBlockAt(x, z);
}
/**
* Is above the highest solid block
*/
isAboveGround(x, y, z) {
return y >= this.getHeightAt(x, z);
}
/**
* Get the first non-solid block
*/
getHeightAt(x, z) {
return this.heightMap[z << 4 | x];
}
/**
* Get the highest solid block
*/
getHighestBlockAt(x, z) {
return this.getHeightAt(x, z) - 1;
}
setLightAt(sourceType, x, y, z, level) {
let section = this.getSection(y >> 4);
section.setLightAt(sourceType, x, y & 15, z, level);
}
setBlockDataAt(x, y, z, data) {
this.setBlockAt(x, y, z, this.getBlockAt(x, y, z), data);
}
setBlockAt(x, y, z, typeId, data = 0) {
let section = this.getSection(y >> 4);
let yInSection = y & 15;
let height = this.getHeightAt(x, z);
let prevTypeId = section.getBlockAt(x, yInSection, z);
let prevData = section.getBlockDataAt(x, yInSection, z);
// Check if block type has changed
if (prevTypeId === typeId && prevData === data) {
return false;
}
// Update block type and data
section.setBlockAt(x, yInSection, z, typeId);
section.setBlockDataAt(x, yInSection, z, data);
if (!this.loaded) {
return;
}
// Update height map
let block = Block.getById(typeId);
if (typeId !== 0 && block.isSolid()) {
if (y >= height) {
this.updateHeightMap(x, y + 1, z);
}
} else if (y === height - 1) {
this.updateHeightMap(x, y, z);
}
let totalX = this.x * 16 + x;
let totalZ = this.z * 16 + z;
// Update light
this.world.updateLight(EnumSkyBlock.SKY, totalX, y, totalZ, totalX, y, totalZ);
this.world.updateLight(EnumSkyBlock.BLOCK, totalX, y, totalZ, totalX, y, totalZ);
// Notify surrounding blocks
this.notifyNeighbors(x, z);
// Handle block abilities
if (typeId !== 0) {
block.onBlockAdded(this.world, totalX, y, totalZ);
}
return true;
}
getBlockID(x, y, z) {
return this.getBlockAt(x, y, z);
}
getBlockAt(x, y, z) {
return this.getSection(y >> 4).getBlockAt(x, y & 15, z);
}
getBlockDataAt(x, y, z) {
return this.getSection(y >> 4).getBlockDataAt(x, y & 15, z);
}
getSection(y) {
return this.sections[y];
}
rebuild(renderer) {
for (let y = 0; y < this.sections.length; y++) {
this.sections[y].rebuild(renderer);
}
}
isLoaded() {
return this.loaded;
}
unload() {
this.loaded = false;
}
setModifiedAllSections() {
for (let y = 0; y < this.sections.length; y++) {
this.sections[y].isModified = true;
}
}
}