diff --git a/Construct [BP]/manifest.json b/Construct [BP]/manifest.json index c0c3d71..6001919 100644 --- a/Construct [BP]/manifest.json +++ b/Construct [BP]/manifest.json @@ -1,7 +1,7 @@ { "format_version": 2, "header": { - "name": "StrucTool [BP] v1.0.0", + "name": "Construct [BP] v1.0.0", "description": "Survival building extension for §l§aCanopy§r by §aForestOfLight§r.", "uuid": "8c0c0153-d8b9-482a-889f-aef922b8fe58", "min_engine_version": [1, 21, 70], @@ -33,7 +33,7 @@ "version": "2.0.0-beta" }, { - "uuid": "375ec465-3dc1-429f-8b4c-a337889e1ed4", // StrucTool RP + "uuid": "375ec465-3dc1-429f-8b4c-a337889e1ed4", // Construct RP "version": [1, 0, 0] }, { diff --git a/Construct [BP]/scripts/classes/BlockVerificationLevelRender.js b/Construct [BP]/scripts/classes/BlockVerificationLevelRender.js index 34ebde3..70a6716 100644 --- a/Construct [BP]/scripts/classes/BlockVerificationLevelRender.js +++ b/Construct [BP]/scripts/classes/BlockVerificationLevelRender.js @@ -36,12 +36,12 @@ export class BlockVerificationLevelRender { const frontFace = new Vector(0.5, 0.5, 1); const backFace = new Vector(0.5, 0.5, 0); return [ - { particleType: "structool:blockoverlay_xz", location: this.location.add(topFace) }, - { particleType: "structool:blockoverlay_xz", location: this.location.add(bottomFace) }, - { particleType: "structool:blockoverlay_yz", location: this.location.add(leftFace) }, - { particleType: "structool:blockoverlay_yz", location: this.location.add(rightFace) }, - { particleType: "structool:blockoverlay_xy", location: this.location.add(frontFace) }, - { particleType: "structool:blockoverlay_xy", location: this.location.add(backFace) } + { particleType: "construct:blockoverlay_xz", location: this.location.add(topFace) }, + { particleType: "construct:blockoverlay_xz", location: this.location.add(bottomFace) }, + { particleType: "construct:blockoverlay_yz", location: this.location.add(leftFace) }, + { particleType: "construct:blockoverlay_yz", location: this.location.add(rightFace) }, + { particleType: "construct:blockoverlay_xy", location: this.location.add(frontFace) }, + { particleType: "construct:blockoverlay_xy", location: this.location.add(backFace) } ]; } diff --git a/Construct [BP]/scripts/classes/Builder/Builder.js b/Construct [BP]/scripts/classes/Builder/Builder.js new file mode 100644 index 0000000..0cc67a9 --- /dev/null +++ b/Construct [BP]/scripts/classes/Builder/Builder.js @@ -0,0 +1,22 @@ +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)); + } + + getOption(id) { + return this.options.get(id); + } + + setOption(id, value) { + return this.options.setValue(id, 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 new file mode 100644 index 0000000..c3621e4 --- /dev/null +++ b/Construct [BP]/scripts/classes/Builder/BuilderForm.js @@ -0,0 +1,24 @@ +import { BuilderFormBuilder } from "./BuilderFormBuilder"; +import { Builders } from "./Builders"; + +export class BuilderForm { + constructor(player) { + this.player = player; + } + + show() { + forceShow(this.player, BuilderFormBuilder.buildSettings(this.player)).then((response) => { + if (response.canceled) return; + this.applySettings(response.formValues); + }); + } + + applySettings(formValues) { + const optionIds = Builders.get(this.player.id).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}`); + } + } +} \ No newline at end of file diff --git a/Construct [BP]/scripts/classes/Builder/BuilderFormBuilder.js b/Construct [BP]/scripts/classes/Builder/BuilderFormBuilder.js new file mode 100644 index 0000000..b5a43c1 --- /dev/null +++ b/Construct [BP]/scripts/classes/Builder/BuilderFormBuilder.js @@ -0,0 +1,15 @@ +import { ModalFormData } from "@minecraft/server-ui"; +import { MenuFormBuilder } from "../MenuFormBuilder"; +import { Builders } from "./Builders"; + +export class BuilderFormBuilder { + static buildSettings(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()); + } + } +} \ No newline at end of file diff --git a/Construct [BP]/scripts/classes/Builder/BuilderOption.js b/Construct [BP]/scripts/classes/Builder/BuilderOption.js new file mode 100644 index 0000000..3153100 --- /dev/null +++ b/Construct [BP]/scripts/classes/Builder/BuilderOption.js @@ -0,0 +1,53 @@ +import { BuilderOptions } from "./BuilderOptions"; +import { Option } from "../Option"; + +export class BuilderOption extends Option { + playerId; + identifier; + displayName; + description; + value; + #onEnable; + #onDisable; + #DP_NAMESPACE = "builderOptions"; + + constructor({ player, identifier, displayName, description, howToUse, onEnableCallback = () => {}, onDisableCallback = () => {} }) { + this.playerId = player.id; + this.identifier = identifier; + this.displayName = displayName; + this.description = description; + this.howToUse = howToUse; + this.#onEnable = onEnableCallback; + this.#onDisable = onDisableCallback; + 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; + } + + setValue(value) { + if (value) + this.#onEnable(); + else + this.#onDisable(); + if (this.value !== value) { + this.value = value; + return value; + } + return void 0; + } +} \ No newline at end of file diff --git a/Construct [BP]/scripts/classes/Builder/BuilderOptions.js b/Construct [BP]/scripts/classes/Builder/BuilderOptions.js new file mode 100644 index 0000000..9a3927d --- /dev/null +++ b/Construct [BP]/scripts/classes/Builder/BuilderOptions.js @@ -0,0 +1,34 @@ +import { world } from "@minecraft/server"; +import { BuilderOption } from "./BuilderOption"; + +export class BuilderOptions { + playerId = void 0; + options = {}; + + constructor(playerId) { + this.playerId = playerId; + this.loadOptions(); + } + + loadOptions() { + const optionDPs = world.getDynamicPropertyIds().filter((id) => id.startsWith(`builderOptions:${this.playerId}:`)); + for (const optionDP of optionDPs) + new BuilderOption(JSON.parse(world.getDynamicProperty(optionDP))); + } + + add(builderOption) { + this.options[builderOption.identifer] = builderOption; + } + + get(id) { + return this.options[id]; + } + + getValue(id) { + return this.options[id].getValue(); + } + + setValue(id, value) { + return this.options[id].setValue(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 new file mode 100644 index 0000000..2cd6458 --- /dev/null +++ b/Construct [BP]/scripts/classes/Builder/Builders.js @@ -0,0 +1,25 @@ +import { world } from "@minecraft/server"; +import { Builder } from "./Builder"; + +export class Builders { + static builders = {}; + + add(playerId) { + this.builders[playerId] = new Builder(playerId); + } + + remove(playerId) { + delete this.builders[playerId]; + } + + get(id) { + return this.builders[id]; + } + + onJoin(playerId) { + this.add(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 diff --git a/Construct [BP]/scripts/classes/InstanceEditForm.js b/Construct [BP]/scripts/classes/Instance/InstanceForm.js similarity index 61% rename from Construct [BP]/scripts/classes/InstanceEditForm.js rename to Construct [BP]/scripts/classes/Instance/InstanceForm.js index 208ef1c..ebdf90e 100644 --- a/Construct [BP]/scripts/classes/InstanceEditForm.js +++ b/Construct [BP]/scripts/classes/Instance/InstanceForm.js @@ -1,34 +1,34 @@ import { structureCollection } from './StructureCollection'; -import { MenuForm } from './MenuForm'; -import { forceShow } from '../utils'; -import { InstanceEditButtons } from './enums/InstanceEditButtons'; -import { InstanceEditFormBuilder } from './InstanceEditFormBuilder'; +import { MenuForm } from '../MenuForm'; +import { forceShow } from '../../utils'; +import { InstanceButtons } from '../enums/InstanceButtons'; +import { InstanceFormBuilder } from './InstanceFormBuilder'; import { FormCancelationReason } from '@minecraft/server-ui'; -export class InstanceEditForm { +export class InstanceForm { instanceName; #buttons = { isEnabled: [ - InstanceEditButtons.NextLayer, - InstanceEditButtons.PreviousLayer, - InstanceEditButtons.SetLayer, - InstanceEditButtons.Move, - InstanceEditButtons.Statistics, - InstanceEditButtons.Settings, - InstanceEditButtons.Rename, - InstanceEditButtons.Disable, + InstanceButtons.NextLayer, + InstanceButtons.PreviousLayer, + InstanceButtons.SetLayer, + InstanceButtons.Move, + InstanceButtons.Statistics, + InstanceButtons.Settings, + InstanceButtons.Rename, + InstanceButtons.Disable, ], isNotEnabledAndIsNotPlaced: [ - InstanceEditButtons.Place, - InstanceEditButtons.Rename + InstanceButtons.Place, + InstanceButtons.Rename ], isNotEnabledButIsPlaced: [ - InstanceEditButtons.Enable, - InstanceEditButtons.Rename + InstanceButtons.Enable, + InstanceButtons.Rename ], common: [ - InstanceEditButtons.Delete, - InstanceEditButtons.MainMenu + InstanceButtons.Delete, + InstanceButtons.MainMenu ] } @@ -41,7 +41,7 @@ export class InstanceEditForm { show() { const currentOptions = this.getActiveOptions(); - forceShow(this.player, InstanceEditFormBuilder.buildInstance(this.instance, currentOptions)).then((response) => { + forceShow(this.player, InstanceFormBuilder.buildInstance(this.instance, currentOptions)).then((response) => { if (response.canceled) return; this.handleOption(currentOptions[response.selection]); }); @@ -59,48 +59,48 @@ export class InstanceEditForm { if (!this.instance.hasLayers()) currentOptions = currentOptions.filter(option => - option !== InstanceEditButtons.SetLayer - && option !== InstanceEditButtons.NextLayer - && option !== InstanceEditButtons.PreviousLayer + option !== InstanceButtons.SetLayer + && option !== InstanceButtons.NextLayer + && option !== InstanceButtons.PreviousLayer ); return currentOptions; } handleOption(option) { switch (option) { - case InstanceEditButtons.Enable: + case InstanceButtons.Enable: this.instance.enable(); break; - case InstanceEditButtons.Disable: + case InstanceButtons.Disable: this.instance.disable(); break; - case InstanceEditButtons.Place: + case InstanceButtons.Place: this.instance.place(this.player.dimension.id, this.player.location); break; - case InstanceEditButtons.Rename: + case InstanceButtons.Rename: this.renameInstanceForm(); break; - case InstanceEditButtons.Delete: + case InstanceButtons.Delete: structureCollection.delete(this.instanceName); break; - case InstanceEditButtons.NextLayer: + case InstanceButtons.NextLayer: this.instance.increaseLayer(); - new InstanceEditForm(this.player, this.instanceName); + new InstanceForm(this.player, this.instanceName); break; - case InstanceEditButtons.PreviousLayer: + case InstanceButtons.PreviousLayer: this.instance.decreaseLayer(); - new InstanceEditForm(this.player, this.instanceName); + new InstanceForm(this.player, this.instanceName); break; - case InstanceEditButtons.Settings: + case InstanceButtons.Settings: this.settingsForm(); break; - case InstanceEditButtons.Move: + case InstanceButtons.Move: this.instance.move(this.player.dimension.id, this.player.location); break; - case InstanceEditButtons.Statistics: + case InstanceButtons.Statistics: this.statisticsForm(); break; - case InstanceEditButtons.MainMenu: + case InstanceButtons.MainMenu: new MenuForm(this.player, { jumpToInstance: false }); break; default: @@ -110,7 +110,7 @@ export class InstanceEditForm { } renameInstanceForm() { - InstanceEditFormBuilder.buildRenameInstance(this.instanceName).show(this.player).then((response) => { + InstanceFormBuilder.buildRenameInstance(this.instanceName).show(this.player).then((response) => { if (response.canceled) return; const newName = response.formValues[0]; @@ -129,7 +129,7 @@ export class InstanceEditForm { } setLayerForm() { - InstanceEditFormBuilder.buildSetLayer(this.instance.getBounds().max.y, this.instance.getLayer()).show(this.player).then((response) => { + InstanceFormBuilder.buildSetLayer(this.instance.getBounds().max.y, this.instance.getLayer()).show(this.player).then((response) => { if (response.canceled) return; this.instance.setLayer(parseInt(response.formValues[0])); @@ -137,7 +137,7 @@ export class InstanceEditForm { } async statisticsForm() { - const statsForm = await InstanceEditFormBuilder.buildStatistics(this.instance) + const statsForm = await InstanceFormBuilder.buildStatistics(this.instance) statsForm.form.show(this.player).then((response) => { if (response.canceled && response.cancelationReason === FormCancelationReason.UserBusy) this.player.sendMessage(statsForm.stats); @@ -145,7 +145,7 @@ export class InstanceEditForm { } settingsForm() { - InstanceEditFormBuilder.buildSettings(this.instance).show(this.player).then((response) => { + InstanceFormBuilder.buildSettings(this.instance).show(this.player).then((response) => { if (response.canceled) return; this.instance.setVerifierEnabled(response.formValues[0]); diff --git a/Construct [BP]/scripts/classes/InstanceEditFormBuilder.js b/Construct [BP]/scripts/classes/Instance/InstanceFormBuilder.js similarity index 91% rename from Construct [BP]/scripts/classes/InstanceEditFormBuilder.js rename to Construct [BP]/scripts/classes/Instance/InstanceFormBuilder.js index fae3ac3..848efb7 100644 --- a/Construct [BP]/scripts/classes/InstanceEditFormBuilder.js +++ b/Construct [BP]/scripts/classes/Instance/InstanceFormBuilder.js @@ -1,10 +1,10 @@ import { ActionFormData, ModalFormData } from '@minecraft/server-ui'; -import { MenuFormBuilder } from './MenuFormBuilder'; +import { MenuFormBuilder } from '../MenuFormBuilder'; import { StructureVerifier } from './StructureVerifier'; import { StructureStatistics } from './StructureStatistics'; import { TicksPerSecond } from '@minecraft/server'; -export class InstanceEditFormBuilder { +export class InstanceFormBuilder { static buildInstance(instance, options) { const location = instance.getLocation(); const form = new ActionFormData() @@ -41,7 +41,7 @@ export class InstanceEditFormBuilder { return new ModalFormData() .title(MenuFormBuilder.menuTitle) .label('Use the slider to select the layer. Use 0 for all layers.') - .toggle('Toggle block validation.', instance.options.verifier.isEnabled) + .toggle('Block Validation', instance.options.verifier.isEnabled) .slider("Layer", 0, maxLayer, 1, currentLayer) .submitButton('§aApply'); } diff --git a/Construct [BP]/scripts/classes/InstanceOptions.js b/Construct [BP]/scripts/classes/Instance/InstanceOptions.js similarity index 68% rename from Construct [BP]/scripts/classes/InstanceOptions.js rename to Construct [BP]/scripts/classes/Instance/InstanceOptions.js index afac847..34f834d 100644 --- a/Construct [BP]/scripts/classes/InstanceOptions.js +++ b/Construct [BP]/scripts/classes/Instance/InstanceOptions.js @@ -1,7 +1,9 @@ -import { Vector } from "../lib/Vector"; +import { Vector } from "../../lib/Vector"; import { world } from "@minecraft/server"; +import { Option } from "../Option"; -export class InstanceOptions { +export class InstanceOptions extends Option { + #DP_NAMESPACE = "instanceOptions"; instanceName = void 0; structureId = void 0; isEnabled = false; @@ -26,26 +28,16 @@ export class InstanceOptions { } save() { - world.setDynamicProperty(`instanceOptions:${this.instanceName}`, JSON.stringify(this)); + this.saveToDP(this.#DP_NAMESPACE, this.instanceName, 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.loadFromDP(this.#DP_NAMESPACE, this.instanceName); this.worldLocation = Vector.from(this.worldLocation); } clear() { - world.setDynamicProperty(`instanceOptions:${this.instanceName}`, void 0); + this.clearDP(this.#DP_NAMESPACE, this.instanceName); } getDimension() { diff --git a/Construct [BP]/scripts/classes/MenuForm.js b/Construct [BP]/scripts/classes/MenuForm.js index 03b8c50..10158e6 100644 --- a/Construct [BP]/scripts/classes/MenuForm.js +++ b/Construct [BP]/scripts/classes/MenuForm.js @@ -1,7 +1,7 @@ import { forceShow } from '../utils'; import { structureCollection } from './StructureCollection'; import { MenuFormBuilder } from './MenuFormBuilder'; -import { InstanceEditForm } from './InstanceEditForm'; +import { InstanceForm } from './InstanceForm'; export class MenuForm { constructor(player, { jumpToInstance = false, instanceName = void 0 } = {}) { @@ -14,14 +14,14 @@ export class MenuForm { if (!instanceName) instanceName = structureCollection.getStructure(this.player.dimension.id, this.player.location, { useActiveLayer: false })?.getName(); if (structureCollection.get(instanceName)) { - new InstanceEditForm(this.player, instanceName); + new InstanceForm(this.player, instanceName); return; } } instanceName = await this.getInstanceNameFromForm(); if (!instanceName) return; - new InstanceEditForm(this.player, instanceName); + new InstanceForm(this.player, instanceName); } async getInstanceNameFromForm() { diff --git a/Construct [BP]/scripts/classes/MenuFormBuilder.js b/Construct [BP]/scripts/classes/MenuFormBuilder.js index 390377a..dcde0bd 100644 --- a/Construct [BP]/scripts/classes/MenuFormBuilder.js +++ b/Construct [BP]/scripts/classes/MenuFormBuilder.js @@ -2,7 +2,7 @@ import { ActionFormData, ModalFormData } from '@minecraft/server-ui'; import { structureCollection } from './StructureCollection'; export class MenuFormBuilder { - static menuTitle = '§l§2StrucTool §8Menu'; + static menuTitle = '§l§2Construct §8Menu'; static buildAllInstanceName() { const allInstanceNameForm = new ActionFormData() diff --git a/Construct [BP]/scripts/classes/Option.js b/Construct [BP]/scripts/classes/Option.js new file mode 100644 index 0000000..72d39b0 --- /dev/null +++ b/Construct [BP]/scripts/classes/Option.js @@ -0,0 +1,25 @@ +import { world } from "@minecraft/server"; + +export class Option { + saveToDP(namespace, id, data) { + world.setDynamicProperty(`${namespace}:${id}`, JSON.stringify(data)); + } + + loadFromDP(namespace, id) { + try { + const options = JSON.parse(world.getDynamicProperty(`${namespace}:${id}`)); + if (options) + Object.assign(this, options); + else + throw new Error("Options not found"); + } catch { + this.saveToDP(namespace, id, {}); + const options = JSON.parse(world.getDynamicProperty(`${namespace}:${id}`) || "{}"); + Object.assign(this, options); + } + } + + clearDP(namespace, id) { + world.setDynamicProperty(`${namespace}:${id}`, void 0); + } +} \ No newline at end of file diff --git a/Construct [BP]/scripts/classes/Outliner.js b/Construct [BP]/scripts/classes/Outliner.js index 6343e58..be1baf8 100644 --- a/Construct [BP]/scripts/classes/Outliner.js +++ b/Construct [BP]/scripts/classes/Outliner.js @@ -5,7 +5,7 @@ export class Outliner { dimension; min = new Vector(); max = new Vector(); - drawParticle = "structool:outline"; + drawParticle = "construct:outline"; drawFrequency = 10; #drawParticles = []; diff --git a/Construct [BP]/scripts/classes/Structure.js b/Construct [BP]/scripts/classes/Structure/Structure.js similarity index 96% rename from Construct [BP]/scripts/classes/Structure.js rename to Construct [BP]/scripts/classes/Structure/Structure.js index 983d6bb..58a36ef 100644 --- a/Construct [BP]/scripts/classes/Structure.js +++ b/Construct [BP]/scripts/classes/Structure/Structure.js @@ -9,7 +9,7 @@ export class Structure { this.structureId = structureId; this.#structure = world.structureManager.get(structureId); if (!this.#structure) - throw new Error(`[StrucTool] Structure '${structureId}' not found.`); + throw new Error(`[Construct] Structure '${structureId}' not found.`); this.#structure.saveToWorld(); } diff --git a/Construct [BP]/scripts/classes/StructureCollection.js b/Construct [BP]/scripts/classes/Structure/StructureCollection.js similarity index 98% rename from Construct [BP]/scripts/classes/StructureCollection.js rename to Construct [BP]/scripts/classes/Structure/StructureCollection.js index 299067c..26f1907 100644 --- a/Construct [BP]/scripts/classes/StructureCollection.js +++ b/Construct [BP]/scripts/classes/Structure/StructureCollection.js @@ -17,7 +17,7 @@ class StructureCollection { structureId = InstanceOptions.getInstanceStrucetureId(instanceName); this.structures[instanceName] = new StructureInstance(instanceName, structureId); } catch (e) { - world.sendMessage(`§c[StrucTool] Error loading structure instance '${instanceName}'. It will be removed.`); + world.sendMessage(`§c[Construct] Error loading structure instance '${instanceName}'. It will be removed.`); world.setDynamicProperty(id, void 0); throw e; } diff --git a/Construct [BP]/scripts/classes/StructureInstance.js b/Construct [BP]/scripts/classes/Structure/StructureInstance.js similarity index 95% rename from Construct [BP]/scripts/classes/StructureInstance.js rename to Construct [BP]/scripts/classes/Structure/StructureInstance.js index 8104ff6..8117fef 100644 --- a/Construct [BP]/scripts/classes/StructureInstance.js +++ b/Construct [BP]/scripts/classes/Structure/StructureInstance.js @@ -70,7 +70,7 @@ export class StructureInstance { getActiveBounds() { if (!this.options.isEnabled) - throw new Error(`[StrucTool] Instance '${this.options.instanceName}' is not placed.`); + throw new Error(`[Construct] Instance '${this.options.instanceName}' is not placed.`); if (this.hasLayerSelected()) return this.getLayerBounds(this.getLayer()); return this.getBounds(); @@ -78,7 +78,7 @@ export class StructureInstance { getLayerBounds(layer) { if (!this.options.isEnabled) - throw new Error(`[StrucTool] Instance '${this.options.instanceName}' is not placed.`); + throw new Error(`[Construct] Instance '${this.options.instanceName}' is not placed.`); const min = this.structure.getMin(); const max = this.structure.getMax(); return { @@ -105,7 +105,7 @@ export class StructureInstance { getActiveBlocks() { if (!this.options.isEnabled) - throw new Error(`[StrucTool] Instance '${this.options.instanceName}' is not placed.`); + throw new Error(`[Construct] Instance '${this.options.instanceName}' is not placed.`); if (this.hasLayerSelected()) return this.getLayerBlocks(this.getLayer()); return this.getAllBlocks(); @@ -126,7 +126,7 @@ export class StructureInstance { getAllActiveLocations() { if (!this.options.isEnabled) - throw new Error(`[StrucTool] Instance '${this.options.instanceName}' is not placed.`); + throw new Error(`[Construct] Instance '${this.options.instanceName}' is not placed.`); if (this.hasLayerSelected()) return this.structure.getLayerLocations(this.getLayer()-1); else @@ -187,7 +187,7 @@ export class StructureInstance { setLayer(layer) { if (layer < 0 || layer > this.getMaxLayer()) - throw new Error(`[StrucTool] Layer ${layer} is out of bounds.`); + throw new Error(`[Construct] Layer ${layer} is out of bounds.`); this.options.setLayer(layer); this.refreshBox(); } diff --git a/Construct [BP]/scripts/classes/StructureOutliner.js b/Construct [BP]/scripts/classes/Structure/StructureOutliner.js similarity index 100% rename from Construct [BP]/scripts/classes/StructureOutliner.js rename to Construct [BP]/scripts/classes/Structure/StructureOutliner.js diff --git a/Construct [BP]/scripts/classes/StructureStatistics.js b/Construct [BP]/scripts/classes/Structure/StructureStatistics.js similarity index 100% rename from Construct [BP]/scripts/classes/StructureStatistics.js rename to Construct [BP]/scripts/classes/Structure/StructureStatistics.js diff --git a/Construct [BP]/scripts/classes/StructureVerifier.js b/Construct [BP]/scripts/classes/Structure/StructureVerifier.js similarity index 100% rename from Construct [BP]/scripts/classes/StructureVerifier.js rename to Construct [BP]/scripts/classes/Structure/StructureVerifier.js diff --git a/Construct [BP]/scripts/classes/enums/InstanceEditButtons.js b/Construct [BP]/scripts/classes/enums/InstanceButtons.js similarity index 87% rename from Construct [BP]/scripts/classes/enums/InstanceEditButtons.js rename to Construct [BP]/scripts/classes/enums/InstanceButtons.js index 08fcd06..a066923 100644 --- a/Construct [BP]/scripts/classes/enums/InstanceEditButtons.js +++ b/Construct [BP]/scripts/classes/enums/InstanceButtons.js @@ -1,4 +1,4 @@ -export const InstanceEditButtons = Object.freeze({ +export const InstanceButtons = Object.freeze({ Unknown: 'Unknown', MainMenu: '<<', Place: '§aPlace Instance', diff --git a/Construct [BP]/scripts/commands/construct.js b/Construct [BP]/scripts/commands/construct.js index f6454c5..34ad208 100644 --- a/Construct [BP]/scripts/commands/construct.js +++ b/Construct [BP]/scripts/commands/construct.js @@ -6,9 +6,9 @@ import { MenuForm } from '../classes/MenuForm'; const ACTION_ITEM = 'minecraft:paper'; const menuCmd = new Command({ - name: 'structool', - description: { text: 'Opens the StrucTool Menu. Using a paper will also open the menu.' }, - usage: 'structool', + name: 'construct', + description: { text: 'Opens the Construct Menu. Using a paper will also open the menu.' }, + usage: 'construct', callback: (sender) => openMenu(sender) }); extension.addCommand(menuCmd); diff --git a/Construct [BP]/scripts/config.js b/Construct [BP]/scripts/config.js index 39efd76..e1de1d4 100644 --- a/Construct [BP]/scripts/config.js +++ b/Construct [BP]/scripts/config.js @@ -2,7 +2,7 @@ import { CanopyExtension } from './lib/canopy/CanopyExtension'; export const extension = new CanopyExtension({ author: 'ForestOfLight', - name: 'StrucTool', - description: 'Survival building extension for §l§aCanopy§r!', + name: 'Construct', + description: 'Make building structures in survival easier!', version: '1.0.0' }); \ No newline at end of file diff --git a/Construct [BP]/scripts/lib/canopy/Rule.js b/Construct [BP]/scripts/lib/canopy/Rule.js deleted file mode 100644 index cafae2f..0000000 --- a/Construct [BP]/scripts/lib/canopy/Rule.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @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 = [], onEnableCallback = () => {}, onDisableCallback = () => {} }) { - this.#identifier = identifier; - this.#description = description; - this.#contingentRules = contingentRules; - this.#independentRules = independentRules; - this.onEnable = onEnableCallback; - this.onDisable = onDisableCallback; - } - - 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) { - if (value === true) - this.onEnable(); - else - this.onDisable(); - world.setDynamicProperty(this.#identifier, value); - } -} - -export default Rule; \ No newline at end of file diff --git a/Construct [BP]/scripts/rules/easyPlace.js b/Construct [BP]/scripts/options/easyPlace.js similarity index 92% rename from Construct [BP]/scripts/rules/easyPlace.js rename to Construct [BP]/scripts/options/easyPlace.js index 519f1f6..0192982 100644 --- a/Construct [BP]/scripts/rules/easyPlace.js +++ b/Construct [BP]/scripts/options/easyPlace.js @@ -1,5 +1,4 @@ -import { Rule } from '../lib/canopy/CanopyExtension'; -import { extension } from '../config'; +import { BuilderOption } from '../classes/Builder/BuilderOption'; import { BlockPermutation, EntityComponentTypes, GameMode, ItemStack, system, world } from '@minecraft/server'; import { structureCollection } from '../classes/StructureCollection'; import { bannedBlocks, bannedToValidBlockMap, whitelistedBlockStates, resetToBlockStates, bannedDimensionBlocks, specialItemPlacementConversions, @@ -8,13 +7,14 @@ import { fetchMatchingItemSlot } from '../utils'; const ACTION_SLOT = 35; -const easyPlace = new Rule({ +new BuilderOption({ identifier: 'easyPlace', - description: { text: "Automatically places the correct block in a structure (paper named 'easyPlace' in bottom right inventory slot)." }, + 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); } }) -extension.addRule(easyPlace); function onPlayerPlaceBlock(event) { const { player, block, permutationBeingPlaced } = event; @@ -30,7 +30,7 @@ function hasActionItemInCorrectSlot(player) { if (!inventory) return false; const actionSlot = inventory.getSlot(ACTION_SLOT); - return actionSlot.hasItem() && actionSlot.typeId === 'minecraft:paper' && actionSlot.nameTag === 'easyPlace'; + return actionSlot.hasItem() && actionSlot.typeId === 'minecraft:paper' && actionSlot.nameTag === 'Easy Place'; } function tryPlaceBlock(event, player, block, structureBlock) { diff --git a/Construct [BP]/scripts/rules/fastEasyPlace.js b/Construct [BP]/scripts/options/fastEasyPlace.js similarity index 93% rename from Construct [BP]/scripts/rules/fastEasyPlace.js rename to Construct [BP]/scripts/options/fastEasyPlace.js index fde0289..8bef942 100644 --- a/Construct [BP]/scripts/rules/fastEasyPlace.js +++ b/Construct [BP]/scripts/options/fastEasyPlace.js @@ -1,18 +1,18 @@ -import { Rule } from '../lib/canopy/CanopyExtension'; -import { extension } from '../config'; +import { BuilderOption } from '../classes/Builder/BuilderOption'; import { BlockPermutation, EntityComponentTypes, EquipmentSlot, GameMode, ItemStack, system, world } from '@minecraft/server'; import { bannedBlocks, bannedToValidBlockMap, whitelistedBlockStates, resetToBlockStates, bannedDimensionBlocks, specialItemPlacementConversions, blockIdToItemStackMap } from '../data'; import { Raycaster } from '../classes/Raycaster'; let runner = void 0; -const easyPlace = new Rule({ +new BuilderOption({ identifier: 'fastEasyPlace', - description: { text: "Looking at a structure block with a paper named 'easyPlace' in your hand will place it." }, + 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); } }) -extension.addRule(easyPlace); function onTick() { for (const player of world.getAllPlayers()) { @@ -35,7 +35,7 @@ function isHoldingActionItem(player) { const mainhandItemStack = player.getComponent(EntityComponentTypes.Equippable).getEquipment(EquipmentSlot.Mainhand); if (!mainhandItemStack) return false; - return mainhandItemStack.typeId === 'minecraft:paper' && mainhandItemStack.nameTag === 'easyPlace'; + return mainhandItemStack.typeId === 'minecraft:paper' && mainhandItemStack.nameTag === 'Easy Place'; } function tryPlaceBlock(player, worldBlock, structureBlock) { diff --git a/Construct [BP]/scripts/rules/materialGrabber.js b/Construct [BP]/scripts/options/materialGrabber.js similarity index 71% rename from Construct [BP]/scripts/rules/materialGrabber.js rename to Construct [BP]/scripts/options/materialGrabber.js index 411a9f3..4d5352f 100644 --- a/Construct [BP]/scripts/rules/materialGrabber.js +++ b/Construct [BP]/scripts/options/materialGrabber.js @@ -1,10 +1,11 @@ -import { Rule } from '../lib/canopy/CanopyExtension'; -import { extension } from '../config'; +import { BuilderOption } from '../classes/Builder/BuilderOption'; import { world } from '@minecraft/server'; -const easyPlace = new Rule({ +new BuilderOption({ identifier: 'materialGrabber', - description: { text: "Automatically grabs structure materials out of inventories. Use a paper named 'materialGrabber' to get started." }, + 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); @@ -14,7 +15,6 @@ const easyPlace = new Rule({ world.beforeEvents.playerInteractWithEntity.unsubscribe(onPlayerInteract); } }) -extension.addRule(easyPlace); function onPlayerInteract(event) { // get inventory diff --git a/Construct [RP]/manifest.json b/Construct [RP]/manifest.json index 6ab353c..abb11fc 100644 --- a/Construct [RP]/manifest.json +++ b/Construct [RP]/manifest.json @@ -1,7 +1,7 @@ { "format_version": 2, "header": { - "name": "StrucTool [RP] v1.0.0", + "name": "Construct [RP] v1.0.0", "description": "Survival building extension for §l§aCanopy§r by §aForestOfLight§r.", "uuid": "375ec465-3dc1-429f-8b4c-a337889e1ed4", "version": [1, 0, 0], @@ -16,7 +16,7 @@ ], "dependencies": [ { - "uuid": "8c0c0153-d8b9-482a-889f-aef922b8fe58", // StrucTool BP + "uuid": "8c0c0153-d8b9-482a-889f-aef922b8fe58", // Construct BP "version": [1, 0, 0] }, { diff --git a/Construct [RP]/particles/blockoverlay_xy.particle.json b/Construct [RP]/particles/blockoverlay_xy.particle.json index 541e12a..c3d1e9d 100644 --- a/Construct [RP]/particles/blockoverlay_xy.particle.json +++ b/Construct [RP]/particles/blockoverlay_xy.particle.json @@ -2,7 +2,7 @@ "format_version": "1.10.0", "particle_effect": { "description": { - "identifier": "structool:blockoverlay_xy", + "identifier": "construct:blockoverlay_xy", "basic_render_parameters": { "material": "particles_blend", "texture": "textures/particle/white" diff --git a/Construct [RP]/particles/blockoverlay_xz.particle.json b/Construct [RP]/particles/blockoverlay_xz.particle.json index 4140060..408c139 100644 --- a/Construct [RP]/particles/blockoverlay_xz.particle.json +++ b/Construct [RP]/particles/blockoverlay_xz.particle.json @@ -2,7 +2,7 @@ "format_version": "1.10.0", "particle_effect": { "description": { - "identifier": "structool:blockoverlay_xz", + "identifier": "construct:blockoverlay_xz", "basic_render_parameters": { "material": "particles_blend", "texture": "textures/particle/white" diff --git a/Construct [RP]/particles/blockoverlay_yz.particle.json b/Construct [RP]/particles/blockoverlay_yz.particle.json index 76b4ef5..17246a0 100644 --- a/Construct [RP]/particles/blockoverlay_yz.particle.json +++ b/Construct [RP]/particles/blockoverlay_yz.particle.json @@ -2,7 +2,7 @@ "format_version": "1.10.0", "particle_effect": { "description": { - "identifier": "structool:blockoverlay_yz", + "identifier": "construct:blockoverlay_yz", "basic_render_parameters": { "material": "particles_blend", "texture": "textures/particle/white" diff --git a/Construct [RP]/particles/outline.particle.json b/Construct [RP]/particles/outline.particle.json index 7fd6ad6..304db04 100644 --- a/Construct [RP]/particles/outline.particle.json +++ b/Construct [RP]/particles/outline.particle.json @@ -2,7 +2,7 @@ "format_version": "1.10.0", "particle_effect": { "description": { - "identifier": "structool:outline", + "identifier": "construct:outline", "basic_render_parameters": { "material": "particles_alpha", "texture": "textures/particle/particles"