From 1ca91a09930d7042eb0223f87dc9d5d551edd15e Mon Sep 17 00:00:00 2001 From: ForestOfLight Date: Sun, 20 Apr 2025 02:06:55 -0700 Subject: [PATCH] Now is standalone from Canopy --- .../scripts/classes/Builder/Builder.js | 15 ++-- .../scripts/classes/Builder/BuilderForm.js | 25 +++++-- .../classes/Builder/BuilderFormBuilder.js | 13 ++-- .../scripts/classes/Builder/BuilderOption.js | 42 +++++------ .../scripts/classes/Builder/BuilderOptions.js | 33 +++------ .../scripts/classes/Builder/Builders.js | 21 ++++-- .../classes/Errors/InvalidInstanceError.js | 6 ++ .../scripts/classes/Instance/InstanceForm.js | 13 ++-- .../classes/Instance/InstanceFormBuilder.js | 13 ++-- .../classes/Instance/InstanceOptions.js | 1 + .../StructureInstance.js | 10 +-- .../{ => Materials}/MaterialCounter.js | 0 Construct [BP]/scripts/classes/MenuForm.js | 18 +++-- .../scripts/classes/MenuFormBuilder.js | 5 +- Construct [BP]/scripts/classes/Raycaster.js | 2 +- .../scripts/classes/Structure/Structure.js | 2 +- .../classes/Structure/StructureCollection.js | 12 ++-- .../classes/Structure/StructureOutliner.js | 4 +- .../classes/Structure/StructureStatistics.js | 2 +- .../BlockVerificationLevelRender.js | 4 +- .../classes/{ => Verifier}/BlockVerifier.js | 2 +- .../StructureVerifier.js | 27 +++++-- Construct [BP]/scripts/commands/construct.js | 9 ++- Construct [BP]/scripts/lib/canopy/Rule.js | 70 +++++++++++++++++++ Construct [BP]/scripts/main.js | 11 +-- Construct [BP]/scripts/options/easyPlace.js | 20 +++--- .../scripts/options/fastEasyPlace.js | 12 ++-- .../scripts/options/materialGrabber.js | 15 ++-- 28 files changed, 256 insertions(+), 151 deletions(-) create mode 100644 Construct [BP]/scripts/classes/Errors/InvalidInstanceError.js rename Construct [BP]/scripts/classes/{Structure => Instance}/StructureInstance.js (96%) rename Construct [BP]/scripts/classes/{ => Materials}/MaterialCounter.js (100%) rename Construct [BP]/scripts/classes/{ => Verifier}/BlockVerificationLevelRender.js (95%) rename Construct [BP]/scripts/classes/{ => Verifier}/BlockVerifier.js (95%) rename Construct [BP]/scripts/classes/{Structure => Verifier}/StructureVerifier.js (84%) create mode 100644 Construct [BP]/scripts/lib/canopy/Rule.js diff --git a/Construct [BP]/scripts/classes/Builder/Builder.js b/Construct [BP]/scripts/classes/Builder/Builder.js index 0cc67a9..afadf1e 100644 --- a/Construct [BP]/scripts/classes/Builder/Builder.js +++ b/Construct [BP]/scripts/classes/Builder/Builder.js @@ -1,22 +1,15 @@ import { BuilderOptions } from "./BuilderOptions"; -import { Builders } from "./Builders"; export class Builder { constructor(playerId) { this.playerId = playerId; - this.options = new BuilderOptions(playerId); - Builders.add(this); } - getOptionIds() { - return Object.keys(this.options.options).sort((a, b) => a.localeCompare(b)); + isOptionEnabled(optionId) { + return BuilderOptions.isEnabled(optionId, this.playerId); } - getOption(id) { - return this.options.get(id); - } - - setOption(id, value) { - return this.options.setValue(id, value); + setOption(optionId, value) { + return BuilderOptions.setValue(optionId, this.playerId, value); } } \ No newline at end of file diff --git a/Construct [BP]/scripts/classes/Builder/BuilderForm.js b/Construct [BP]/scripts/classes/Builder/BuilderForm.js index c3621e4..174a718 100644 --- a/Construct [BP]/scripts/classes/Builder/BuilderForm.js +++ b/Construct [BP]/scripts/classes/Builder/BuilderForm.js @@ -1,24 +1,37 @@ import { BuilderFormBuilder } from "./BuilderFormBuilder"; -import { Builders } from "./Builders"; +import { BuilderOptions } from "./BuilderOptions"; +import { forceShow } from '../../utils'; export class BuilderForm { constructor(player) { this.player = player; + this.show(); } show() { - forceShow(this.player, BuilderFormBuilder.buildSettings(this.player)).then((response) => { + forceShow(this.player, BuilderFormBuilder.buildBuilderOptions(this.player)).then((response) => { if (response.canceled) return; this.applySettings(response.formValues); }); } applySettings(formValues) { - const optionIds = Builders.get(this.player.id).getOptionIds(); + const optionIds = BuilderOptions.getOptionIds(); for (let i = 0; i < optionIds.length; i++) { - const option = optionIds[i]; - if (option.setValue(formValues[i]) && formValues[i] === true) - this.player.sendMessage(`§a${option.displayName} is enabled!§7 ${option.howToUse}`); + const option = BuilderOptions.get(optionIds[i]); + const changedToValue = option.setValue(this.player.id, formValues[i]); + if (changedToValue === true) + this.player.sendMessage(`§a${option.displayName} is now enabled!§7 ${option.howToUse}`); + else if (changedToValue === false) + this.player.sendMessage(`§c${option.displayName} is now disabled.`) } } + + valueChangedToEnabled(hasChanged, newValue) { + return hasChanged && newValue === true; + } + + valueChangedToDisabled(hasChanged, newValue) { + return hasChanged && newValue === false; + } } \ No newline at end of file diff --git a/Construct [BP]/scripts/classes/Builder/BuilderFormBuilder.js b/Construct [BP]/scripts/classes/Builder/BuilderFormBuilder.js index b5a43c1..736cc43 100644 --- a/Construct [BP]/scripts/classes/Builder/BuilderFormBuilder.js +++ b/Construct [BP]/scripts/classes/Builder/BuilderFormBuilder.js @@ -1,15 +1,16 @@ import { ModalFormData } from "@minecraft/server-ui"; import { MenuFormBuilder } from "../MenuFormBuilder"; -import { Builders } from "./Builders"; +import { BuilderOptions } from "./BuilderOptions"; export class BuilderFormBuilder { - static buildSettings(player) { + static buildBuilderOptions(player) { const form = new ModalFormData() .title(MenuFormBuilder.menuTitle); - const builder = Builders.get(player.id); - for (const optionId of builder.getOptionIds()) { - const option = builder.getOption(optionId); - form.toggle(`${option.displayName} - ${option.description}`, option.getValue()); + for (const optionId of BuilderOptions.getOptionIds()) { + const option = BuilderOptions.get(optionId); + form.toggle(`${option.displayName} - ${option.description}`, option.isEnabled(player.id)); } + form.submitButton('§2Apply'); + return form; } } \ No newline at end of file diff --git a/Construct [BP]/scripts/classes/Builder/BuilderOption.js b/Construct [BP]/scripts/classes/Builder/BuilderOption.js index 3153100..cdd981c 100644 --- a/Construct [BP]/scripts/classes/Builder/BuilderOption.js +++ b/Construct [BP]/scripts/classes/Builder/BuilderOption.js @@ -1,18 +1,16 @@ import { BuilderOptions } from "./BuilderOptions"; -import { Option } from "../Option"; +import { world } from "@minecraft/server"; -export class BuilderOption extends Option { - playerId; +export class BuilderOption { identifier; displayName; description; - value; + howToUse; #onEnable; #onDisable; #DP_NAMESPACE = "builderOptions"; - constructor({ player, identifier, displayName, description, howToUse, onEnableCallback = () => {}, onDisableCallback = () => {} }) { - this.playerId = player.id; + constructor({ identifier, displayName, description, howToUse, onEnableCallback = () => {}, onDisableCallback = () => {} }) { this.identifier = identifier; this.displayName = displayName; this.description = description; @@ -22,32 +20,24 @@ export class BuilderOption extends Option { BuilderOptions.add(this); } - save() { - this.saveToDP(this.#DP_NAMESPACE, `${this.playerId}:${this.identifier}`, this); - } - - load() { - this.loadFromDP(this.#DP_NAMESPACE, `${this.playerId}:${this.identifier}`); - this.value = this.value ?? false; - } - - clear() { - this.clearDP(this.#DP_NAMESPACE, `${this.playerId}:${this.identifier}`); - } - - getValue() { - return this.value; + isEnabled(playerId) { + return world.getDynamicProperty(`${this.#DP_NAMESPACE}:${playerId}:${this.identifier}`) === true; } - setValue(value) { + setValue(playerId, value) { if (value) - this.#onEnable(); + this.#onEnable(playerId); else - this.#onDisable(); - if (this.value !== value) { - this.value = value; + this.#onDisable(playerId); + if (this.isEnabled(playerId) !== value) { + this.save(playerId, value); return value; } + this.save(playerId, value); return void 0; } + + save(playerId, value) { + world.setDynamicProperty(`${this.#DP_NAMESPACE}:${playerId}:${this.identifier}`, value); + } } \ No newline at end of file diff --git a/Construct [BP]/scripts/classes/Builder/BuilderOptions.js b/Construct [BP]/scripts/classes/Builder/BuilderOptions.js index 9a3927d..ad3c607 100644 --- a/Construct [BP]/scripts/classes/Builder/BuilderOptions.js +++ b/Construct [BP]/scripts/classes/Builder/BuilderOptions.js @@ -1,34 +1,23 @@ -import { world } from "@minecraft/server"; -import { BuilderOption } from "./BuilderOption"; - export class BuilderOptions { - playerId = void 0; - options = {}; + static options = {}; - constructor(playerId) { - this.playerId = playerId; - this.loadOptions(); + static add(builderOption) { + this.options[builderOption.identifier] = builderOption; } - loadOptions() { - const optionDPs = world.getDynamicPropertyIds().filter((id) => id.startsWith(`builderOptions:${this.playerId}:`)); - for (const optionDP of optionDPs) - new BuilderOption(JSON.parse(world.getDynamicProperty(optionDP))); + static get(optionId) { + return this.options[optionId]; } - add(builderOption) { - this.options[builderOption.identifer] = builderOption; + static getOptionIds() { + return Object.keys(this.options).sort((a, b) => a - b); } - get(id) { - return this.options[id]; + static isEnabled(optionId, playerId) { + return this.options[optionId].isEnabled(playerId); } - getValue(id) { - return this.options[id].getValue(); - } - - setValue(id, value) { - return this.options[id].setValue(value); + static setValue(optionId, playerId, value) { + return this.options[optionId].setValue(playerId, value); } } \ No newline at end of file diff --git a/Construct [BP]/scripts/classes/Builder/Builders.js b/Construct [BP]/scripts/classes/Builder/Builders.js index 2cd6458..d612b72 100644 --- a/Construct [BP]/scripts/classes/Builder/Builders.js +++ b/Construct [BP]/scripts/classes/Builder/Builders.js @@ -4,22 +4,33 @@ import { Builder } from "./Builder"; export class Builders { static builders = {}; - add(playerId) { + static add(playerId) { + if (this.builders[playerId]) + return; this.builders[playerId] = new Builder(playerId); } - remove(playerId) { + static remove(playerId) { delete this.builders[playerId]; } - get(id) { + static get(id) { return this.builders[id]; } - onJoin(playerId) { + static onJoin(playerId) { this.add(playerId); } + + static onLeave(playerId) { + this.remove(playerId); + } } world.afterEvents.playerJoin.subscribe((event) => Builders.onJoin(event.playerId)); -world.beforeEvents.playerLeave.subscribe((event) => Builders.onLeave(event.player)); \ No newline at end of file +world.beforeEvents.playerLeave.subscribe((event) => Builders.onLeave(event.player.id)); +world.afterEvents.worldLoad.subscribe((event) => { + for (const player of world.getAllPlayers()) { + Builders.onJoin(player.id); + } +}); \ No newline at end of file diff --git a/Construct [BP]/scripts/classes/Errors/InvalidInstanceError.js b/Construct [BP]/scripts/classes/Errors/InvalidInstanceError.js new file mode 100644 index 0000000..612e2dc --- /dev/null +++ b/Construct [BP]/scripts/classes/Errors/InvalidInstanceError.js @@ -0,0 +1,6 @@ +export class InvalidInstanceError extends Error { + constructor(message) { + super(message); + this.name = 'InvalidInstanceError'; + } +} \ No newline at end of file diff --git a/Construct [BP]/scripts/classes/Instance/InstanceForm.js b/Construct [BP]/scripts/classes/Instance/InstanceForm.js index ebdf90e..afc401c 100644 --- a/Construct [BP]/scripts/classes/Instance/InstanceForm.js +++ b/Construct [BP]/scripts/classes/Instance/InstanceForm.js @@ -1,7 +1,7 @@ -import { structureCollection } from './StructureCollection'; +import { structureCollection } from '../Structure/StructureCollection'; import { MenuForm } from '../MenuForm'; import { forceShow } from '../../utils'; -import { InstanceButtons } from '../enums/InstanceButtons'; +import { InstanceButtons } from '../Enums/InstanceButtons'; import { InstanceFormBuilder } from './InstanceFormBuilder'; import { FormCancelationReason } from '@minecraft/server-ui'; @@ -11,12 +11,11 @@ export class InstanceForm { isEnabled: [ InstanceButtons.NextLayer, InstanceButtons.PreviousLayer, - InstanceButtons.SetLayer, InstanceButtons.Move, InstanceButtons.Statistics, InstanceButtons.Settings, InstanceButtons.Rename, - InstanceButtons.Disable, + InstanceButtons.Disable ], isNotEnabledAndIsNotPlaced: [ InstanceButtons.Place, @@ -149,7 +148,11 @@ export class InstanceForm { if (response.canceled) return; this.instance.setVerifierEnabled(response.formValues[0]); - this.instance.setLayer(parseInt(response.formValues[1])); + if (response.formValues[1]) + this.instance.setVerifierDistance(5); + else + this.instance.setVerifierDistance(0); + this.instance.setLayer(parseInt(response.formValues[2])); }); } } \ No newline at end of file diff --git a/Construct [BP]/scripts/classes/Instance/InstanceFormBuilder.js b/Construct [BP]/scripts/classes/Instance/InstanceFormBuilder.js index 848efb7..2ac929c 100644 --- a/Construct [BP]/scripts/classes/Instance/InstanceFormBuilder.js +++ b/Construct [BP]/scripts/classes/Instance/InstanceFormBuilder.js @@ -1,7 +1,7 @@ import { ActionFormData, ModalFormData } from '@minecraft/server-ui'; import { MenuFormBuilder } from '../MenuFormBuilder'; -import { StructureVerifier } from './StructureVerifier'; -import { StructureStatistics } from './StructureStatistics'; +import { StructureVerifier } from '../Verifier/StructureVerifier'; +import { StructureStatistics } from '../Structure/StructureStatistics'; import { TicksPerSecond } from '@minecraft/server'; export class InstanceFormBuilder { @@ -29,7 +29,7 @@ export class InstanceFormBuilder { static async buildStatistics(instance) { const buildStatisticsForm = new ActionFormData() .title(MenuFormBuilder.menuTitle) - const structureVerifier = new StructureVerifier(instance, { isEnabled: true, trackPlayerDistance: 0, intervalOrLifetime: 30 * TicksPerSecond }); + const structureVerifier = new StructureVerifier(instance, { isEnabled: true, trackPlayerDistance: 0, intervalOrLifetime: 30 * TicksPerSecond, isStandalone: true }); const verification = await structureVerifier.verifyStructure(); const statistics = new StructureStatistics(instance, verification); const statsMessage = statistics.getMessage(); @@ -39,10 +39,11 @@ export class InstanceFormBuilder { static buildSettings(instance) { return new ModalFormData() - .title(MenuFormBuilder.menuTitle) + .title(MenuFormBuilder.menuTitle) .label('Use the slider to select the layer. Use 0 for all layers.') .toggle('Block Validation', instance.options.verifier.isEnabled) - .slider("Layer", 0, maxLayer, 1, currentLayer) - .submitButton('§aApply'); + .toggle('Distance-Based Block Validation', instance.verifier.getTrackPlayerDistance() !== 0) + .slider("Layer", 0, instance.getMaxLayer(), 1, instance.getLayer()) + .submitButton('§2Apply'); } } \ No newline at end of file diff --git a/Construct [BP]/scripts/classes/Instance/InstanceOptions.js b/Construct [BP]/scripts/classes/Instance/InstanceOptions.js index 34f834d..8558355 100644 --- a/Construct [BP]/scripts/classes/Instance/InstanceOptions.js +++ b/Construct [BP]/scripts/classes/Instance/InstanceOptions.js @@ -22,6 +22,7 @@ export class InstanceOptions extends Option { } constructor(instanceName, structureId) { + super(); this.instanceName = instanceName; this.structureId = structureId; this.load(); diff --git a/Construct [BP]/scripts/classes/Structure/StructureInstance.js b/Construct [BP]/scripts/classes/Instance/StructureInstance.js similarity index 96% rename from Construct [BP]/scripts/classes/Structure/StructureInstance.js rename to Construct [BP]/scripts/classes/Instance/StructureInstance.js index 8117fef..4835317 100644 --- a/Construct [BP]/scripts/classes/Structure/StructureInstance.js +++ b/Construct [BP]/scripts/classes/Instance/StructureInstance.js @@ -1,8 +1,8 @@ -import { Vector } from "../lib/Vector"; -import { StructureOutliner } from "./StructureOutliner"; -import { StructureVerifier } from "./StructureVerifier"; +import { Vector } from "../../lib/Vector"; +import { StructureOutliner } from "../Structure/StructureOutliner"; +import { StructureVerifier } from "../Verifier/StructureVerifier"; +import { Structure } from "../Structure/Structure"; import { InstanceOptions } from "./InstanceOptions"; -import { Structure } from "./Structure"; import { TicksPerSecond } from "@minecraft/server"; export class StructureInstance { @@ -19,11 +19,11 @@ export class StructureInstance { delete() { this.disable(); + this.options.clear(); delete this.options; delete this.structure; delete this.outliner; delete this.verifier; - this.options.clear(); } refreshBox() { diff --git a/Construct [BP]/scripts/classes/MaterialCounter.js b/Construct [BP]/scripts/classes/Materials/MaterialCounter.js similarity index 100% rename from Construct [BP]/scripts/classes/MaterialCounter.js rename to Construct [BP]/scripts/classes/Materials/MaterialCounter.js diff --git a/Construct [BP]/scripts/classes/MenuForm.js b/Construct [BP]/scripts/classes/MenuForm.js index 10158e6..a70eb45 100644 --- a/Construct [BP]/scripts/classes/MenuForm.js +++ b/Construct [BP]/scripts/classes/MenuForm.js @@ -1,7 +1,8 @@ import { forceShow } from '../utils'; -import { structureCollection } from './StructureCollection'; +import { structureCollection } from './Structure/StructureCollection'; import { MenuFormBuilder } from './MenuFormBuilder'; -import { InstanceForm } from './InstanceForm'; +import { InstanceForm } from './Instance/InstanceForm'; +import { BuilderForm } from './Builder/BuilderForm'; export class MenuForm { constructor(player, { jumpToInstance = false, instanceName = void 0 } = {}) { @@ -13,7 +14,7 @@ export class MenuForm { if (jumpToInstance) { if (!instanceName) instanceName = structureCollection.getStructure(this.player.dimension.id, this.player.location, { useActiveLayer: false })?.getName(); - if (structureCollection.get(instanceName)) { + if (instanceName) { new InstanceForm(this.player, instanceName); return; } @@ -29,8 +30,15 @@ export class MenuForm { return forceShow(this.player, MenuFormBuilder.buildAllInstanceName()).then((response) => { if (response.canceled) return; - const selectedInstanceName = structureCollection.getInstanceNames()[response.selection]; - return selectedInstanceName || this.createNewInstance(); + let selection = response.selection; + if (selection === 0) { + new BuilderForm(this.player); + return void 0; + } else { + selection--; + const selectedInstanceName = structureCollection.getInstanceNames()[selection]; + return selectedInstanceName || this.createNewInstance(); + } }); } catch (e) { if (e.message === 'Menu timed out.') { diff --git a/Construct [BP]/scripts/classes/MenuFormBuilder.js b/Construct [BP]/scripts/classes/MenuFormBuilder.js index dcde0bd..cd24324 100644 --- a/Construct [BP]/scripts/classes/MenuFormBuilder.js +++ b/Construct [BP]/scripts/classes/MenuFormBuilder.js @@ -1,13 +1,14 @@ import { ActionFormData, ModalFormData } from '@minecraft/server-ui'; -import { structureCollection } from './StructureCollection'; +import { structureCollection } from './Structure/StructureCollection'; export class MenuFormBuilder { - static menuTitle = '§l§2Construct §8Menu'; + static menuTitle = '§l§2Construct'; static buildAllInstanceName() { const allInstanceNameForm = new ActionFormData() .title(this.menuTitle) .body('Select an instance:'); + allInstanceNameForm.button('Builder Settings'); structureCollection.getInstanceNames().forEach(instanceName => { allInstanceNameForm.button(`§2${instanceName}`); }); diff --git a/Construct [BP]/scripts/classes/Raycaster.js b/Construct [BP]/scripts/classes/Raycaster.js index 0e36355..3cc5ead 100644 --- a/Construct [BP]/scripts/classes/Raycaster.js +++ b/Construct [BP]/scripts/classes/Raycaster.js @@ -1,4 +1,4 @@ -import { structureCollection } from "./StructureCollection"; +import { structureCollection } from "./Structure/StructureCollection"; import { world } from "@minecraft/server"; export class Raycaster { diff --git a/Construct [BP]/scripts/classes/Structure/Structure.js b/Construct [BP]/scripts/classes/Structure/Structure.js index 58a36ef..822f53e 100644 --- a/Construct [BP]/scripts/classes/Structure/Structure.js +++ b/Construct [BP]/scripts/classes/Structure/Structure.js @@ -1,5 +1,5 @@ import { world } from "@minecraft/server"; -import { Vector } from "../lib/Vector"; +import { Vector } from "../../lib/Vector"; export class Structure { structureId; diff --git a/Construct [BP]/scripts/classes/Structure/StructureCollection.js b/Construct [BP]/scripts/classes/Structure/StructureCollection.js index 26f1907..91d1c4e 100644 --- a/Construct [BP]/scripts/classes/Structure/StructureCollection.js +++ b/Construct [BP]/scripts/classes/Structure/StructureCollection.js @@ -1,5 +1,6 @@ -import { InstanceOptions } from './InstanceOptions'; -import { StructureInstance } from './StructureInstance'; +import { InvalidInstanceError } from '../Errors/InvalidInstanceError'; +import { InstanceOptions } from '../Instance/InstanceOptions'; +import { StructureInstance } from '../Instance/StructureInstance'; import { world } from '@minecraft/server'; class StructureCollection { @@ -26,7 +27,7 @@ class StructureCollection { add(instanceName, structureId) { if (this.structures[instanceName]) - throw new Error(`Instance ${instanceName} already exists.`); + throw new InvalidInstanceError(`Instance ${instanceName} already exists.`); const structure = new StructureInstance(instanceName, structureId); this.structures[instanceName] = structure; return structure; @@ -34,9 +35,8 @@ class StructureCollection { get(instanceName) { const structure = this.structures[instanceName]; - if (!structure) { - throw new Error(`Instance ${instanceName} not found.`); - } + if (!structure) + throw new InvalidInstanceError(`Instance ${instanceName} not found.`); return structure; } diff --git a/Construct [BP]/scripts/classes/Structure/StructureOutliner.js b/Construct [BP]/scripts/classes/Structure/StructureOutliner.js index 023fde5..6d7e005 100644 --- a/Construct [BP]/scripts/classes/Structure/StructureOutliner.js +++ b/Construct [BP]/scripts/classes/Structure/StructureOutliner.js @@ -1,4 +1,4 @@ -import { Outliner } from './Outliner'; +import { Outliner } from '../Outliner'; export class StructureOutliner { constructor(instance) { @@ -42,7 +42,7 @@ export class StructureOutliner { } layeredDraw() { - const { min, max } = this.instance.getLayeredBounds(); + const { min, max } = this.instance.getLayerBounds(this.instance.getLayer()); this.outliner.setVertices(this.dimension, this.instance.toGlobalCoords(min), this.instance.toGlobalCoords(max)); this.outliner.addStandaloneParticles(this.getCornerVertices()); } diff --git a/Construct [BP]/scripts/classes/Structure/StructureStatistics.js b/Construct [BP]/scripts/classes/Structure/StructureStatistics.js index 0b09187..20b96ff 100644 --- a/Construct [BP]/scripts/classes/Structure/StructureStatistics.js +++ b/Construct [BP]/scripts/classes/Structure/StructureStatistics.js @@ -1,4 +1,4 @@ -import { BlockVerificationLevel } from './enums/BlockVerificationLevel.js'; +import { BlockVerificationLevel } from '../Enums/BlockVerificationLevel.js'; export class StructureStatistics { constructor(instance, verification) { diff --git a/Construct [BP]/scripts/classes/BlockVerificationLevelRender.js b/Construct [BP]/scripts/classes/Verifier/BlockVerificationLevelRender.js similarity index 95% rename from Construct [BP]/scripts/classes/BlockVerificationLevelRender.js rename to Construct [BP]/scripts/classes/Verifier/BlockVerificationLevelRender.js index 70a6716..bef6cc4 100644 --- a/Construct [BP]/scripts/classes/BlockVerificationLevelRender.js +++ b/Construct [BP]/scripts/classes/Verifier/BlockVerificationLevelRender.js @@ -1,6 +1,6 @@ import { MolangVariableMap } from "@minecraft/server"; -import { BlockVerificationLevel } from "./enums/BlockVerificationLevel"; -import { Vector } from "../lib/Vector"; +import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel"; +import { Vector } from "../../lib/Vector"; export class BlockVerificationLevelRender { opacity = 0.2; diff --git a/Construct [BP]/scripts/classes/BlockVerifier.js b/Construct [BP]/scripts/classes/Verifier/BlockVerifier.js similarity index 95% rename from Construct [BP]/scripts/classes/BlockVerifier.js rename to Construct [BP]/scripts/classes/Verifier/BlockVerifier.js index 81b9a4e..2c5d593 100644 --- a/Construct [BP]/scripts/classes/BlockVerifier.js +++ b/Construct [BP]/scripts/classes/Verifier/BlockVerifier.js @@ -1,4 +1,4 @@ -import { BlockVerificationLevel } from "./enums/BlockVerificationLevel"; +import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel"; export class BlockVerifier { constructor(block, instance) { diff --git a/Construct [BP]/scripts/classes/Structure/StructureVerifier.js b/Construct [BP]/scripts/classes/Verifier/StructureVerifier.js similarity index 84% rename from Construct [BP]/scripts/classes/Structure/StructureVerifier.js rename to Construct [BP]/scripts/classes/Verifier/StructureVerifier.js index 441b629..90ed15a 100644 --- a/Construct [BP]/scripts/classes/Structure/StructureVerifier.js +++ b/Construct [BP]/scripts/classes/Verifier/StructureVerifier.js @@ -1,8 +1,8 @@ import { BlockVerifier } from "./BlockVerifier"; -import { BlockVerificationLevel } from "./enums/BlockVerificationLevel"; -import { BlockVerificationLevelRender } from "./BlockVerificationLevelRender"; +import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel"; +import { BlockVerificationLevelRender } from "../Verifier/BlockVerificationLevelRender"; import { system, TicksPerSecond } from "@minecraft/server"; -import { Vector } from "../lib/Vector"; +import { Vector } from "../../lib/Vector"; const MIN_TRACK_PLAYER_DISTANCE = 0; const MAX_TRACK_PLAYER_DISTANCE = 7; @@ -21,11 +21,17 @@ export class StructureVerifier { #verifyJob; #populateJob = {}; - constructor(instance, { isEnabled = false, trackPlayerDistance = 0, intervalOrLifetime = 10 } = {}) { + constructor(instance, { isEnabled = false, trackPlayerDistance = 0, intervalOrLifetime = 10, isStandalone: isIndependent = false } = {}) { this.instance = instance; this.intervalOrLifetime = Math.max(intervalOrLifetime, MIN_LIFETIME); - this.instance.options.setVerifierEnabled(isEnabled); - this.instance.options.setVerifierDistance(trackPlayerDistance); + if (isIndependent) { + this.isIndependent = isIndependent; + this.enabled = isEnabled; + this.trackPlayerDistance = trackPlayerDistance; + } else { + this.instance.options.setVerifierEnabled(isEnabled); + this.instance.options.setVerifierDistance(trackPlayerDistance); + } this.locationsToVerify = new Set(); } @@ -50,11 +56,18 @@ export class StructureVerifier { } isEnabled() { + if (this.isIndependent) + return this.enabled; return this.instance.options.verifier.isEnabled; } getTrackPlayerDistance() { - return Math.min(MAX_TRACK_PLAYER_DISTANCE, Math.max(MIN_TRACK_PLAYER_DISTANCE, this.instance.options.verifier.trackPlayerDistance)); + let distance; + if (this.isIndependent) + distance = this.trackPlayerDistance; + else + distance = this.instance.options.verifier.trackPlayerDistance + return Math.min(MAX_TRACK_PLAYER_DISTANCE, Math.max(MIN_TRACK_PLAYER_DISTANCE, distance)); } init() { diff --git a/Construct [BP]/scripts/commands/construct.js b/Construct [BP]/scripts/commands/construct.js index 34ad208..219b373 100644 --- a/Construct [BP]/scripts/commands/construct.js +++ b/Construct [BP]/scripts/commands/construct.js @@ -2,6 +2,7 @@ import { Command } from '../lib/canopy/CanopyExtension'; import { extension } from '../config'; import { world, system } from '@minecraft/server'; import { MenuForm } from '../classes/MenuForm'; +import { structureCollection } from '../classes/Structure/StructureCollection' const ACTION_ITEM = 'minecraft:paper'; @@ -21,7 +22,11 @@ world.beforeEvents.itemUse.subscribe((event) => { function openMenu(sender, event = void 0) { const options = { jumpToInstance: true } - if (event) - options.instanceName = event.itemStack?.typeId; + if (event) { + const instanceNames = structureCollection.getInstanceNames(); + const instanceName = event.itemStack?.nameTag; + if (instanceNames.includes(instanceName)) + options.instanceName = instanceName; + } new MenuForm(sender, options); } \ No newline at end of file diff --git a/Construct [BP]/scripts/lib/canopy/Rule.js b/Construct [BP]/scripts/lib/canopy/Rule.js new file mode 100644 index 0000000..3ba7b4f --- /dev/null +++ b/Construct [BP]/scripts/lib/canopy/Rule.js @@ -0,0 +1,70 @@ +/** + * @license + * MIT License + * + * Copyright (c) 2024 ForestOfLight + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +import { world } from '@minecraft/server'; + +class Rule { + #identifier; + #description; + #contingentRules; + #independentRules; + + constructor({ identifier, description, contingentRules = [], independentRules = [] }) { + this.#identifier = identifier; + this.#description = description; + this.#contingentRules = contingentRules; + this.#independentRules = independentRules; + } + + getID() { + return this.#identifier; + } + + getDescription() { + return this.#description; + } + + getContigentRules() { + return this.#contingentRules; + } + + getIndependentRules() { + return this.#independentRules; + } + + getValue() { + const value = world.getDynamicProperty(this.#identifier); + if (String(value) === 'true') + return true; + if (['false', 'undefined'].includes(String(value))) + return false; + throw new Error(`Rule ${this.#identifier} has an invalid value: ${value} (${typeof value})`); + } + + setValue(value) { + world.setDynamicProperty(this.#identifier, value); + } +} + +export default Rule; \ No newline at end of file diff --git a/Construct [BP]/scripts/main.js b/Construct [BP]/scripts/main.js index 4bc4dda..508d3f0 100644 --- a/Construct [BP]/scripts/main.js +++ b/Construct [BP]/scripts/main.js @@ -1,7 +1,10 @@ -// Rules -import './rules/easyPlace'; -import './rules/fastEasyPlace'; -import './rules/materialGrabber'; +// Setup +import './classes/Builder/Builders'; + +// Options +import './options/easyPlace'; +import './options/fastEasyPlace'; +import './options/materialGrabber'; // Commands import './commands/construct'; diff --git a/Construct [BP]/scripts/options/easyPlace.js b/Construct [BP]/scripts/options/easyPlace.js index 0192982..3cd44b7 100644 --- a/Construct [BP]/scripts/options/easyPlace.js +++ b/Construct [BP]/scripts/options/easyPlace.js @@ -1,24 +1,24 @@ import { BuilderOption } from '../classes/Builder/BuilderOption'; import { BlockPermutation, EntityComponentTypes, GameMode, ItemStack, system, world } from '@minecraft/server'; -import { structureCollection } from '../classes/StructureCollection'; +import { structureCollection } from '../classes/Structure/StructureCollection'; import { bannedBlocks, bannedToValidBlockMap, whitelistedBlockStates, resetToBlockStates, bannedDimensionBlocks, specialItemPlacementConversions, blockIdToItemStackMap } from '../data'; import { fetchMatchingItemSlot } from '../utils'; -const ACTION_SLOT = 35; +const ACTION_SLOT = 27; -new BuilderOption({ +const builderOption = new BuilderOption({ identifier: 'easyPlace', displayName: 'Easy Place', - description: 'Always place the correct block.', - howToUse: "Place blocks in a structure with a paper named 'Easy Place' in the bottom right slot of your inventory to always place the correct block.", - onEnableCallback: () => { world.beforeEvents.playerPlaceBlock.subscribe(onPlayerPlaceBlock); }, - onDisableCallback: () => { world.beforeEvents.playerPlaceBlock.unsubscribe(onPlayerPlaceBlock); } -}) + description: 'Always place the correct structure block.', + howToUse: "Place blocks in a structure with a paper named 'Easy Place' in the inventory slot above your first hotbar slot to always place the correct block." +}); + +world.beforeEvents.playerPlaceBlock.subscribe(onPlayerPlaceBlock); function onPlayerPlaceBlock(event) { - const { player, block, permutationBeingPlaced } = event; - if (!player || !block || !hasActionItemInCorrectSlot(player)) return; + const { player, block } = event; + if (!player || !block || !builderOption.isEnabled(player.id) || !hasActionItemInCorrectSlot(player)) return; const structureBlock = structureCollection.fetchStructureBlock(block.dimension.id, block.location); if (!structureBlock) return; diff --git a/Construct [BP]/scripts/options/fastEasyPlace.js b/Construct [BP]/scripts/options/fastEasyPlace.js index 8bef942..e7ba9fc 100644 --- a/Construct [BP]/scripts/options/fastEasyPlace.js +++ b/Construct [BP]/scripts/options/fastEasyPlace.js @@ -4,19 +4,21 @@ import { bannedBlocks, bannedToValidBlockMap, whitelistedBlockStates, resetToBlo blockIdToItemStackMap } from '../data'; import { Raycaster } from '../classes/Raycaster'; +const PROCESS_INTERVAL = 2; + let runner = void 0; -new BuilderOption({ +const builderOption = new BuilderOption({ identifier: 'fastEasyPlace', displayName: 'Fast Easy Place', description: 'Place structure blocks just by looking at them.', howToUse: "Look at structure blocks with a paper named 'Easy Place' in your hand to place them.", - onEnableCallback: () => { runner = system.runInterval(onTick, 2); }, - onDisableCallback: () => { system.clearRun(runner); } -}) +}); + +system.runInterval(onTick, PROCESS_INTERVAL); function onTick() { for (const player of world.getAllPlayers()) { - if (!player) + if (!player || !builderOption.isEnabled(player.id)) continue; processEasyPlace(player); } diff --git a/Construct [BP]/scripts/options/materialGrabber.js b/Construct [BP]/scripts/options/materialGrabber.js index 4d5352f..e11fec3 100644 --- a/Construct [BP]/scripts/options/materialGrabber.js +++ b/Construct [BP]/scripts/options/materialGrabber.js @@ -1,20 +1,15 @@ import { BuilderOption } from '../classes/Builder/BuilderOption'; import { world } from '@minecraft/server'; -new BuilderOption({ +const builderOption = new BuilderOption({ identifier: 'materialGrabber', displayName: 'Material Grabber', description: 'Pulls structure items from inventories.', howToUse: "Interact with inventories using a paper named 'Material Grabber' to pull structure items from them.", - onEnableCallback: () => { - world.beforeEvents.playerInteractWithBlock.subscribe(onPlayerInteract); - world.beforeEvents.playerInteractWithEntity.subscribe(onPlayerInteract); - }, - onDisableCallback: () => { - world.beforeEvents.playerInteractWithBlock.unsubscribe(onPlayerInteract); - world.beforeEvents.playerInteractWithEntity.unsubscribe(onPlayerInteract); - } -}) +}); + +world.beforeEvents.playerInteractWithBlock.subscribe(onPlayerInteract); +world.beforeEvents.playerInteractWithEntity.subscribe(onPlayerInteract); function onPlayerInteract(event) { // get inventory