Merge branch 'StructureVerifier'
This commit is contained in:
@@ -14,7 +14,7 @@ class BlockInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static showStructureBlockInfo(player) {
|
static showStructureBlockInfo(player) {
|
||||||
const block = Raycaster.getTargetedStructureBlock(player, { isFirst: true, collideWithWorldBlocks: true, useLayers: false });
|
const block = Raycaster.getTargetedStructureBlock(player, { isFirst: true, collideWithWorldBlocks: true, useActiveLayer: true });
|
||||||
if (!block && this.shownToLastTick.has(player.id)) {
|
if (!block && this.shownToLastTick.has(player.id)) {
|
||||||
player.onScreenDisplay.setActionBar({ text: 'Structure:\n§7None' });
|
player.onScreenDisplay.setActionBar({ text: 'Structure:\n§7None' });
|
||||||
this.shownToLastTick.delete(player.id);
|
this.shownToLastTick.delete(player.id);
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import { Vector } from "../lib/Vector";
|
|||||||
|
|
||||||
export class BlockVerificationLevelRender {
|
export class BlockVerificationLevelRender {
|
||||||
opacity = 0.2;
|
opacity = 0.2;
|
||||||
lifetime = 0;
|
lifetimeSeconds = 0;
|
||||||
|
|
||||||
constructor(dimensionLocation, verificationLevel, lifetime = 5) {
|
constructor(dimensionLocation, verificationLevel, lifetimeSeconds = 5) {
|
||||||
this.dimension = dimensionLocation.dimension;
|
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.verificationLevel = verificationLevel;
|
||||||
this.lifetime = lifetime;
|
this.lifetimeSeconds = lifetimeSeconds;
|
||||||
this.renderBlock();
|
this.renderBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ export class BlockVerificationLevelRender {
|
|||||||
const color = this.getRGBAMolang();
|
const color = this.getRGBAMolang();
|
||||||
if (!color)
|
if (!color)
|
||||||
return;
|
return;
|
||||||
color.setFloat("lifetime", this.lifetime);
|
color.setFloat("lifetime", this.lifetimeSeconds);
|
||||||
try {
|
try {
|
||||||
this.dimension.spawnParticle(particleLocation.particleType, particleLocation.location, color);
|
this.dimension.spawnParticle(particleLocation.particleType, particleLocation.location, color);
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { structureCollection } from './StructureCollection';
|
import { structureCollection } from './StructureCollection';
|
||||||
import { MenuForm } from '../classes/MenuForm';
|
import { MenuForm } from '../classes/MenuForm';
|
||||||
import { InstanceEditOptions } from './enums/InstanceEditOptions';
|
import { forceShow } from '../utils';
|
||||||
|
import { InstanceEditButtons } from './enums/InstanceEditButtons';
|
||||||
import { InstanceEditFormBuilder } from './InstanceEditFormBuilder';
|
import { InstanceEditFormBuilder } from './InstanceEditFormBuilder';
|
||||||
import { FormCancelationReason } from '@minecraft/server-ui';
|
import { FormCancelationReason } from '@minecraft/server-ui';
|
||||||
|
|
||||||
@@ -8,25 +9,26 @@ export class InstanceEditForm {
|
|||||||
instanceName;
|
instanceName;
|
||||||
#buttons = {
|
#buttons = {
|
||||||
isEnabled: [
|
isEnabled: [
|
||||||
InstanceEditOptions.NextLayer,
|
InstanceEditButtons.NextLayer,
|
||||||
InstanceEditOptions.PreviousLayer,
|
InstanceEditButtons.PreviousLayer,
|
||||||
InstanceEditOptions.SetLayer,
|
InstanceEditButtons.SetLayer,
|
||||||
InstanceEditOptions.Move,
|
InstanceEditButtons.Move,
|
||||||
InstanceEditOptions.Statistics,
|
InstanceEditButtons.Statistics,
|
||||||
InstanceEditOptions.RenameInstance,
|
InstanceEditButtons.Settings,
|
||||||
InstanceEditOptions.DisableInstance,
|
InstanceEditButtons.Rename,
|
||||||
|
InstanceEditButtons.Disable,
|
||||||
],
|
],
|
||||||
isNotEnabledAndIsNotPlaced: [
|
isNotEnabledAndIsNotPlaced: [
|
||||||
InstanceEditOptions.PlaceInstance,
|
InstanceEditButtons.Place,
|
||||||
InstanceEditOptions.RenameInstance
|
InstanceEditButtons.Rename
|
||||||
],
|
],
|
||||||
isNotEnabledButIsPlaced: [
|
isNotEnabledButIsPlaced: [
|
||||||
InstanceEditOptions.EnableInstance,
|
InstanceEditButtons.Enable,
|
||||||
InstanceEditOptions.RenameInstance
|
InstanceEditButtons.Rename
|
||||||
],
|
],
|
||||||
common: [
|
common: [
|
||||||
InstanceEditOptions.DeleteInstance,
|
InstanceEditButtons.Delete,
|
||||||
InstanceEditOptions.MainMenu
|
InstanceEditButtons.MainMenu
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +41,7 @@ export class InstanceEditForm {
|
|||||||
|
|
||||||
show() {
|
show() {
|
||||||
const currentOptions = this.getActiveOptions();
|
const currentOptions = this.getActiveOptions();
|
||||||
InstanceEditFormBuilder.buildInstance(this.instance, currentOptions).show(this.player).then((response) => {
|
forceShow(this.player, InstanceEditFormBuilder.buildInstance(this.instance, currentOptions)).then((response) => {
|
||||||
if (response.canceled) return;
|
if (response.canceled) return;
|
||||||
this.handleOption(currentOptions[response.selection]);
|
this.handleOption(currentOptions[response.selection]);
|
||||||
});
|
});
|
||||||
@@ -57,50 +59,53 @@ export class InstanceEditForm {
|
|||||||
|
|
||||||
if (!this.instance.hasLayers())
|
if (!this.instance.hasLayers())
|
||||||
currentOptions = currentOptions.filter(option =>
|
currentOptions = currentOptions.filter(option =>
|
||||||
option !== InstanceEditOptions.SetLayer
|
option !== InstanceEditButtons.SetLayer
|
||||||
&& option !== InstanceEditOptions.NextLayer
|
&& option !== InstanceEditButtons.NextLayer
|
||||||
&& option !== InstanceEditOptions.PreviousLayer
|
&& option !== InstanceEditButtons.PreviousLayer
|
||||||
);
|
);
|
||||||
return currentOptions;
|
return currentOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOption(option) {
|
handleOption(option) {
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case InstanceEditOptions.EnableInstance:
|
case InstanceEditButtons.Enable:
|
||||||
this.instance.enable();
|
this.instance.enable();
|
||||||
break;
|
break;
|
||||||
case InstanceEditOptions.DisableInstance:
|
case InstanceEditButtons.Disable:
|
||||||
this.instance.disable();
|
this.instance.disable();
|
||||||
break;
|
break;
|
||||||
case InstanceEditOptions.PlaceInstance:
|
case InstanceEditButtons.Place:
|
||||||
this.instance.place(this.player.dimension.id, this.player.location);
|
this.instance.place(this.player.dimension.id, this.player.location);
|
||||||
break;
|
break;
|
||||||
case InstanceEditOptions.RenameInstance:
|
case InstanceEditButtons.Rename:
|
||||||
this.renameInstanceForm();
|
this.renameInstanceForm();
|
||||||
break;
|
break;
|
||||||
case InstanceEditOptions.DeleteInstance:
|
case InstanceEditButtons.Delete:
|
||||||
structureCollection.delete(this.instanceName);
|
structureCollection.delete(this.instanceName);
|
||||||
break;
|
break;
|
||||||
case InstanceEditOptions.NextLayer:
|
case InstanceEditButtons.NextLayer:
|
||||||
this.instance.increaseLayer();
|
this.instance.increaseLayer();
|
||||||
new InstanceEditForm(this.player, this.instanceName);
|
new InstanceEditForm(this.player, this.instanceName);
|
||||||
break;
|
break;
|
||||||
case InstanceEditOptions.PreviousLayer:
|
case InstanceEditButtons.PreviousLayer:
|
||||||
this.instance.decreaseLayer();
|
this.instance.decreaseLayer();
|
||||||
new InstanceEditForm(this.player, this.instanceName);
|
new InstanceEditForm(this.player, this.instanceName);
|
||||||
break;
|
break;
|
||||||
case InstanceEditOptions.SetLayer:
|
case InstanceEditButtons.SetLayer:
|
||||||
this.setLayerForm();
|
this.setLayerForm();
|
||||||
break;
|
break;
|
||||||
case InstanceEditOptions.Move:
|
case InstanceEditButtons.Move:
|
||||||
this.instance.move(this.player.dimension.id, this.player.location);
|
this.instance.move(this.player.dimension.id, this.player.location);
|
||||||
break;
|
break;
|
||||||
case InstanceEditOptions.Statistics:
|
case InstanceEditButtons.Statistics:
|
||||||
this.statisticsForm();
|
this.statisticsForm();
|
||||||
break;
|
break;
|
||||||
case InstanceEditOptions.MainMenu:
|
case InstanceEditButtons.MainMenu:
|
||||||
new MenuForm(this.player, { jumpToInstance: false });
|
new MenuForm(this.player, { jumpToInstance: false });
|
||||||
break;
|
break;
|
||||||
|
case InstanceEditButtons.Settings:
|
||||||
|
this.settingsForm();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
this.player.sendMessage(`§cUnknown option: ${option}`);
|
this.player.sendMessage(`§cUnknown option: ${option}`);
|
||||||
break;
|
break;
|
||||||
@@ -130,8 +135,7 @@ export class InstanceEditForm {
|
|||||||
InstanceEditFormBuilder.buildSetLayer(this.instance.getBounds().max.y, this.instance.getLayer()).show(this.player).then((response) => {
|
InstanceEditFormBuilder.buildSetLayer(this.instance.getBounds().max.y, this.instance.getLayer()).show(this.player).then((response) => {
|
||||||
if (response.canceled)
|
if (response.canceled)
|
||||||
return;
|
return;
|
||||||
const selectedLayer = response.formValues[0];
|
this.instance.setLayer(parseInt(response.formValues[0]));
|
||||||
this.instance.setLayer(parseInt(selectedLayer));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,4 +146,12 @@ export class InstanceEditForm {
|
|||||||
this.player.sendMessage(statsForm.stats);
|
this.player.sendMessage(statsForm.stats);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settingsForm() {
|
||||||
|
InstanceEditFormBuilder.buildSettings(this.instance).show(this.player).then((response) => {
|
||||||
|
if (response.canceled)
|
||||||
|
return;
|
||||||
|
this.instance.setVerifierEnabled(response.formValues[0]);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,13 +2,14 @@ import { ActionFormData, ModalFormData } from '@minecraft/server-ui';
|
|||||||
import { MenuFormBuilder } from './MenuFormBuilder';
|
import { MenuFormBuilder } from './MenuFormBuilder';
|
||||||
import { StructureVerifier } from './StructureVerifier';
|
import { StructureVerifier } from './StructureVerifier';
|
||||||
import { StructureStatistics } from './StructureStatistics';
|
import { StructureStatistics } from './StructureStatistics';
|
||||||
|
import { TicksPerSecond } from '@minecraft/server';
|
||||||
|
|
||||||
export class InstanceEditFormBuilder {
|
export class InstanceEditFormBuilder {
|
||||||
static buildInstance(instance, options) {
|
static buildInstance(instance, options) {
|
||||||
const location = instance.getLocation();
|
const location = instance.getLocation();
|
||||||
const form = new ActionFormData()
|
const form = new ActionFormData()
|
||||||
.title(MenuFormBuilder.menuTitle)
|
.title(MenuFormBuilder.menuTitle)
|
||||||
let body = `Instance: §a${instance.name}\n§fStructure: §2${instance.getStructureId()}\n`;
|
let body = `Instance: §a${instance.getName()}\n§fStructure: §2${instance.getStructureId()}\n`;
|
||||||
if (instance.hasLocation())
|
if (instance.hasLocation())
|
||||||
body += `§7(${location.location.x} ${location.location.y} ${location.location.z} in ${location.dimensionId})\n`;
|
body += `§7(${location.location.x} ${location.location.y} ${location.location.z} in ${location.dimensionId})\n`;
|
||||||
form.body(body);
|
form.body(body);
|
||||||
@@ -36,11 +37,18 @@ export class InstanceEditFormBuilder {
|
|||||||
static async buildStatistics(instance) {
|
static async buildStatistics(instance) {
|
||||||
const buildStatisticsForm = new ActionFormData()
|
const buildStatisticsForm = new ActionFormData()
|
||||||
.title(MenuFormBuilder.menuTitle)
|
.title(MenuFormBuilder.menuTitle)
|
||||||
const structureVerifier = new StructureVerifier(instance);
|
const structureVerifier = new StructureVerifier(instance, { isEnabled: true, trackPlayerDistance: 0, intervalOrLifetime: 30 * TicksPerSecond });
|
||||||
const verification = await structureVerifier.verifyStructure();
|
const verification = await structureVerifier.verifyStructure();
|
||||||
const statistics = new StructureStatistics(instance, verification);
|
const statistics = new StructureStatistics(instance, verification);
|
||||||
const statsMessage = statistics.getMessage();
|
const statsMessage = statistics.getMessage();
|
||||||
buildStatisticsForm.body(statsMessage);
|
buildStatisticsForm.body(statsMessage);
|
||||||
return { form: buildStatisticsForm, stats: statsMessage };
|
return { form: buildStatisticsForm, stats: statsMessage };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static buildSettings(instance) {
|
||||||
|
return new ModalFormData()
|
||||||
|
.title(MenuFormBuilder.menuTitle)
|
||||||
|
.toggle('Toggle block validation.', instance.options.verifier.isEnabled)
|
||||||
|
.submitButton('§aApply');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import { Vector } from "../lib/Vector";
|
||||||
|
import { world } from "@minecraft/server";
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
static getInstanceStrucetureId(instanceName) {
|
||||||
|
const options = new InstanceOptions(instanceName, void 0);
|
||||||
|
return options.structureId;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ class MaterialCounter {
|
|||||||
static getAll(name) {
|
static getAll(name) {
|
||||||
const structure = structureCollection.get(name);
|
const structure = structureCollection.get(name);
|
||||||
const materials = {};
|
const materials = {};
|
||||||
for (const block of structure.getBlocks()) {
|
for (const block of structure.getAllBlocks()) {
|
||||||
const typeId = block?.getItemStack()?.typeId.replace('minecraft:', '');
|
const typeId = block?.getItemStack()?.typeId.replace('minecraft:', '');
|
||||||
if (!typeId) continue;
|
if (!typeId) continue;
|
||||||
if (!materials[typeId]) {
|
if (!materials[typeId]) {
|
||||||
@@ -32,7 +32,7 @@ class MaterialCounter {
|
|||||||
static getPrintable(name) {
|
static getPrintable(name) {
|
||||||
const structure = structureCollection.get(name);
|
const structure = structureCollection.get(name);
|
||||||
const materials = {};
|
const materials = {};
|
||||||
for (const block of structure.getBlocks()) {
|
for (const block of structure.getAllBlocks()) {
|
||||||
const itemStack = block?.getItemStack();
|
const itemStack = block?.getItemStack();
|
||||||
const typeId = itemStack?.typeId.replace('minecraft:', '');
|
const typeId = itemStack?.typeId.replace('minecraft:', '');
|
||||||
if (!typeId) continue;
|
if (!typeId) continue;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export class MenuForm {
|
|||||||
async show(jumpToInstance = true) {
|
async show(jumpToInstance = true) {
|
||||||
let instanceName;
|
let instanceName;
|
||||||
if (jumpToInstance) {
|
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 })?.getName();
|
||||||
if (instanceName) {
|
if (instanceName) {
|
||||||
new InstanceEditForm(this.player, instanceName);
|
new InstanceEditForm(this.player, instanceName);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ export class Outliner {
|
|||||||
|
|
||||||
constructor(dimension, min, max) {
|
constructor(dimension, min, max) {
|
||||||
this.dimension = dimension;
|
this.dimension = dimension;
|
||||||
this.min = new Vector(min.x, min.y, min.z);
|
this.min = Vector.from(min);
|
||||||
this.max = new Vector(max.x, max.y, max.z);
|
this.max = Vector.from(max);
|
||||||
this.vertices = this.getVertices(min, max);
|
this.vertices = this.getVertices(min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,8 +65,8 @@ export class Outliner {
|
|||||||
|
|
||||||
setVertices(dimension, min, max) {
|
setVertices(dimension, min, max) {
|
||||||
this.dimension = dimension;
|
this.dimension = dimension;
|
||||||
this.min = new Vector(min.x, min.y, min.z);
|
this.min = Vector.from(min);
|
||||||
this.max = new Vector(max.x, max.y, max.z);
|
this.max = Vector.from(max);
|
||||||
this.vertices = this.getVertices(min, max);
|
this.vertices = this.getVertices(min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ export class Outliner {
|
|||||||
|
|
||||||
addStandaloneParticles(locations) {
|
addStandaloneParticles(locations) {
|
||||||
for (const location of locations)
|
for (const location of locations)
|
||||||
this.vertices.push(new Vector(location.x, location.y, location.z));
|
this.vertices.push(Vector.from(location));
|
||||||
}
|
}
|
||||||
|
|
||||||
getNextParticleColor() {
|
getNextParticleColor() {
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import { world } from "@minecraft/server";
|
|||||||
export class Raycaster {
|
export class Raycaster {
|
||||||
static STEP_SIZE = 0.2;
|
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
|
// Can probably be optimized by the fact that we only need full blocks and aren't checking for partial blocks
|
||||||
const blocks = [];
|
const blocks = [];
|
||||||
let location = startLocation;
|
let location = startLocation;
|
||||||
let distance = 0;
|
let distance = 0;
|
||||||
while (distance < maxDistance) {
|
while (distance < maxDistance) {
|
||||||
const structure = structureCollection.getStructure(dimension.id, location, { useLayers });
|
const structure = structureCollection.getStructure(dimension.id, location, { useActiveLayer });
|
||||||
if (structure) {
|
if (structure) {
|
||||||
const block = structure.getBlock(structure.toStructureCoords(location));
|
const block = structure.getBlock(structure.toStructureCoords(location));
|
||||||
if (block?.type.id !== 'minecraft:air') {
|
if (block?.type.id !== 'minecraft:air') {
|
||||||
@@ -41,11 +41,11 @@ export class Raycaster {
|
|||||||
return blocks;
|
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 startLocation = player.getHeadLocation();
|
||||||
const direction = player.getViewDirection();
|
const direction = player.getViewDirection();
|
||||||
const maxDistance = 7;
|
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)
|
if (blocks.length === 0)
|
||||||
return void 0;
|
return void 0;
|
||||||
return isFirst ? blocks[0] : blocks[blocks.length - 1];
|
return isFirst ? blocks[0] : blocks[blocks.length - 1];
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
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(layer) {
|
||||||
|
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: layer, z });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*getAllBlocks() {
|
||||||
|
for (let y = 0; y < this.#structure.size.y; y++) {
|
||||||
|
yield * this.getLayerBlocks(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getLayerLocations(layer) {
|
||||||
|
const locations = new Set();
|
||||||
|
for (let x = 0; x < this.#structure.size.x; x++) {
|
||||||
|
for (let z = 0; z < this.#structure.size.z; z++) {
|
||||||
|
locations.add(new Vector(x, layer, z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllLocations() {
|
||||||
|
const locations = new Set();
|
||||||
|
for (let y = 0; y < this.#structure.size.y; y++) {
|
||||||
|
const layerLocations = this.getLayerLocations(y);
|
||||||
|
for (const location of layerLocations) {
|
||||||
|
locations.add(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { InstanceOptions } from './InstanceOptions';
|
||||||
import { StructureInstance } from './StructureInstance';
|
import { StructureInstance } from './StructureInstance';
|
||||||
import { world } from '@minecraft/server';
|
import { world } from '@minecraft/server';
|
||||||
|
|
||||||
@@ -8,12 +9,12 @@ class StructureCollection {
|
|||||||
this.structures = {};
|
this.structures = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
loadExistingStructures() {
|
loadExistingInstances() {
|
||||||
world.getDynamicPropertyIds().filter(id => id.startsWith('structOptions:')).forEach(id => {
|
world.getDynamicPropertyIds().filter(id => id.startsWith('instanceOptions:')).forEach(id => {
|
||||||
const instanceName = id.replace('structOptions:', '');
|
const instanceName = id.replace('instanceOptions:', '');
|
||||||
let structureId;
|
let structureId;
|
||||||
try {
|
try {
|
||||||
structureId = StructureInstance.parseOptions(instanceName).structureId;
|
structureId = InstanceOptions.getInstanceStrucetureId(instanceName);
|
||||||
this.structures[instanceName] = new StructureInstance(instanceName, structureId);
|
this.structures[instanceName] = new StructureInstance(instanceName, structureId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
world.sendMessage(`§c[StrucTool] Error loading structure instance '${instanceName}'. It will be removed.`);
|
world.sendMessage(`§c[StrucTool] Error loading structure instance '${instanceName}'. It will be removed.`);
|
||||||
@@ -95,5 +96,5 @@ class StructureCollection {
|
|||||||
export const structureCollection = new StructureCollection();
|
export const structureCollection = new StructureCollection();
|
||||||
|
|
||||||
world.afterEvents.worldLoad.subscribe(() => {
|
world.afterEvents.worldLoad.subscribe(() => {
|
||||||
structureCollection.loadExistingStructures();
|
structureCollection.loadExistingInstances();
|
||||||
});
|
});
|
||||||
@@ -1,181 +1,29 @@
|
|||||||
import { world } from "@minecraft/server";
|
import { Vector } from "../lib/Vector";
|
||||||
import { Outliner } from "./Outliner";
|
|
||||||
import { StructureOutliner } from "./StructureOutliner";
|
import { StructureOutliner } from "./StructureOutliner";
|
||||||
import { StructureVerifier } from "./StructureVerifier";
|
import { StructureVerifier } from "./StructureVerifier";
|
||||||
|
import { InstanceOptions } from "./InstanceOptions";
|
||||||
|
import { Structure } from "./Structure";
|
||||||
|
import { TicksPerSecond } from "@minecraft/server";
|
||||||
|
|
||||||
export class StructureInstance {
|
export class StructureInstance {
|
||||||
name;
|
options;
|
||||||
#structure;
|
structure = void 0;
|
||||||
#options = {
|
|
||||||
structureId: void 0,
|
|
||||||
isEnabled: false,
|
|
||||||
dimensionId: void 0,
|
|
||||||
worldLocation: { x: 0, y: 0, z: 0 },
|
|
||||||
rotation: 0,
|
|
||||||
mirror: false,
|
|
||||||
currentLayer: 0
|
|
||||||
};
|
|
||||||
outliner = void 0;
|
|
||||||
verifier = void 0;
|
verifier = void 0;
|
||||||
|
outliner = void 0;
|
||||||
|
|
||||||
constructor(instanceName, structureId) {
|
constructor(instanceName, structureId) {
|
||||||
this.name = instanceName;
|
this.structure = new Structure(structureId);
|
||||||
this.#structure = world.structureManager.get(structureId);
|
this.options = new InstanceOptions(instanceName, 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();
|
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() {
|
delete() {
|
||||||
this.disable();
|
this.disable();
|
||||||
world.setDynamicProperty(`structOptions:${this.name}`, void 0);
|
delete this.options;
|
||||||
this.#structure = void 0;
|
delete this.structure;
|
||||||
this.#options = void 0;
|
|
||||||
delete this.outliner;
|
delete this.outliner;
|
||||||
}
|
delete this.verifier;
|
||||||
|
this.options.clear();
|
||||||
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) {
|
|
||||||
dimension = world.getDimension("minecraft:overworld");
|
|
||||||
}
|
|
||||||
return dimension;
|
|
||||||
}
|
|
||||||
|
|
||||||
getBlock(structureLocation) {
|
|
||||||
return this.#structure.getBlockPermutation(structureLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
*getBlocks() {
|
|
||||||
const max = this.#structure.size;
|
|
||||||
for (let y = 0; y < max.y; y++) {
|
|
||||||
yield * this.getLayerBlocks(y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*getLayerBlocks(y) {
|
|
||||||
const max = this.#structure.size;
|
|
||||||
for (let x = 0; x < max.x; x++) {
|
|
||||||
for (let z = 0; z < max.z; z++) {
|
|
||||||
const blockPermutation = this.#structure.getBlockPermutation({ x, y, z });
|
|
||||||
if (!blockPermutation)
|
|
||||||
yield void 0;
|
|
||||||
blockPermutation.location = { x, y, z };
|
|
||||||
yield blockPermutation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getBounds() {
|
|
||||||
return {
|
|
||||||
min: { x: 0, y: 0, z: 0 },
|
|
||||||
max: this.#structure.size
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getLayeredBounds() {
|
|
||||||
if (!this.#options.isEnabled)
|
|
||||||
throw new Error(`[StrucTool] Instance '${this.name}' is not placed.`);
|
|
||||||
return {
|
|
||||||
min: { x: 0, y: this.#options.currentLayer - 1, z: 0 },
|
|
||||||
max: { x: this.#structure.size.x, y: this.#options.currentLayer, z: 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() {
|
refreshBox() {
|
||||||
@@ -184,92 +32,193 @@ export class StructureInstance {
|
|||||||
if (!this.outliner)
|
if (!this.outliner)
|
||||||
this.outliner = new StructureOutliner(this);
|
this.outliner = new StructureOutliner(this);
|
||||||
if (!this.verifier)
|
if (!this.verifier)
|
||||||
this.verifier = new StructureVerifier(this, { shouldRender: true });
|
this.verifier = new StructureVerifier(this, { isEnabled: this.options.verifier.isEnabled, trackPlayerDistance: this.options.verifier.trackPlayerDistance });
|
||||||
this.outliner.refresh();
|
this.outliner.refresh();
|
||||||
this.verifier.refresh();
|
this.verifier.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
isLocationInStructure(dimensionId, structureLocation) {
|
getName() {
|
||||||
if (this.#options.dimensionId !== dimensionId)
|
return this.options.instanceName;
|
||||||
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) {
|
getStructureId() {
|
||||||
if (!this.#options.isEnabled || this.#options.dimensionId !== dimensionId)
|
return this.options.structureId;
|
||||||
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 } = {}) {
|
getLocation() {
|
||||||
if (!this.#options.isEnabled || this.#options.dimensionId !== dimensionId)
|
return { dimensionId: this.options.dimensionId, location: this.options.worldLocation };
|
||||||
return false
|
|
||||||
if (useLayers && this.#options.currentLayer !== 0)
|
|
||||||
return this.isLocationInLayer(dimensionId, structureLocation);
|
|
||||||
return this.isLocationInStructure(dimensionId, structureLocation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toGlobalCoords(structureLocation) {
|
getDimension() {
|
||||||
|
return this.options.getDimension();
|
||||||
|
}
|
||||||
|
|
||||||
|
getMaxLayer() {
|
||||||
|
return this.structure.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
getLayer() {
|
||||||
|
return this.options.currentLayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBounds() {
|
||||||
return {
|
return {
|
||||||
x: this.#options.worldLocation.x + structureLocation.x,
|
min: this.structure.getMin(),
|
||||||
y: this.#options.worldLocation.y + structureLocation.y,
|
max: this.structure.getMax()
|
||||||
z: this.#options.worldLocation.z + structureLocation.z
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
toStructureCoords(worldLocation) {
|
getBlock(structureLocation) {
|
||||||
return {
|
return this.structure.getBlock(structureLocation);
|
||||||
x: worldLocation.x - this.#options.worldLocation.x,
|
}
|
||||||
y: worldLocation.y - this.#options.worldLocation.y,
|
|
||||||
z: worldLocation.z - this.#options.worldLocation.z
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllActiveLocations() {
|
||||||
|
if (!this.options.isEnabled)
|
||||||
|
throw new Error(`[StrucTool] Instance '${this.options.instanceName}' is not placed.`);
|
||||||
|
if (this.hasLayerSelected())
|
||||||
|
return this.structure.getLayerLocations(this.getLayer()-1);
|
||||||
|
else
|
||||||
|
return this.structure.getAllLocations();
|
||||||
}
|
}
|
||||||
|
|
||||||
isEnabled() {
|
isEnabled() {
|
||||||
return this.#options.isEnabled;
|
return this.options.isEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasLocation() {
|
hasLocation() {
|
||||||
return this.#options.dimensionId && this.#options.worldLocation.x !== 0 && this.#options.worldLocation.y !== 0 && this.#options.worldLocation.z !== 0;
|
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() {
|
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() {
|
isAtMaxLayer() {
|
||||||
return !this.hasLayers || this.#options.currentLayer >= this.#structure.size.y;
|
return !this.hasLayers || this.options.currentLayer >= this.getMaxLayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
isAtMinLayer() {
|
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);
|
||||||
|
if (this.options.verifier.trackPlayerDistance === 0) {
|
||||||
|
const bounds = this.getBounds();
|
||||||
|
this.options.verifier.intervalOrLifetime = Math.max(bounds.min.volume(bounds.max) / TicksPerSecond, 2*TicksPerSecond);
|
||||||
|
} else {
|
||||||
|
this.options.verifier.intervalOrLifetime = 10;
|
||||||
|
}
|
||||||
|
this.verifier.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
increaseLayer() {
|
increaseLayer() {
|
||||||
if (this.isAtMaxLayer())
|
if (this.isAtMaxLayer())
|
||||||
this.setLayer(0);
|
this.setLayer(0);
|
||||||
else
|
else
|
||||||
this.setLayer(this.#options.currentLayer + 1);
|
this.setLayer(this.options.currentLayer + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
decreaseLayer() {
|
decreaseLayer() {
|
||||||
if (this.isAtMinLayer())
|
if (this.isAtMinLayer())
|
||||||
this.setLayer(this.#structure.size.y);
|
this.setLayer(this.getMaxLayer());
|
||||||
else
|
else
|
||||||
this.setLayer(this.#options.currentLayer - 1);
|
this.setLayer(this.options.currentLayer - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDimension() {
|
toGlobalCoords(structureLocation) {
|
||||||
return world.getDimension(this.#options.dimensionId);
|
return Vector.from(structureLocation).add(this.options.worldLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
toStructureCoords(worldLocation) {
|
||||||
|
return Vector.from(worldLocation).subtract(this.options.worldLocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ export class StructureOutliner {
|
|||||||
this.outliner.stopDraw();
|
this.outliner.stopDraw();
|
||||||
if (!this.instance.isEnabled())
|
if (!this.instance.isEnabled())
|
||||||
return;
|
return;
|
||||||
if (this.instance.isUsingLayers())
|
if (this.instance.hasLayerSelected())
|
||||||
this.layeredDraw();
|
this.layeredDraw();
|
||||||
else
|
else
|
||||||
this.boxDraw();
|
this.boxDraw();
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ export class StructureStatistics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getNonAirBlocks() {
|
getNonAirBlocks() {
|
||||||
return this.instance.getActiveVolume() - this.verification.correctlyAir;
|
const activeBounds = this.instance.getActiveBounds();
|
||||||
|
return activeBounds.min.volume(activeBounds.max) - this.verification.correctlyAir;
|
||||||
}
|
}
|
||||||
|
|
||||||
getStat(blockVerificationLevel) {
|
getStat(blockVerificationLevel) {
|
||||||
@@ -44,8 +45,8 @@ export class StructureStatistics {
|
|||||||
|
|
||||||
getMessage() {
|
getMessage() {
|
||||||
let message = '';
|
let message = '';
|
||||||
message += `§fStatistics for §a${this.instance.name}§f:`;
|
message += `§fStatistics for §a${this.instance.getName()}§f:`;
|
||||||
if (this.instance.isUsingLayers())
|
if (this.instance.hasLayerSelected())
|
||||||
message += ` §7(layer ${this.instance.getLayer()})`;
|
message += ` §7(layer ${this.instance.getLayer()})`;
|
||||||
message += `\n§7Blocks: §2${this.getNonAirBlocks()}\n`;
|
message += `\n§7Blocks: §2${this.getNonAirBlocks()}\n`;
|
||||||
const skipped = this.getSkipped();
|
const skipped = this.getSkipped();
|
||||||
|
|||||||
@@ -2,23 +2,37 @@ import { BlockVerifier } from "./BlockVerifier";
|
|||||||
import { BlockVerificationLevel } from "./enums/BlockVerificationLevel";
|
import { BlockVerificationLevel } from "./enums/BlockVerificationLevel";
|
||||||
import { BlockVerificationLevelRender } from "./BlockVerificationLevelRender";
|
import { BlockVerificationLevelRender } from "./BlockVerificationLevelRender";
|
||||||
import { system, TicksPerSecond } from "@minecraft/server";
|
import { system, TicksPerSecond } from "@minecraft/server";
|
||||||
|
import { Vector } from "../lib/Vector";
|
||||||
|
|
||||||
|
const MIN_TRACK_PLAYER_DISTANCE = 0;
|
||||||
|
const MAX_TRACK_PLAYER_DISTANCE = 7;
|
||||||
|
const MIN_LIFETIME = 8;
|
||||||
|
|
||||||
export class StructureVerifier {
|
export class StructureVerifier {
|
||||||
shouldRender;
|
instance;
|
||||||
isComplete;
|
intervalOrLifetime;
|
||||||
interval = 5*20;
|
|
||||||
#runner;
|
|
||||||
|
|
||||||
constructor(instance, { shouldRender = false } = {}) {
|
locationsToVerify;
|
||||||
this.shouldRender = shouldRender;
|
blockVerificationLevels;
|
||||||
|
isLocationPopulationComplete;
|
||||||
|
isVerificationComplete;
|
||||||
|
|
||||||
|
#runner;
|
||||||
|
#verifyJob;
|
||||||
|
#populateJob = {};
|
||||||
|
|
||||||
|
constructor(instance, { isEnabled = false, trackPlayerDistance = 0, intervalOrLifetime = 10 } = {}) {
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.interval = Math.max(instance.getActiveVolume() / 50, 20);
|
this.intervalOrLifetime = Math.max(intervalOrLifetime, MIN_LIFETIME);
|
||||||
|
this.instance.options.setVerifierEnabled(isEnabled);
|
||||||
|
this.instance.options.setVerifierDistance(trackPlayerDistance);
|
||||||
|
this.locationsToVerify = new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
startContinuousVerification() {
|
startContinuousVerification() {
|
||||||
this.#runner = system.runInterval(() => {
|
this.#runner = system.runInterval(() => {
|
||||||
this.verifyStructure();
|
this.verifyStructure();
|
||||||
}, this.interval);
|
}, this.intervalOrLifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
stopContinuousVerification() {
|
stopContinuousVerification() {
|
||||||
@@ -35,21 +49,32 @@ export class StructureVerifier {
|
|||||||
this.startContinuousVerification();
|
this.startContinuousVerification();
|
||||||
}
|
}
|
||||||
|
|
||||||
init(shouldRender) {
|
isEnabled() {
|
||||||
this.blockVerificationLevels = { correctlyAir: 0 };
|
return this.instance.options.verifier.isEnabled;
|
||||||
this.shouldRender = shouldRender;
|
|
||||||
this.isComplete = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async verifyStructure() {
|
getTrackPlayerDistance() {
|
||||||
this.init(this.shouldRender);
|
return Math.min(MAX_TRACK_PLAYER_DISTANCE, Math.max(MIN_TRACK_PLAYER_DISTANCE, this.instance.options.verifier.trackPlayerDistance));
|
||||||
return new Promise((resolve) => {
|
}
|
||||||
if (this.instance.isUsingLayers())
|
|
||||||
system.runJob(this.verifyBlocks(this.instance.getLayerBlocks(this.instance.getLayer()-1)));
|
init() {
|
||||||
else
|
this.locationsToVerify.clear();
|
||||||
system.runJob(this.verifyBlocks(this.instance.getBlocks()));
|
this.blockVerificationLevels = { correctlyAir: 0 };
|
||||||
|
this.isLocationPopulationComplete = false;
|
||||||
|
this.isVerificationComplete = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyStructure(shouldRender = true) {
|
||||||
|
if (!this.isEnabled())
|
||||||
|
return;
|
||||||
|
this.init();
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
await this.populateLocationsToVerify();
|
||||||
|
if (this.#verifyJob)
|
||||||
|
system.clearJob(this.#verifyJob);
|
||||||
|
this.verifyJob = system.runJob(this.verifyBlocks(this.locationsToVerify, shouldRender));
|
||||||
const checker = system.runInterval(() => {
|
const checker = system.runInterval(() => {
|
||||||
if (this.isComplete) {
|
if (this.isVerificationComplete) {
|
||||||
system.clearRun(checker);
|
system.clearRun(checker);
|
||||||
resolve(this.blockVerificationLevels);
|
resolve(this.blockVerificationLevels);
|
||||||
}
|
}
|
||||||
@@ -57,28 +82,64 @@ export class StructureVerifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
*verifyBlocks(blocks) {
|
async populateLocationsToVerify() {
|
||||||
for (const block of blocks) {
|
return new Promise((resolve) => {
|
||||||
const verificationLevel = this.verifyBlock(block.location);
|
if (this.getTrackPlayerDistance() === 0) {
|
||||||
|
this.locationsToVerify = this.instance.getAllActiveLocations();
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
for (const job of Object.values(this.#populateJob))
|
||||||
|
system.clearJob(job);
|
||||||
|
for (const player of this.instance.getDimension().getPlayers())
|
||||||
|
this.#populateJob[player.id] = system.runJob(this.populateActiveLocationsNearPlayer(player));
|
||||||
|
const checker = system.runInterval(() => {
|
||||||
|
if (this.isLocationPopulationComplete) {
|
||||||
|
system.clearRun(checker);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
*populateActiveLocationsNearPlayer(player) {
|
||||||
|
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 worldLocation = Vector.from(player.location).add(new Vector(x, y, z)).floor();;
|
||||||
|
const structureLocation = this.instance.toStructureCoords(worldLocation);
|
||||||
|
if (this.instance.isLocationActive(player.dimension.id, structureLocation, { useActiveLayer: true })) {
|
||||||
|
this.locationsToVerify.add(structureLocation);
|
||||||
|
}
|
||||||
|
yield void 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.isLocationPopulationComplete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
*verifyBlocks(locations, shouldRender) {
|
||||||
|
for (const location of locations) {
|
||||||
|
const verificationLevel = this.verifyBlock(location);
|
||||||
if (verificationLevel === BlockVerificationLevel.Air) {
|
if (verificationLevel === BlockVerificationLevel.Air) {
|
||||||
this.blockVerificationLevels.correctlyAir++;
|
this.blockVerificationLevels.correctlyAir++;
|
||||||
} else {
|
} else {
|
||||||
this.blockVerificationLevels[JSON.stringify(block.location)] = verificationLevel;
|
this.blockVerificationLevels[JSON.stringify(location)] = verificationLevel;
|
||||||
if (this.shouldRender) {
|
if (shouldRender) {
|
||||||
const dimensionLocation = { dimension: this.instance.getDimension(), location: this.instance.toGlobalCoords(block.location) };
|
const dimensionLocation = { dimension: this.instance.getDimension(), location: this.instance.toGlobalCoords(location) };
|
||||||
new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.interval/TicksPerSecond);
|
new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.intervalOrLifetime/TicksPerSecond);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
yield void 0;
|
yield void 0;
|
||||||
}
|
}
|
||||||
this.isComplete = true;
|
this.isVerificationComplete = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyBlock(location) {
|
verifyBlock(location) {
|
||||||
const worldBlock = this.instance.getDimension().getBlock(this.instance.toGlobalCoords(location));
|
const worldBlock = this.instance.getDimension().getBlock(this.instance.toGlobalCoords(location));
|
||||||
if (!worldBlock) {
|
if (!worldBlock)
|
||||||
return BlockVerificationLevel.Skipped;
|
return BlockVerificationLevel.Skipped;
|
||||||
}
|
|
||||||
const blockVerifier = new BlockVerifier(worldBlock, this.instance);
|
const blockVerifier = new BlockVerifier(worldBlock, this.instance);
|
||||||
return blockVerifier.verify();
|
return blockVerifier.verify();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
export const InstanceEditButtons = Object.freeze({
|
||||||
|
Unknown: 'Unknown',
|
||||||
|
MainMenu: '<<',
|
||||||
|
Place: '§aPlace Instance',
|
||||||
|
Enable: '§aEnable Instance',
|
||||||
|
Disable: '§cDisable Instance',
|
||||||
|
Rename: 'Rename Instance',
|
||||||
|
Delete: '§cDelete Instance',
|
||||||
|
NextLayer: 'Increase Layer',
|
||||||
|
PreviousLayer: 'Decrease Layer',
|
||||||
|
SetLayer: 'Set Layer',
|
||||||
|
Move: 'Move Here',
|
||||||
|
Statistics: 'Statistics',
|
||||||
|
Settings: 'Settings'
|
||||||
|
});
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
export const InstanceEditOptions = Object.freeze({
|
|
||||||
Unknown: 'Unknown',
|
|
||||||
MainMenu: '<<',
|
|
||||||
PlaceInstance: '§aPlace Instance',
|
|
||||||
EnableInstance: '§aEnable Instance',
|
|
||||||
DisableInstance: '§cDisable Instance',
|
|
||||||
RenameInstance: 'Rename Instance',
|
|
||||||
DeleteInstance: '§cDelete Instance',
|
|
||||||
NextLayer: 'Increase Layer',
|
|
||||||
PreviousLayer: 'Decrease Layer',
|
|
||||||
SetLayer: 'Set Layer',
|
|
||||||
Move: 'Move Here',
|
|
||||||
Statistics: 'Statistics',
|
|
||||||
});
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Part of ItemStack Database by @gameza_src
|
* Unknown author, with additions.
|
||||||
* Unknown author
|
|
||||||
*/
|
*/
|
||||||
const isVec3Symbol = Symbol("isVec3");
|
const isVec3Symbol = Symbol("isVec3");
|
||||||
export function Vector(x = 0, y = 0, z = 0) {
|
export function Vector(x = 0, y = 0, z = 0) {
|
||||||
@@ -23,6 +22,10 @@ Vector.multiply = function multiply(vec, num) {
|
|||||||
}
|
}
|
||||||
Vector.isVec3 = function isVec3(vec) { return vec[isVec3Symbol] === true; }
|
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.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.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.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))); }
|
Vector.reflect = function reflect(v, n) { return Vector.subtract(v, Vector.multiply(n, 2 * Vector.dot(v, n))); }
|
||||||
@@ -56,6 +59,7 @@ Vector.prototype = {
|
|||||||
cross(vec) { return Vector.cross(this, vec); },
|
cross(vec) { return Vector.cross(this, vec); },
|
||||||
dot(vec) { return Vector.dot(this, vec); },
|
dot(vec) { return Vector.dot(this, vec); },
|
||||||
floor() { return Vector.floor(this); },
|
floor() { return Vector.floor(this); },
|
||||||
|
volume(vec) { return Vector.volume(this, vec); },
|
||||||
add(vec) { return Vector.add(this, vec); },
|
add(vec) { return Vector.add(this, vec); },
|
||||||
subtract(vec) { return Vector.subtract(this, vec); },
|
subtract(vec) { return Vector.subtract(this, vec); },
|
||||||
multiply(num) { return Vector.multiply(this, num); },
|
multiply(num) { return Vector.multiply(this, num); },
|
||||||
|
|||||||
Reference in New Issue
Block a user