diff --git a/StrucTool [BP]/scripts/classes/BlockInfo.js b/StrucTool [BP]/scripts/classes/BlockInfo.js index 844c391..d2bbedc 100644 --- a/StrucTool [BP]/scripts/classes/BlockInfo.js +++ b/StrucTool [BP]/scripts/classes/BlockInfo.js @@ -14,7 +14,7 @@ class BlockInfo { } static showStructureBlockInfo(player) { - const block = Raycaster.getTargetedStructureBlock(player, { isFirst: true, collideWithWorldBlocks: true, useLayers: false }); + const block = Raycaster.getTargetedStructureBlock(player, { isFirst: true, collideWithWorldBlocks: true, useActiveLayer: false }); if (!block && this.shownToLastTick.has(player.id)) { player.onScreenDisplay.setActionBar({ text: 'Structure:\n§7None' }); this.shownToLastTick.delete(player.id); diff --git a/StrucTool [BP]/scripts/classes/BlockVerificationLevelRender.js b/StrucTool [BP]/scripts/classes/BlockVerificationLevelRender.js index 33ed279..34ebde3 100644 --- a/StrucTool [BP]/scripts/classes/BlockVerificationLevelRender.js +++ b/StrucTool [BP]/scripts/classes/BlockVerificationLevelRender.js @@ -4,13 +4,13 @@ import { Vector } from "../lib/Vector"; export class BlockVerificationLevelRender { opacity = 0.2; - lifetime = 0; + lifetimeSeconds = 0; - constructor(dimensionLocation, verificationLevel, lifetime = 5) { + constructor(dimensionLocation, verificationLevel, lifetimeSeconds = 5) { this.dimension = dimensionLocation.dimension; - this.location = new Vector(dimensionLocation.location.x, dimensionLocation.location.y, dimensionLocation.location.z); + this.location = Vector.from(dimensionLocation.location); this.verificationLevel = verificationLevel; - this.lifetime = lifetime; + this.lifetimeSeconds = lifetimeSeconds; this.renderBlock(); } @@ -19,7 +19,7 @@ export class BlockVerificationLevelRender { const color = this.getRGBAMolang(); if (!color) return; - color.setFloat("lifetime", this.lifetime); + color.setFloat("lifetime", this.lifetimeSeconds); try { this.dimension.spawnParticle(particleLocation.particleType, particleLocation.location, color); } catch { diff --git a/StrucTool [BP]/scripts/classes/InstanceEditForm.js b/StrucTool [BP]/scripts/classes/InstanceEditForm.js index e183aa4..46db387 100644 --- a/StrucTool [BP]/scripts/classes/InstanceEditForm.js +++ b/StrucTool [BP]/scripts/classes/InstanceEditForm.js @@ -1,7 +1,7 @@ import { structureCollection } from './StructureCollection'; import { MenuForm } from '../classes/MenuForm'; import { forceShow } from '../utils'; -import { InstanceEditOptions } from './enums/InstanceEditOptions'; +import { InstanceEditButtons } from './enums/InstanceEditButtons'; import { InstanceEditFormBuilder } from './InstanceEditFormBuilder'; import { FormCancelationReason } from '@minecraft/server-ui'; @@ -9,26 +9,26 @@ export class InstanceEditForm { instanceName; #buttons = { isEnabled: [ - InstanceEditOptions.NextLayer, - InstanceEditOptions.PreviousLayer, - InstanceEditOptions.SetLayer, - InstanceEditOptions.Move, - InstanceEditOptions.Statistics, - InstanceEditOptions.Settings, - InstanceEditOptions.Rename, - InstanceEditOptions.Disable, + InstanceEditButtons.NextLayer, + InstanceEditButtons.PreviousLayer, + InstanceEditButtons.SetLayer, + InstanceEditButtons.Move, + InstanceEditButtons.Statistics, + InstanceEditButtons.Settings, + InstanceEditButtons.Rename, + InstanceEditButtons.Disable, ], isNotEnabledAndIsNotPlaced: [ - InstanceEditOptions.Place, - InstanceEditOptions.Rename + InstanceEditButtons.Place, + InstanceEditButtons.Rename ], isNotEnabledButIsPlaced: [ - InstanceEditOptions.Enable, - InstanceEditOptions.Rename + InstanceEditButtons.Enable, + InstanceEditButtons.Rename ], common: [ - InstanceEditOptions.Delete, - InstanceEditOptions.MainMenu + InstanceEditButtons.Delete, + InstanceEditButtons.MainMenu ] } @@ -59,51 +59,51 @@ export class InstanceEditForm { if (!this.instance.hasLayers()) currentOptions = currentOptions.filter(option => - option !== InstanceEditOptions.SetLayer - && option !== InstanceEditOptions.NextLayer - && option !== InstanceEditOptions.PreviousLayer + option !== InstanceEditButtons.SetLayer + && option !== InstanceEditButtons.NextLayer + && option !== InstanceEditButtons.PreviousLayer ); return currentOptions; } handleOption(option) { switch (option) { - case InstanceEditOptions.Enable: + case InstanceEditButtons.Enable: this.instance.enable(); break; - case InstanceEditOptions.Disable: + case InstanceEditButtons.Disable: this.instance.disable(); break; - case InstanceEditOptions.Place: + case InstanceEditButtons.Place: this.instance.place(this.player.dimension.id, this.player.location); break; - case InstanceEditOptions.Rename: + case InstanceEditButtons.Rename: this.renameInstanceForm(); break; - case InstanceEditOptions.Delete: + case InstanceEditButtons.Delete: structureCollection.delete(this.instanceName); break; - case InstanceEditOptions.NextLayer: + case InstanceEditButtons.NextLayer: this.instance.increaseLayer(); new InstanceEditForm(this.player, this.instanceName); break; - case InstanceEditOptions.PreviousLayer: + case InstanceEditButtons.PreviousLayer: this.instance.decreaseLayer(); new InstanceEditForm(this.player, this.instanceName); break; - case InstanceEditOptions.SetLayer: + case InstanceEditButtons.SetLayer: this.setLayerForm(); break; - case InstanceEditOptions.Move: + case InstanceEditButtons.Move: this.instance.move(this.player.dimension.id, this.player.location); break; - case InstanceEditOptions.Statistics: + case InstanceEditButtons.Statistics: this.statisticsForm(); break; - case InstanceEditOptions.MainMenu: + case InstanceEditButtons.MainMenu: new MenuForm(this.player, { jumpToInstance: false }); break; - case InstanceEditOptions.Settings: + case InstanceEditButtons.Settings: this.settingsForm(); break; default: @@ -135,8 +135,7 @@ export class InstanceEditForm { InstanceEditFormBuilder.buildSetLayer(this.instance.getBounds().max.y, this.instance.getLayer()).show(this.player).then((response) => { if (response.canceled) return; - const selectedLayer = response.formValues[0]; - this.instance.setLayer(parseInt(selectedLayer)); + this.instance.setLayer(parseInt(response.formValues[0])); }); } @@ -152,9 +151,8 @@ export class InstanceEditForm { InstanceEditFormBuilder.buildSettings(this.instance).show(this.player).then((response) => { if (response.canceled) return; - const shouldRender = response.formValues[0]; - const trackPlayerDistance = response.formValues[1]; - this.instance.setVerifierOptions({ shouldRender, trackPlayerDistance }); + this.instance.setVerifierEnabled(response.formValues[0]); + this.instance.setVerifierDistance(response.formValues[1]); }); } } \ No newline at end of file diff --git a/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js b/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js index 3e316ab..e1f0148 100644 --- a/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js +++ b/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js @@ -9,7 +9,7 @@ export class InstanceEditFormBuilder { const location = instance.getLocation(); const form = new ActionFormData() .title(MenuFormBuilder.menuTitle) - let body = `Instance: §a${instance.name}\n§fStructure: §2${instance.getStructureId()}\n`; + let body = `Instance: §a${instance.name}\n§fStructure: §2${instance.options.getStructureId()}\n`; if (instance.hasLocation()) body += `§7(${location.location.x} ${location.location.y} ${location.location.z} in ${location.dimensionId})\n`; form.body(body); @@ -37,7 +37,7 @@ export class InstanceEditFormBuilder { static async buildStatistics(instance) { const buildStatisticsForm = new ActionFormData() .title(MenuFormBuilder.menuTitle) - const structureVerifier = new StructureVerifier(instance, { shouldRender: true, trackPlayerDistance: 0, intervalOrLifetime: 30 * TicksPerSecond }); + const structureVerifier = new StructureVerifier(instance, { isEnabled: true, trackPlayerDistance: 0, intervalOrLifetime: 30 * TicksPerSecond }); const verification = await structureVerifier.verifyStructure(); const statistics = new StructureStatistics(instance, verification); const statsMessage = statistics.getMessage(); @@ -48,7 +48,7 @@ export class InstanceEditFormBuilder { static buildSettings(instance) { return new ModalFormData() .title(MenuFormBuilder.menuTitle) - .toggle('Toggle block validation.', instance.verifier.shouldRender) + .toggle('Toggle block validation.', instance.options.verifier.isEnabled) .slider('Use the slider to restrict how far from players block validation occurs. Use 0 for no restriction.', 0, 10, 1, instance.verifier.trackPlayerDistance) .submitButton('§aApply'); } diff --git a/StrucTool [BP]/scripts/classes/InstanceOptions.js b/StrucTool [BP]/scripts/classes/InstanceOptions.js new file mode 100644 index 0000000..b3ce70b --- /dev/null +++ b/StrucTool [BP]/scripts/classes/InstanceOptions.js @@ -0,0 +1,85 @@ +import { Vector } from "../lib/Vector"; + +export class InstanceOptions { + instanceName = void 0; + structureId = void 0; + isEnabled = false; + dimensionId = void 0; + worldLocation = new Vector(); + currentLayer = 0; + verifier = { + isEnabled: true, + trackPlayerDistance: 5, + intervalOrLifetime: 10 + }; + + constructor(instanceName, structureId) { + this.instanceName = instanceName; + this.structureId = structureId; + this.load(); + } + + save() { + world.setDynamicProperty(`instanceOptions:${this.instanceName}`, JSON.stringify(this)); + } + + load() { + try { + const options = JSON.parse(world.getDynamicProperty(`instanceOptions:${this.instanceName}`)); + if (options) + Object.assign(this, options); + else + throw new Error("Options not found"); + } catch { + this.save(); + const options = JSON.parse(world.getDynamicProperty(`instanceOptions:${this.instanceName}`) || "{}"); + Object.assign(this, options); + } + this.worldLocation = Vector.from(this.worldLocation); + } + + clear() { + world.setDynamicProperty(`instanceOptions:${this.instanceName}`, void 0); + } + + getDimension() { + return world.getDimension(this.options.dimensionId); + } + + enable() { + this.isEnabled = true; + this.save(); + } + + disable() { + this.isEnabled = false; + this.save(); + } + + rename(newName) { + this.clear(); + this.instanceName = newName; + this.save(); + } + + move(dimensionId, worldLocation) { + this.dimensionId = dimensionId; + this.worldLocation = Vector.from(worldLocation).floor(); + this.save(); + } + + setLayer(layer) { + this.currentLayer = layer; + this.save(); + } + + setVerifierEnabled(enable) { + this.verifier.isEnabled = enable; + this.save(); + } + + setVerifierDistance(distance) { + this.verifier.trackPlayerDistance = distance; + this.save(); + } +} \ No newline at end of file diff --git a/StrucTool [BP]/scripts/classes/MaterialCounter.js b/StrucTool [BP]/scripts/classes/MaterialCounter.js index 3c7974c..a880a13 100644 --- a/StrucTool [BP]/scripts/classes/MaterialCounter.js +++ b/StrucTool [BP]/scripts/classes/MaterialCounter.js @@ -4,7 +4,7 @@ class MaterialCounter { static getAll(name) { const structure = structureCollection.get(name); const materials = {}; - for (const block of structure.getBlocks()) { + for (const block of structure.getAllBlocks()) { const typeId = block?.getItemStack()?.typeId.replace('minecraft:', ''); if (!typeId) continue; if (!materials[typeId]) { @@ -32,7 +32,7 @@ class MaterialCounter { static getPrintable(name) { const structure = structureCollection.get(name); const materials = {}; - for (const block of structure.getBlocks()) { + for (const block of structure.getAllBlocks()) { const itemStack = block?.getItemStack(); const typeId = itemStack?.typeId.replace('minecraft:', ''); if (!typeId) continue; diff --git a/StrucTool [BP]/scripts/classes/MenuForm.js b/StrucTool [BP]/scripts/classes/MenuForm.js index 13cc1be..17e1ec5 100644 --- a/StrucTool [BP]/scripts/classes/MenuForm.js +++ b/StrucTool [BP]/scripts/classes/MenuForm.js @@ -12,7 +12,7 @@ export class MenuForm { async show(jumpToInstance = true) { let instanceName; if (jumpToInstance) { - instanceName = structureCollection.getStructure(this.player.dimension.id, this.player.location, { useLayers: false })?.name; + instanceName = structureCollection.getStructure(this.player.dimension.id, this.player.location, { useActiveLayer: false })?.name; if (instanceName) { new InstanceEditForm(this.player, instanceName); return; diff --git a/StrucTool [BP]/scripts/classes/Outliner.js b/StrucTool [BP]/scripts/classes/Outliner.js index 817604a..6343e58 100644 --- a/StrucTool [BP]/scripts/classes/Outliner.js +++ b/StrucTool [BP]/scripts/classes/Outliner.js @@ -13,8 +13,8 @@ export class Outliner { constructor(dimension, min, max) { this.dimension = dimension; - this.min = new Vector(min.x, min.y, min.z); - this.max = new Vector(max.x, max.y, max.z); + this.min = Vector.from(min); + this.max = Vector.from(max); this.vertices = this.getVertices(min, max); } @@ -65,8 +65,8 @@ export class Outliner { setVertices(dimension, min, max) { this.dimension = dimension; - this.min = new Vector(min.x, min.y, min.z); - this.max = new Vector(max.x, max.y, max.z); + this.min = Vector.from(min); + this.max = Vector.from(max); this.vertices = this.getVertices(min, max); } @@ -103,7 +103,7 @@ export class Outliner { addStandaloneParticles(locations) { for (const location of locations) - this.vertices.push(new Vector(location.x, location.y, location.z)); + this.vertices.push(Vector.from(location)); } getNextParticleColor() { diff --git a/StrucTool [BP]/scripts/classes/Raycaster.js b/StrucTool [BP]/scripts/classes/Raycaster.js index 030b58c..0e36355 100644 --- a/StrucTool [BP]/scripts/classes/Raycaster.js +++ b/StrucTool [BP]/scripts/classes/Raycaster.js @@ -4,13 +4,13 @@ import { world } from "@minecraft/server"; export class Raycaster { static STEP_SIZE = 0.2; - static getStructureBlocks(dimension, startLocation, direction, { maxDistance = 7, getFirst = true, collideWithWorldBlocks = true, useLayers = true }) { + static getStructureBlocks(dimension, startLocation, direction, { maxDistance = 7, getFirst = true, collideWithWorldBlocks = true, useActiveLayer = true }) { // Can probably be optimized by the fact that we only need full blocks and aren't checking for partial blocks const blocks = []; let location = startLocation; let distance = 0; while (distance < maxDistance) { - const structure = structureCollection.getStructure(dimension.id, location, { useLayers }); + const structure = structureCollection.getStructure(dimension.id, location, { useActiveLayer }); if (structure) { const block = structure.getBlock(structure.toStructureCoords(location)); if (block?.type.id !== 'minecraft:air') { @@ -41,11 +41,11 @@ export class Raycaster { return blocks; } - static getTargetedStructureBlock(player, { isFirst = true, collideWithWorldBlocks = true, useLayers = true } = {}) { + static getTargetedStructureBlock(player, { isFirst = true, collideWithWorldBlocks = true, useActiveLayer = true } = {}) { const startLocation = player.getHeadLocation(); const direction = player.getViewDirection(); const maxDistance = 7; - const blocks = this.getStructureBlocks(player.dimension, startLocation, direction, { maxDistance, getFirst: isFirst, collideWithWorldBlocks, useLayers }); + const blocks = this.getStructureBlocks(player.dimension, startLocation, direction, { maxDistance, getFirst: isFirst, collideWithWorldBlocks, useActiveLayer }); if (blocks.length === 0) return void 0; return isFirst ? blocks[0] : blocks[blocks.length - 1]; diff --git a/StrucTool [BP]/scripts/classes/Structure.js b/StrucTool [BP]/scripts/classes/Structure.js new file mode 100644 index 0000000..e41d41e --- /dev/null +++ b/StrucTool [BP]/scripts/classes/Structure.js @@ -0,0 +1,56 @@ +import { world } from "@minecraft/server"; +import { Vector } from "../lib/Vector"; + +export class Structure { + structureId; + #structure; + + constructor(structureId) { + this.structureId = structureId; + this.#structure = world.structureManager.get(structureId); + if (!this.#structure) + throw new Error(`[StrucTool] Structure '${structureId}' not found.`); + this.#structure.saveToWorld(); + } + + getHeight() { + return this.#structure.size.y; + } + + getMin() { + return new Vector(0, 0, 0); + } + + getMax() { + return Vector.from(this.#structure.size); + } + + getBlock(structureLocation) { + const blockPermutation = this.#structure.getBlockPermutation(structureLocation); + if (!blockPermutation) + return void 0; + blockPermutation.location = structureLocation; + return blockPermutation; + } + + *getBlocks(locations) { + for (const location of locations) { + yield this.getBlock(location); + } + } + + *getLayerBlocks(y) { + for (let x = 0; x < this.#structure.size.x; x++) { + for (let z = 0; z < this.#structure.size.z; z++) { + yield this.getBlock({ x, y, z }); + } + } + } + + *getAllBlocks() { + for (let y = 0; y < this.#structure.size.y; y++) { + yield * this.getLayerBlocks(y); + } + } +} + \ No newline at end of file diff --git a/StrucTool [BP]/scripts/classes/StructureInstance.js b/StrucTool [BP]/scripts/classes/StructureInstance.js index 0fd71a0..9a5dbe0 100644 --- a/StrucTool [BP]/scripts/classes/StructureInstance.js +++ b/StrucTool [BP]/scripts/classes/StructureInstance.js @@ -1,191 +1,28 @@ -import { world } from "@minecraft/server"; import { Vector } from "../lib/Vector"; import { StructureOutliner } from "./StructureOutliner"; import { StructureVerifier } from "./StructureVerifier"; +import { InstanceOptions } from "./InstanceOptions"; +import { Structure } from "./Structure"; export class StructureInstance { - name; - #structure; - #options = { - structureId: void 0, - isEnabled: false, - dimensionId: void 0, - worldLocation: { x: 0, y: 0, z: 0 }, - rotation: 0, - mirror: false, - currentLayer: 0, - verifier: { - shouldRender: true, - trackPlayerDistance: 5, - intervalOrLifetime: 10 - } - }; - outliner = void 0; + options; + structure = void 0; verifier = void 0; + outliner = void 0; constructor(instanceName, structureId) { - this.name = instanceName; - this.#structure = world.structureManager.get(structureId); - if (!this.#structure) - throw new Error(`[StrucTool] Structure '${structureId}' not found.`); - this.#structure.saveToWorld(); - this.#options = this.loadOptions(); - this.#options.structureId = structureId; - this.updateOptions(); + this.structure = new Structure(structureId); + this.options = new InstanceOptions(instanceName, structureId); this.refreshBox(); } - loadOptions() { - try { - return JSON.parse(world.getDynamicProperty(`structOptions:${this.name}`)); - } catch (e) { - world.setDynamicProperty(`structOptions:${this.name}`, JSON.stringify(this.#options)); - } - return this.#options; - } - delete() { this.disable(); - world.setDynamicProperty(`structOptions:${this.name}`, void 0); - this.#structure = void 0; - this.#options = void 0; + this.options.clear(); + delete this.options; + delete this.structure; delete this.outliner; - } - - static parseOptions(instanceName) { - const options = JSON.parse(world.getDynamicProperty(`structOptions:${instanceName}`)); - if (!options) - throw new Error(`[StrucTool] Instance '${instanceName}' not found.`); - return options; - } - - updateOptions() { - world.setDynamicProperty(`structOptions:${this.name}`, JSON.stringify(this.#options)); - } - - getStructure() { - return this.#structure; - } - - getStructureId() { - return this.#options.structureId; - } - - getLocation() { - return { dimensionId: this.#options.dimensionId, location: this.#options.worldLocation }; - } - - getHeight() { - return this.#structure.size.y; - } - - getLayer() { - return this.#options.currentLayer || 0; - } - - getDimension() { - let dimension; - try { - dimension = world.getDimension(this.#options.dimensionId); - } catch (e) { - console.warn(`[StrucTool] Dimension '${this.#options.dimensionId}' not found. Defaulting to 'minecraft:overworld'.`); - dimension = world.getDimension("minecraft:overworld"); - } - return dimension; - } - - getBlock(structureLocation) { - const blockPermutation = this.#structure.getBlockPermutation({ x: structureLocation.x, y: structureLocation.y, z: structureLocation.z }); - if (!blockPermutation) - return void 0; - blockPermutation.location = structureLocation; - return blockPermutation; - } - - *getBlocks(locations = void 0) { - if (locations === void 0) { - for (let y = 0; y < this.#structure.size.y; y++) { - yield * this.getLayerBlocks(y); - } - } else { - for (const location of locations) { - yield this.getBlock(location); - } - } - } - - *getLayerBlocks(y) { - for (let x = 0; x < this.#structure.size.x; x++) { - for (let z = 0; z < this.#structure.size.z; z++) { - yield this.getBlock({ x, y, z }); - } - } - } - - getBounds() { - return { - min: new Vector(0, 0, 0), - max: new Vector(this.#structure.size.x, this.#structure.size.y, this.#structure.size.z) - }; - } - - getLayeredBounds() { - if (!this.#options.isEnabled) - throw new Error(`[StrucTool] Instance '${this.name}' is not placed.`); - return { - min: new Vector(0, this.#options.currentLayer - 1, 0), - max: new Vector(this.#structure.size.x, this.#options.currentLayer, this.#structure.size.z) - }; - } - - getTotalVolume() { - return this.#structure.size.x * this.#structure.size.y * this.#structure.size.z; - } - - getActiveVolume() { - if (!this.#options.isEnabled) - return 0; - if (this.#options.currentLayer === 0) - return this.getTotalVolume(); - return this.#structure.size.x * this.#structure.size.z; - } - - rename(newName) { - world.setDynamicProperty(`structOptions:${this.name}`, void 0); - this.name = newName; - world.setDynamicProperty(`structOptions:${this.name}`, JSON.stringify(this.#options)); - } - - place(dimensionId, worldLocation) { - this.move(dimensionId, worldLocation); - this.enable(); - } - - enable() { - this.#options.isEnabled = true; - this.updateOptions(); - this.refreshBox(); - } - - disable() { - this.#options.isEnabled = false; - this.updateOptions(); - this.refreshBox(); - } - - move(dimensionId, location) { - this.#options.dimensionId = dimensionId; - this.#options.worldLocation = { x: Math.floor(location.x), y: Math.floor(location.y), z: Math.floor(location.z) }; - this.updateOptions(); - this.refreshBox(); - } - - setLayer(layer) { - if (layer < 0 || layer > this.#structure.size.y) - throw new Error(`[StrucTool] Layer ${layer} is out of bounds.`); - this.#options.currentLayer = layer; - this.updateOptions(); - this.refreshBox(); + delete this.verifier; } refreshBox() { @@ -194,101 +31,174 @@ export class StructureInstance { if (!this.outliner) this.outliner = new StructureOutliner(this); if (!this.verifier) - this.verifier = new StructureVerifier(this, { shouldRender: this.#options.verifier.shouldRender, trackPlayerDistance: this.#options.verifier.trackPlayerDistance }); + this.verifier = new StructureVerifier(this, { isEnabled: this.options.verifier.isEnabled, trackPlayerDistance: this.options.verifier.trackPlayerDistance }); this.outliner.refresh(); this.verifier.refresh(); } - isLocationInStructure(dimensionId, structureLocation) { - if (this.#options.dimensionId !== dimensionId) - return false - const { min, max } = this.getBounds(); - return structureLocation.x >= min.x && structureLocation.x < max.x - && structureLocation.y >= min.y && structureLocation.y < max.y - && structureLocation.z >= min.z && structureLocation.z < max.z; + getStructureId() { + return this.options.structureId; } - isLocationInLayer(dimensionId, structureLocation) { - if (!this.#options.isEnabled || this.#options.dimensionId !== dimensionId) - return false - const { min, max } = this.getLayeredBounds(true); - return structureLocation.x >= min.x && structureLocation.x < max.x - && structureLocation.y >= min.y && structureLocation.y < max.y - && structureLocation.z >= min.z && structureLocation.z < max.z; + getLocation() { + return { dimensionId: this.options.dimensionId, location: this.options.worldLocation }; } - isLocationActive(dimensionId, structureLocation, { useLayers = true } = {}) { - if (!this.#options.isEnabled || this.#options.dimensionId !== dimensionId) - return false - if (useLayers && this.#options.currentLayer !== 0) - return this.isLocationInLayer(dimensionId, structureLocation); - return this.isLocationInStructure(dimensionId, structureLocation); + getDimension() { + return this.options.getDimension(); } - toGlobalCoords(structureLocation) { - return new Vector( - this.#options.worldLocation.x + structureLocation.x, - this.#options.worldLocation.y + structureLocation.y, - this.#options.worldLocation.z + structureLocation.z - ); + getMaxLayer() { + return this.structure.getHeight(); } - toStructureCoords(worldLocation) { - return new Vector( - worldLocation.x - this.#options.worldLocation.x, - worldLocation.y - this.#options.worldLocation.y, - worldLocation.z - this.#options.worldLocation.z - ); + getLayer() { + return this.options.currentLayer; + } + + getBounds() { + return { + min: this.structure.getMin(), + max: this.structure.getMax() + } + } + + getActiveBounds() { + if (!this.options.isEnabled) + throw new Error(`[StrucTool] Instance '${this.options.instanceName}' is not placed.`); + if (this.hasLayerSelected()) + return this.getLayeredBounds(); + return this.getBounds(); + } + + getLayeredBounds() { + if (!this.options.isEnabled) + throw new Error(`[StrucTool] Instance '${this.options.instanceName}' is not placed.`); + const min = this.structure.getMin(); + const max = this.structure.getMax(); + return { + min: new Vector(min.x, this.options.currentLayer - 1, min.z), + max: new Vector(max.x, this.options.currentLayer, max.z) + }; + } + + getBlock(structureLocation) { + return this.structure.getBlock(structureLocation); + } + + getBlocks(structureLocations) { + return this.structure.getBlocks(structureLocations); + } + + getLayerBlocks(layer) { + return this.structure.getLayerBlocks(layer); + } + + getAllBlocks() { + return this.structure.getAllBlocks(); + } + + isLocationActive(dimensionId, structureLocation, { useActiveLayer = true } = {}) { + if (!this.options.isEnabled || this.options.dimensionId !== dimensionId) + return false; + let bounds; + if (useActiveLayer) + bounds = this.getActiveBounds(); + else + bounds = this.getBounds(); + return structureLocation.x >= bounds.min.x && structureLocation.x < bounds.max.x + && structureLocation.y >= bounds.min.y && structureLocation.y < bounds.max.y + && structureLocation.z >= bounds.min.z && structureLocation.z < bounds.max.z; } isEnabled() { - return this.#options.isEnabled; + return this.options.isEnabled; } hasLocation() { - return this.#options.dimensionId && this.#options.worldLocation.x !== 0 && this.#options.worldLocation.y !== 0 && this.#options.worldLocation.z !== 0; - } - - isUsingLayers() { - return this.hasLayers() && this.#options.currentLayer !== 0; + return this.options.dimensionId && this.options.worldLocation.x !== 0 && this.options.worldLocation.y !== 0 && this.options.worldLocation.z !== 0; } hasLayers() { - return this.#structure.size.y > 1; + return this.getMaxLayer() > 1; + } + + hasLayerSelected() { + return this.hasLayers() && this.options.currentLayer !== 0; + } + + hasWholeStructureSelected() { + return this.hasLocation() && this.options.currentLayer === 0; } isAtMaxLayer() { - return !this.hasLayers || this.#options.currentLayer >= this.#structure.size.y; + return !this.hasLayers || this.options.currentLayer >= this.getMaxLayer(); } isAtMinLayer() { - return !this.hasLayers || this.#options.currentLayer <= 0; + return !this.hasLayers || this.options.currentLayer <= 0; + } + + enable() { + this.options.enable(); + this.refreshBox(); + } + + disable() { + this.options.disable(); + this.refreshBox(); + } + + rename(newName) { + this.options.rename(newName); + } + + place(dimensionId, worldLocation) { + this.move(dimensionId, worldLocation); + this.enable(); + } + + move(dimensionId, worldLocation) { + this.options.move(dimensionId, worldLocation); + this.refreshBox(); + } + + setLayer(layer) { + if (layer < 0 || layer > this.getMaxLayer()) + throw new Error(`[StrucTool] Layer ${layer} is out of bounds.`); + this.options.setLayer(layer); + this.refreshBox(); + } + + setVerifierEnabled(enable) { + this.options.setVerifierEnabled(enable); + this.verifier.refresh(); + } + + setVerifierDistance(distance) { + this.options.setVerifierDistance(distance); + this.verifier.refresh(); } increaseLayer() { if (this.isAtMaxLayer()) this.setLayer(0); else - this.setLayer(this.#options.currentLayer + 1); + this.setLayer(this.options.currentLayer + 1); } decreaseLayer() { if (this.isAtMinLayer()) - this.setLayer(this.#structure.size.y); + this.setLayer(this.getMaxLayer()); else - this.setLayer(this.#options.currentLayer - 1); + this.setLayer(this.options.currentLayer - 1); } - getDimension() { - return world.getDimension(this.#options.dimensionId); + toGlobalCoords(structureLocation) { + return Vector.from(structureLocation).add(this.options.worldLocation); } - setVerifierOptions({ shouldRender, trackPlayerDistance, intervalOrLifetime }) { - this.#options.verifier.shouldRender = shouldRender; - this.#options.verifier.trackPlayerDistance = trackPlayerDistance; - this.#options.verifier.intervalOrLifetime = intervalOrLifetime; - this.updateOptions(); - this.verifier.setOptions(this.#options.verifier); - this.verifier.refresh(); + toStructureCoords(worldLocation) { + return Vector.from(worldLocation).subtract(this.options.worldLocation); } } \ No newline at end of file diff --git a/StrucTool [BP]/scripts/classes/StructureOutliner.js b/StrucTool [BP]/scripts/classes/StructureOutliner.js index a11cbc6..65648fa 100644 --- a/StrucTool [BP]/scripts/classes/StructureOutliner.js +++ b/StrucTool [BP]/scripts/classes/StructureOutliner.js @@ -30,7 +30,7 @@ export class StructureOutliner { this.outliner.stopDraw(); if (!this.instance.isEnabled()) return; - if (this.instance.isUsingLayers()) + if (this.instance.hasLayerSelected()) this.layeredDraw(); else this.boxDraw(); diff --git a/StrucTool [BP]/scripts/classes/StructureStatistics.js b/StrucTool [BP]/scripts/classes/StructureStatistics.js index d1612d4..3962cfb 100644 --- a/StrucTool [BP]/scripts/classes/StructureStatistics.js +++ b/StrucTool [BP]/scripts/classes/StructureStatistics.js @@ -31,7 +31,8 @@ export class StructureStatistics { } getNonAirBlocks() { - return this.instance.getActiveVolume() - this.verification.correctlyAir; + const activeBounds = this.instance.getActiveBounds(); + return activeBounds.min.volume(activeBounds.max) - this.verification.correctlyAir; } getStat(blockVerificationLevel) { @@ -45,7 +46,7 @@ export class StructureStatistics { getMessage() { let message = ''; message += `§fStatistics for §a${this.instance.name}§f:`; - if (this.instance.isUsingLayers()) + if (this.instance.hasLayerSelected()) message += ` §7(layer ${this.instance.getLayer()})`; message += `\n§7Blocks: §2${this.getNonAirBlocks()}\n`; const skipped = this.getSkipped(); diff --git a/StrucTool [BP]/scripts/classes/StructureVerifier.js b/StrucTool [BP]/scripts/classes/StructureVerifier.js index 8f4c2bb..3c02386 100644 --- a/StrucTool [BP]/scripts/classes/StructureVerifier.js +++ b/StrucTool [BP]/scripts/classes/StructureVerifier.js @@ -1,7 +1,7 @@ import { BlockVerifier } from "./BlockVerifier"; import { BlockVerificationLevel } from "./enums/BlockVerificationLevel"; import { BlockVerificationLevelRender } from "./BlockVerificationLevelRender"; -import { system, TicksPerSecond, world } from "@minecraft/server"; +import { system, TicksPerSecond } from "@minecraft/server"; const MIN_TRACK_PLAYER_DISTANCE = 0; const MAX_TRACK_PLAYER_DISTANCE = 7; @@ -9,18 +9,20 @@ const MIN_LIFETIME = 8; export class StructureVerifier { instance; - blockVerificationLevels; - shouldRender; - trackPlayerDistance; intervalOrLifetime; + + locationsToVerify; + blocksToVerify; + blockVerificationLevels; isBlockPopulationComplete; isVerificationComplete; #runner; - constructor(instance, { shouldRender = false, trackPlayerDistance = 1, intervalOrLifetime = 10 } = {}) { + constructor(instance, { isEnabled = false, trackPlayerDistance = 1, intervalOrLifetime = 10 } = {}) { this.instance = instance; this.intervalOrLifetime = Math.max(intervalOrLifetime, MIN_LIFETIME); - this.setOptions({ shouldRender, trackPlayerDistance }); + this.instance.options.setVerifierEnabled(isEnabled); + this.instance.options.setVerifierDistance(trackPlayerDistance); } startContinuousVerification() { @@ -43,6 +45,14 @@ export class StructureVerifier { this.startContinuousVerification(); } + isEnabled() { + return this.instance.options.verifier.isEnabled; + } + + getTrackPlayerDistance() { + return Math.min(MAX_TRACK_PLAYER_DISTANCE, Math.max(MIN_TRACK_PLAYER_DISTANCE, this.instance.options.trackPlayerDistance)); + } + init() { this.locationsToVerify = new Set(); this.blocksToVerify = []; @@ -51,18 +61,13 @@ export class StructureVerifier { this.isVerificationComplete = false; } - setOptions({ shouldRender = false, trackPlayerDistance = 0 }) { - this.blockVerificationLevels = { correctlyAir: 0 }; - this.shouldRender = shouldRender; - this.trackPlayerDistance = Math.min(trackPlayerDistance, MAX_TRACK_PLAYER_DISTANCE); - this.isVerificationComplete = false; - } - - async verifyStructure() { + async verifyStructure(shouldRender = true) { + if (!this.isEnabled()) + return; this.init(); return new Promise(async (resolve) => { await this.populateBlocksToVerify(); - system.runJob(this.verifyBlocks(this.blocksToVerify)); + system.runJob(this.verifyBlocks(this.blocksToVerify, shouldRender)); const checker = system.runInterval(() => { if (this.isVerificationComplete) { system.clearRun(checker); @@ -74,8 +79,10 @@ export class StructureVerifier { populateBlocksToVerify() { return new Promise((resolve) => { - if (this.trackPlayerDistance == 0) - return this.instance.getBlocks(); + if (this.getTrackPlayerDistance == 0) { + this.blocksToVerify = this.instance.getAllBlocks(); + resolve(); + } this.locationsToVerify = new Set(); for (const player of this.instance.getDimension().getPlayers()) system.runJob(this.populateActiveLocationsNearPlayer(player)); @@ -90,11 +97,12 @@ export class StructureVerifier { } *populateActiveLocationsNearPlayer(player) { - for (let x = -this.trackPlayerDistance; x < this.trackPlayerDistance; x++) { - for (let y = -this.trackPlayerDistance; y < this.trackPlayerDistance; y++) { - for (let z = -this.trackPlayerDistance; z < this.trackPlayerDistance; z++) { + const distance = this.getTrackPlayerDistance(); + for (let x = -distance; x < distance; x++) { + for (let y = -distance; y < distance; y++) { + for (let z = -distance; z < distance; z++) { const structureLocation = this.instance.toStructureCoords({ x: player.location.x + x, y: player.location.y + y, z: player.location.z + z }); - if (this.instance.isLocationActive(player.dimension.id, structureLocation, { useLayers: true })) { + if (this.instance.isLocationActive(player.dimension.id, structureLocation, { useActiveLayer: true })) { this.locationsToVerify.add({ x: structureLocation.x, y: structureLocation.y, z: structureLocation.z }); yield void 0; } @@ -104,14 +112,14 @@ export class StructureVerifier { this.isBlockPopulationComplete = true; } - *verifyBlocks(blocks) { + *verifyBlocks(blocks, shouldRender) { for (const block of blocks) { const verificationLevel = this.verifyBlock(block.location); if (verificationLevel === BlockVerificationLevel.Air) { this.blockVerificationLevels.correctlyAir++; } else { this.blockVerificationLevels[JSON.stringify(block.location)] = verificationLevel; - if (this.shouldRender) { + if (shouldRender) { const dimensionLocation = { dimension: this.instance.getDimension(), location: this.instance.toGlobalCoords(block.location) }; new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.intervalOrLifetime/TicksPerSecond); } diff --git a/StrucTool [BP]/scripts/classes/enums/InstanceEditOptions.js b/StrucTool [BP]/scripts/classes/enums/InstanceEditButtons.js similarity index 88% rename from StrucTool [BP]/scripts/classes/enums/InstanceEditOptions.js rename to StrucTool [BP]/scripts/classes/enums/InstanceEditButtons.js index 7071640..1fafa5b 100644 --- a/StrucTool [BP]/scripts/classes/enums/InstanceEditOptions.js +++ b/StrucTool [BP]/scripts/classes/enums/InstanceEditButtons.js @@ -1,4 +1,4 @@ -export const InstanceEditOptions = Object.freeze({ +export const InstanceEditButtons = Object.freeze({ Unknown: 'Unknown', MainMenu: '<<', Place: '§aPlace Instance', diff --git a/StrucTool [BP]/scripts/lib/Vector.js b/StrucTool [BP]/scripts/lib/Vector.js index 85fe960..1245a38 100644 --- a/StrucTool [BP]/scripts/lib/Vector.js +++ b/StrucTool [BP]/scripts/lib/Vector.js @@ -1,6 +1,5 @@ /** - * Part of ItemStack Database by @gameza_src - * Unknown author + * Unknown author, with additions. */ const isVec3Symbol = Symbol("isVec3"); export function Vector(x = 0, y = 0, z = 0) { @@ -23,6 +22,7 @@ Vector.multiply = function multiply(vec, num) { } Vector.isVec3 = function isVec3(vec) { return vec[isVec3Symbol] === true; } Vector.floor = function floor(vec) { return { x: Math.floor(vec.x), y: Math.floor(vec.y), z: Math.floor(vec.z), __proto__: Vector.prototype }; } +Vector.volume = function volume(a, b) { const [min, max] = Vector.sort(a, b); return (max.x - min.x) * (max.y - min.y) * (max.z - min.z); } Vector.projection = function projection(a, b) { return Vector.multiply(b, Vector.dot(a, b) / ((b.x * b.x + b.y * b.y + b.z * b.z) ** 2)); } Vector.rejection = function rejection(a, b) { return Vector.subtract(a, Vector.projection(a, b)); } Vector.reflect = function reflect(v, n) { return Vector.subtract(v, Vector.multiply(n, 2 * Vector.dot(v, n))); } @@ -56,6 +56,7 @@ Vector.prototype = { cross(vec) { return Vector.cross(this, vec); }, dot(vec) { return Vector.dot(this, vec); }, floor() { return Vector.floor(this); }, + volume(vec) { return Vector.volume(this, vec); }, add(vec) { return Vector.add(this, vec); }, subtract(vec) { return Vector.subtract(this, vec); }, multiply(num) { return Vector.multiply(this, num); },