Files
Construct-CN/StrucTool [BP]/scripts/classes/StructureInstance.js
T
2025-04-17 21:16:13 -07:00

294 lines
9.2 KiB
JavaScript

import { world } from "@minecraft/server";
import { Vector } from "../lib/Vector";
import { StructureOutliner } from "./StructureOutliner";
import { StructureVerifier } from "./StructureVerifier";
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;
verifier = 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.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;
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();
}
refreshBox() {
if (!this.hasLocation())
return;
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.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;
}
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;
}
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);
}
toGlobalCoords(structureLocation) {
return new Vector(
this.#options.worldLocation.x + structureLocation.x,
this.#options.worldLocation.y + structureLocation.y,
this.#options.worldLocation.z + structureLocation.z
);
}
toStructureCoords(worldLocation) {
return new Vector(
worldLocation.x - this.#options.worldLocation.x,
worldLocation.y - this.#options.worldLocation.y,
worldLocation.z - this.#options.worldLocation.z
);
}
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;
}
hasLayers() {
return this.#structure.size.y > 1;
}
isAtMaxLayer() {
return !this.hasLayers || this.#options.currentLayer >= this.#structure.size.y;
}
isAtMinLayer() {
return !this.hasLayers || this.#options.currentLayer <= 0;
}
increaseLayer() {
if (this.isAtMaxLayer())
this.setLayer(0);
else
this.setLayer(this.#options.currentLayer + 1);
}
decreaseLayer() {
if (this.isAtMinLayer())
this.setLayer(this.#structure.size.y);
else
this.setLayer(this.#options.currentLayer - 1);
}
getDimension() {
return world.getDimension(this.#options.dimensionId);
}
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();
}
}