Merge branch 'player-settings'
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"format_version": 2,
|
"format_version": 2,
|
||||||
"header": {
|
"header": {
|
||||||
"name": "StrucTool [BP] v1.0.0",
|
"name": "Construct [BP] v1.0.0",
|
||||||
"description": "Survival building extension for §l§aCanopy§r by §aForestOfLight§r.",
|
"description": "Survival building addon by §aForestOfLight§r.",
|
||||||
"uuid": "8c0c0153-d8b9-482a-889f-aef922b8fe58",
|
"uuid": "8c0c0153-d8b9-482a-889f-aef922b8fe58",
|
||||||
"min_engine_version": [1, 21, 70],
|
"min_engine_version": [1, 21, 70],
|
||||||
"version": [1, 0, 0]
|
"version": [1, 0, 0]
|
||||||
@@ -33,16 +33,8 @@
|
|||||||
"version": "2.0.0-beta"
|
"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]
|
"version": [1, 0, 0]
|
||||||
},
|
|
||||||
{
|
|
||||||
"uuid": "bcf34368-ed0c-4cf7-938e-582cccf9950d", // Canopy RP
|
|
||||||
"version": [1, 0, 3]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uuid": "7f6b23df-a583-476b-b0e4-87457e65f7c0", // Canopy BP
|
|
||||||
"version": [1, 3, 9]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { BuilderOptions } from "./BuilderOptions";
|
||||||
|
|
||||||
|
export class Builder {
|
||||||
|
constructor(playerId) {
|
||||||
|
this.playerId = playerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
isOptionEnabled(optionId) {
|
||||||
|
return BuilderOptions.isEnabled(optionId, this.playerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
setOption(optionId, value) {
|
||||||
|
return BuilderOptions.setValue(optionId, this.playerId, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { BuilderFormBuilder } from "./BuilderFormBuilder";
|
||||||
|
import { BuilderOptions } from "./BuilderOptions";
|
||||||
|
import { forceShow } from '../../utils';
|
||||||
|
|
||||||
|
export class BuilderForm {
|
||||||
|
constructor(player) {
|
||||||
|
this.player = player;
|
||||||
|
this.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
forceShow(this.player, BuilderFormBuilder.buildBuilderOptions(this.player)).then((response) => {
|
||||||
|
if (response.canceled) return;
|
||||||
|
this.applySettings(response.formValues);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
applySettings(formValues) {
|
||||||
|
const optionIds = BuilderOptions.getOptionIds();
|
||||||
|
for (let i = 0; i < optionIds.length; i++) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { ModalFormData } from "@minecraft/server-ui";
|
||||||
|
import { MenuFormBuilder } from "../MenuFormBuilder";
|
||||||
|
import { BuilderOptions } from "./BuilderOptions";
|
||||||
|
|
||||||
|
export class BuilderFormBuilder {
|
||||||
|
static buildBuilderOptions(player) {
|
||||||
|
const form = new ModalFormData()
|
||||||
|
.title(MenuFormBuilder.menuTitle);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { BuilderOptions } from "./BuilderOptions";
|
||||||
|
import { world } from "@minecraft/server";
|
||||||
|
|
||||||
|
export class BuilderOption {
|
||||||
|
identifier;
|
||||||
|
displayName;
|
||||||
|
description;
|
||||||
|
howToUse;
|
||||||
|
#onEnable;
|
||||||
|
#onDisable;
|
||||||
|
#DP_NAMESPACE = "builderOptions";
|
||||||
|
|
||||||
|
constructor({ identifier, displayName, description, howToUse, onEnableCallback = () => {}, onDisableCallback = () => {} }) {
|
||||||
|
this.identifier = identifier;
|
||||||
|
this.displayName = displayName;
|
||||||
|
this.description = description;
|
||||||
|
this.howToUse = howToUse;
|
||||||
|
this.#onEnable = onEnableCallback;
|
||||||
|
this.#onDisable = onDisableCallback;
|
||||||
|
BuilderOptions.add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
isEnabled(playerId) {
|
||||||
|
return world.getDynamicProperty(`${this.#DP_NAMESPACE}:${playerId}:${this.identifier}`) === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(playerId, value) {
|
||||||
|
if (value)
|
||||||
|
this.#onEnable(playerId);
|
||||||
|
else
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
export class BuilderOptions {
|
||||||
|
static options = {};
|
||||||
|
|
||||||
|
static add(builderOption) {
|
||||||
|
this.options[builderOption.identifier] = builderOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get(optionId) {
|
||||||
|
return this.options[optionId];
|
||||||
|
}
|
||||||
|
|
||||||
|
static getOptionIds() {
|
||||||
|
return Object.keys(this.options).sort((a, b) => a - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static isEnabled(optionId, playerId) {
|
||||||
|
return this.options[optionId].isEnabled(playerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
static setValue(optionId, playerId, value) {
|
||||||
|
return this.options[optionId].setValue(playerId, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { world } from "@minecraft/server";
|
||||||
|
import { Builder } from "./Builder";
|
||||||
|
|
||||||
|
export class Builders {
|
||||||
|
static builders = {};
|
||||||
|
|
||||||
|
static add(playerId) {
|
||||||
|
if (this.builders[playerId])
|
||||||
|
return;
|
||||||
|
this.builders[playerId] = new Builder(playerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove(playerId) {
|
||||||
|
delete this.builders[playerId];
|
||||||
|
}
|
||||||
|
|
||||||
|
static get(id) {
|
||||||
|
return this.builders[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
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.id));
|
||||||
|
world.afterEvents.worldLoad.subscribe((event) => {
|
||||||
|
for (const player of world.getAllPlayers()) {
|
||||||
|
Builders.onJoin(player.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export class InvalidInstanceError extends Error {
|
||||||
|
constructor(message) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'InvalidInstanceError';
|
||||||
|
}
|
||||||
|
}
|
||||||
+45
-42
@@ -1,34 +1,33 @@
|
|||||||
import { structureCollection } from './StructureCollection';
|
import { structureCollection } from '../Structure/StructureCollection';
|
||||||
import { MenuForm } from './MenuForm';
|
import { MenuForm } from '../MenuForm';
|
||||||
import { forceShow } from '../utils';
|
import { forceShow } from '../../utils';
|
||||||
import { InstanceEditButtons } from './enums/InstanceEditButtons';
|
import { InstanceButtons } from '../Enums/InstanceButtons';
|
||||||
import { InstanceEditFormBuilder } from './InstanceEditFormBuilder';
|
import { InstanceFormBuilder } from './InstanceFormBuilder';
|
||||||
import { FormCancelationReason } from '@minecraft/server-ui';
|
import { FormCancelationReason } from '@minecraft/server-ui';
|
||||||
|
|
||||||
export class InstanceEditForm {
|
export class InstanceForm {
|
||||||
instanceName;
|
instanceName;
|
||||||
#buttons = {
|
#buttons = {
|
||||||
isEnabled: [
|
isEnabled: [
|
||||||
InstanceEditButtons.NextLayer,
|
InstanceButtons.NextLayer,
|
||||||
InstanceEditButtons.PreviousLayer,
|
InstanceButtons.PreviousLayer,
|
||||||
InstanceEditButtons.SetLayer,
|
InstanceButtons.Move,
|
||||||
InstanceEditButtons.Move,
|
InstanceButtons.Statistics,
|
||||||
InstanceEditButtons.Statistics,
|
InstanceButtons.Settings,
|
||||||
InstanceEditButtons.Settings,
|
InstanceButtons.Rename,
|
||||||
InstanceEditButtons.Rename,
|
InstanceButtons.Disable
|
||||||
InstanceEditButtons.Disable,
|
|
||||||
],
|
],
|
||||||
isNotEnabledAndIsNotPlaced: [
|
isNotEnabledAndIsNotPlaced: [
|
||||||
InstanceEditButtons.Place,
|
InstanceButtons.Place,
|
||||||
InstanceEditButtons.Rename
|
InstanceButtons.Rename
|
||||||
],
|
],
|
||||||
isNotEnabledButIsPlaced: [
|
isNotEnabledButIsPlaced: [
|
||||||
InstanceEditButtons.Enable,
|
InstanceButtons.Enable,
|
||||||
InstanceEditButtons.Rename
|
InstanceButtons.Rename
|
||||||
],
|
],
|
||||||
common: [
|
common: [
|
||||||
InstanceEditButtons.Delete,
|
InstanceButtons.Delete,
|
||||||
InstanceEditButtons.MainMenu
|
InstanceButtons.MainMenu
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +40,7 @@ export class InstanceEditForm {
|
|||||||
|
|
||||||
show() {
|
show() {
|
||||||
const currentOptions = this.getActiveOptions();
|
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;
|
if (response.canceled) return;
|
||||||
this.handleOption(currentOptions[response.selection]);
|
this.handleOption(currentOptions[response.selection]);
|
||||||
});
|
});
|
||||||
@@ -59,48 +58,48 @@ export class InstanceEditForm {
|
|||||||
|
|
||||||
if (!this.instance.hasLayers())
|
if (!this.instance.hasLayers())
|
||||||
currentOptions = currentOptions.filter(option =>
|
currentOptions = currentOptions.filter(option =>
|
||||||
option !== InstanceEditButtons.SetLayer
|
option !== InstanceButtons.SetLayer
|
||||||
&& option !== InstanceEditButtons.NextLayer
|
&& option !== InstanceButtons.NextLayer
|
||||||
&& option !== InstanceEditButtons.PreviousLayer
|
&& option !== InstanceButtons.PreviousLayer
|
||||||
);
|
);
|
||||||
return currentOptions;
|
return currentOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOption(option) {
|
handleOption(option) {
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case InstanceEditButtons.Enable:
|
case InstanceButtons.Enable:
|
||||||
this.instance.enable();
|
this.instance.enable();
|
||||||
break;
|
break;
|
||||||
case InstanceEditButtons.Disable:
|
case InstanceButtons.Disable:
|
||||||
this.instance.disable();
|
this.instance.disable();
|
||||||
break;
|
break;
|
||||||
case InstanceEditButtons.Place:
|
case InstanceButtons.Place:
|
||||||
this.instance.place(this.player.dimension.id, this.player.location);
|
this.instance.place(this.player.dimension.id, this.player.location);
|
||||||
break;
|
break;
|
||||||
case InstanceEditButtons.Rename:
|
case InstanceButtons.Rename:
|
||||||
this.renameInstanceForm();
|
this.renameInstanceForm();
|
||||||
break;
|
break;
|
||||||
case InstanceEditButtons.Delete:
|
case InstanceButtons.Delete:
|
||||||
structureCollection.delete(this.instanceName);
|
structureCollection.delete(this.instanceName);
|
||||||
break;
|
break;
|
||||||
case InstanceEditButtons.NextLayer:
|
case InstanceButtons.NextLayer:
|
||||||
this.instance.increaseLayer();
|
this.instance.increaseLayer();
|
||||||
new InstanceEditForm(this.player, this.instanceName);
|
new InstanceForm(this.player, this.instanceName);
|
||||||
break;
|
break;
|
||||||
case InstanceEditButtons.PreviousLayer:
|
case InstanceButtons.PreviousLayer:
|
||||||
this.instance.decreaseLayer();
|
this.instance.decreaseLayer();
|
||||||
new InstanceEditForm(this.player, this.instanceName);
|
new InstanceForm(this.player, this.instanceName);
|
||||||
break;
|
break;
|
||||||
case InstanceEditButtons.Settings:
|
case InstanceButtons.Settings:
|
||||||
this.settingsForm();
|
this.settingsForm();
|
||||||
break;
|
break;
|
||||||
case InstanceEditButtons.Move:
|
case InstanceButtons.Move:
|
||||||
this.instance.move(this.player.dimension.id, this.player.location);
|
this.instance.move(this.player.dimension.id, this.player.location);
|
||||||
break;
|
break;
|
||||||
case InstanceEditButtons.Statistics:
|
case InstanceButtons.Statistics:
|
||||||
this.statisticsForm();
|
this.statisticsForm();
|
||||||
break;
|
break;
|
||||||
case InstanceEditButtons.MainMenu:
|
case InstanceButtons.MainMenu:
|
||||||
new MenuForm(this.player, { jumpToInstance: false });
|
new MenuForm(this.player, { jumpToInstance: false });
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -110,7 +109,7 @@ export class InstanceEditForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renameInstanceForm() {
|
renameInstanceForm() {
|
||||||
InstanceEditFormBuilder.buildRenameInstance(this.instanceName).show(this.player).then((response) => {
|
InstanceFormBuilder.buildRenameInstance(this.instanceName).show(this.player).then((response) => {
|
||||||
if (response.canceled)
|
if (response.canceled)
|
||||||
return;
|
return;
|
||||||
const newName = response.formValues[0];
|
const newName = response.formValues[0];
|
||||||
@@ -129,7 +128,7 @@ export class InstanceEditForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setLayerForm() {
|
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)
|
if (response.canceled)
|
||||||
return;
|
return;
|
||||||
this.instance.setLayer(parseInt(response.formValues[0]));
|
this.instance.setLayer(parseInt(response.formValues[0]));
|
||||||
@@ -137,7 +136,7 @@ export class InstanceEditForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async statisticsForm() {
|
async statisticsForm() {
|
||||||
const statsForm = await InstanceEditFormBuilder.buildStatistics(this.instance)
|
const statsForm = await InstanceFormBuilder.buildStatistics(this.instance)
|
||||||
statsForm.form.show(this.player).then((response) => {
|
statsForm.form.show(this.player).then((response) => {
|
||||||
if (response.canceled && response.cancelationReason === FormCancelationReason.UserBusy)
|
if (response.canceled && response.cancelationReason === FormCancelationReason.UserBusy)
|
||||||
this.player.sendMessage(statsForm.stats);
|
this.player.sendMessage(statsForm.stats);
|
||||||
@@ -145,11 +144,15 @@ export class InstanceEditForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
settingsForm() {
|
settingsForm() {
|
||||||
InstanceEditFormBuilder.buildSettings(this.instance).show(this.player).then((response) => {
|
InstanceFormBuilder.buildSettings(this.instance).show(this.player).then((response) => {
|
||||||
if (response.canceled)
|
if (response.canceled)
|
||||||
return;
|
return;
|
||||||
this.instance.setVerifierEnabled(response.formValues[0]);
|
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]));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+9
-8
@@ -1,10 +1,10 @@
|
|||||||
import { ActionFormData, ModalFormData } from '@minecraft/server-ui';
|
import { ActionFormData, ModalFormData } from '@minecraft/server-ui';
|
||||||
import { MenuFormBuilder } from './MenuFormBuilder';
|
import { MenuFormBuilder } from '../MenuFormBuilder';
|
||||||
import { StructureVerifier } from './StructureVerifier';
|
import { StructureVerifier } from '../Verifier/StructureVerifier';
|
||||||
import { StructureStatistics } from './StructureStatistics';
|
import { StructureStatistics } from '../Structure/StructureStatistics';
|
||||||
import { TicksPerSecond } from '@minecraft/server';
|
import { TicksPerSecond } from '@minecraft/server';
|
||||||
|
|
||||||
export class InstanceEditFormBuilder {
|
export class InstanceFormBuilder {
|
||||||
static buildInstance(instance, options) {
|
static buildInstance(instance, options) {
|
||||||
const location = instance.getLocation();
|
const location = instance.getLocation();
|
||||||
const form = new ActionFormData()
|
const form = new ActionFormData()
|
||||||
@@ -29,7 +29,7 @@ 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, { 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 verification = await structureVerifier.verifyStructure();
|
||||||
const statistics = new StructureStatistics(instance, verification);
|
const statistics = new StructureStatistics(instance, verification);
|
||||||
const statsMessage = statistics.getMessage();
|
const statsMessage = statistics.getMessage();
|
||||||
@@ -41,8 +41,9 @@ export class InstanceEditFormBuilder {
|
|||||||
return new ModalFormData()
|
return new ModalFormData()
|
||||||
.title(MenuFormBuilder.menuTitle)
|
.title(MenuFormBuilder.menuTitle)
|
||||||
.label('Use the slider to select the layer. Use 0 for all layers.')
|
.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)
|
.toggle('Distance-Based Block Validation', instance.verifier.getTrackPlayerDistance() !== 0)
|
||||||
.submitButton('§aApply');
|
.slider("Layer", 0, instance.getMaxLayer(), 1, instance.getLayer())
|
||||||
|
.submitButton('§2Apply');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+8
-15
@@ -1,7 +1,9 @@
|
|||||||
import { Vector } from "../lib/Vector";
|
import { Vector } from "../../lib/Vector";
|
||||||
import { world } from "@minecraft/server";
|
import { world } from "@minecraft/server";
|
||||||
|
import { Option } from "../Option";
|
||||||
|
|
||||||
export class InstanceOptions {
|
export class InstanceOptions extends Option {
|
||||||
|
#DP_NAMESPACE = "instanceOptions";
|
||||||
instanceName = void 0;
|
instanceName = void 0;
|
||||||
structureId = void 0;
|
structureId = void 0;
|
||||||
isEnabled = false;
|
isEnabled = false;
|
||||||
@@ -20,32 +22,23 @@ export class InstanceOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(instanceName, structureId) {
|
constructor(instanceName, structureId) {
|
||||||
|
super();
|
||||||
this.instanceName = instanceName;
|
this.instanceName = instanceName;
|
||||||
this.structureId = structureId;
|
this.structureId = structureId;
|
||||||
this.load();
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
world.setDynamicProperty(`instanceOptions:${this.instanceName}`, JSON.stringify(this));
|
this.saveToDP(this.#DP_NAMESPACE, this.instanceName, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
load() {
|
load() {
|
||||||
try {
|
this.loadFromDP(this.#DP_NAMESPACE, this.instanceName);
|
||||||
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);
|
this.worldLocation = Vector.from(this.worldLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
world.setDynamicProperty(`instanceOptions:${this.instanceName}`, void 0);
|
this.clearDP(this.#DP_NAMESPACE, this.instanceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDimension() {
|
getDimension() {
|
||||||
+10
-10
@@ -1,8 +1,8 @@
|
|||||||
import { Vector } from "../lib/Vector";
|
import { Vector } from "../../lib/Vector";
|
||||||
import { StructureOutliner } from "./StructureOutliner";
|
import { StructureOutliner } from "../Structure/StructureOutliner";
|
||||||
import { StructureVerifier } from "./StructureVerifier";
|
import { StructureVerifier } from "../Verifier/StructureVerifier";
|
||||||
|
import { Structure } from "../Structure/Structure";
|
||||||
import { InstanceOptions } from "./InstanceOptions";
|
import { InstanceOptions } from "./InstanceOptions";
|
||||||
import { Structure } from "./Structure";
|
|
||||||
import { TicksPerSecond } from "@minecraft/server";
|
import { TicksPerSecond } from "@minecraft/server";
|
||||||
|
|
||||||
export class StructureInstance {
|
export class StructureInstance {
|
||||||
@@ -19,11 +19,11 @@ export class StructureInstance {
|
|||||||
|
|
||||||
delete() {
|
delete() {
|
||||||
this.disable();
|
this.disable();
|
||||||
|
this.options.clear();
|
||||||
delete this.options;
|
delete this.options;
|
||||||
delete this.structure;
|
delete this.structure;
|
||||||
delete this.outliner;
|
delete this.outliner;
|
||||||
delete this.verifier;
|
delete this.verifier;
|
||||||
this.options.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshBox() {
|
refreshBox() {
|
||||||
@@ -70,7 +70,7 @@ export class StructureInstance {
|
|||||||
|
|
||||||
getActiveBounds() {
|
getActiveBounds() {
|
||||||
if (!this.options.isEnabled)
|
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())
|
if (this.hasLayerSelected())
|
||||||
return this.getLayerBounds(this.getLayer());
|
return this.getLayerBounds(this.getLayer());
|
||||||
return this.getBounds();
|
return this.getBounds();
|
||||||
@@ -78,7 +78,7 @@ export class StructureInstance {
|
|||||||
|
|
||||||
getLayerBounds(layer) {
|
getLayerBounds(layer) {
|
||||||
if (!this.options.isEnabled)
|
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 min = this.structure.getMin();
|
||||||
const max = this.structure.getMax();
|
const max = this.structure.getMax();
|
||||||
return {
|
return {
|
||||||
@@ -105,7 +105,7 @@ export class StructureInstance {
|
|||||||
|
|
||||||
getActiveBlocks() {
|
getActiveBlocks() {
|
||||||
if (!this.options.isEnabled)
|
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())
|
if (this.hasLayerSelected())
|
||||||
return this.getLayerBlocks(this.getLayer());
|
return this.getLayerBlocks(this.getLayer());
|
||||||
return this.getAllBlocks();
|
return this.getAllBlocks();
|
||||||
@@ -126,7 +126,7 @@ export class StructureInstance {
|
|||||||
|
|
||||||
getAllActiveLocations() {
|
getAllActiveLocations() {
|
||||||
if (!this.options.isEnabled)
|
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())
|
if (this.hasLayerSelected())
|
||||||
return this.structure.getLayerLocations(this.getLayer()-1);
|
return this.structure.getLayerLocations(this.getLayer()-1);
|
||||||
else
|
else
|
||||||
@@ -187,7 +187,7 @@ export class StructureInstance {
|
|||||||
|
|
||||||
setLayer(layer) {
|
setLayer(layer) {
|
||||||
if (layer < 0 || layer > this.getMaxLayer())
|
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.options.setLayer(layer);
|
||||||
this.refreshBox();
|
this.refreshBox();
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { forceShow } from '../utils';
|
import { forceShow } from '../utils';
|
||||||
import { structureCollection } from './StructureCollection';
|
import { structureCollection } from './Structure/StructureCollection';
|
||||||
import { MenuFormBuilder } from './MenuFormBuilder';
|
import { MenuFormBuilder } from './MenuFormBuilder';
|
||||||
import { InstanceEditForm } from './InstanceEditForm';
|
import { InstanceForm } from './Instance/InstanceForm';
|
||||||
|
import { BuilderForm } from './Builder/BuilderForm';
|
||||||
|
|
||||||
export class MenuForm {
|
export class MenuForm {
|
||||||
constructor(player, { jumpToInstance = false, instanceName = void 0 } = {}) {
|
constructor(player, { jumpToInstance = false, instanceName = void 0 } = {}) {
|
||||||
@@ -13,15 +14,15 @@ export class MenuForm {
|
|||||||
if (jumpToInstance) {
|
if (jumpToInstance) {
|
||||||
if (!instanceName)
|
if (!instanceName)
|
||||||
instanceName = structureCollection.getStructure(this.player.dimension.id, this.player.location, { useActiveLayer: false })?.getName();
|
instanceName = structureCollection.getStructure(this.player.dimension.id, this.player.location, { useActiveLayer: false })?.getName();
|
||||||
if (structureCollection.get(instanceName)) {
|
if (instanceName) {
|
||||||
new InstanceEditForm(this.player, instanceName);
|
new InstanceForm(this.player, instanceName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
instanceName = await this.getInstanceNameFromForm();
|
instanceName = await this.getInstanceNameFromForm();
|
||||||
if (!instanceName)
|
if (!instanceName)
|
||||||
return;
|
return;
|
||||||
new InstanceEditForm(this.player, instanceName);
|
new InstanceForm(this.player, instanceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getInstanceNameFromForm() {
|
async getInstanceNameFromForm() {
|
||||||
@@ -29,8 +30,15 @@ export class MenuForm {
|
|||||||
return forceShow(this.player, MenuFormBuilder.buildAllInstanceName()).then((response) => {
|
return forceShow(this.player, MenuFormBuilder.buildAllInstanceName()).then((response) => {
|
||||||
if (response.canceled)
|
if (response.canceled)
|
||||||
return;
|
return;
|
||||||
const selectedInstanceName = structureCollection.getInstanceNames()[response.selection];
|
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();
|
return selectedInstanceName || this.createNewInstance();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.message === 'Menu timed out.') {
|
if (e.message === 'Menu timed out.') {
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { ActionFormData, ModalFormData } from '@minecraft/server-ui';
|
import { ActionFormData, ModalFormData } from '@minecraft/server-ui';
|
||||||
import { structureCollection } from './StructureCollection';
|
import { structureCollection } from './Structure/StructureCollection';
|
||||||
|
|
||||||
export class MenuFormBuilder {
|
export class MenuFormBuilder {
|
||||||
static menuTitle = '§l§2StrucTool §8Menu';
|
static menuTitle = '§l§2Construct';
|
||||||
|
|
||||||
static buildAllInstanceName() {
|
static buildAllInstanceName() {
|
||||||
const allInstanceNameForm = new ActionFormData()
|
const allInstanceNameForm = new ActionFormData()
|
||||||
.title(this.menuTitle)
|
.title(this.menuTitle)
|
||||||
.body('Select an instance:');
|
.body('Select an instance:');
|
||||||
|
allInstanceNameForm.button('Builder Settings');
|
||||||
structureCollection.getInstanceNames().forEach(instanceName => {
|
structureCollection.getInstanceNames().forEach(instanceName => {
|
||||||
allInstanceNameForm.button(`§2${instanceName}`);
|
allInstanceNameForm.button(`§2${instanceName}`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ export class Outliner {
|
|||||||
dimension;
|
dimension;
|
||||||
min = new Vector();
|
min = new Vector();
|
||||||
max = new Vector();
|
max = new Vector();
|
||||||
drawParticle = "structool:outline";
|
drawParticle = "construct:outline";
|
||||||
drawFrequency = 10;
|
drawFrequency = 10;
|
||||||
|
|
||||||
#drawParticles = [];
|
#drawParticles = [];
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { structureCollection } from "./StructureCollection";
|
import { structureCollection } from "./Structure/StructureCollection";
|
||||||
import { world } from "@minecraft/server";
|
import { world } from "@minecraft/server";
|
||||||
|
|
||||||
export class Raycaster {
|
export class Raycaster {
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
import { world } from "@minecraft/server";
|
import { world } from "@minecraft/server";
|
||||||
import { Vector } from "../lib/Vector";
|
import { Vector } from "../../lib/Vector";
|
||||||
|
|
||||||
export class Structure {
|
export class Structure {
|
||||||
structureId;
|
structureId;
|
||||||
@@ -9,7 +9,7 @@ export class Structure {
|
|||||||
this.structureId = structureId;
|
this.structureId = structureId;
|
||||||
this.#structure = world.structureManager.get(structureId);
|
this.#structure = world.structureManager.get(structureId);
|
||||||
if (!this.#structure)
|
if (!this.#structure)
|
||||||
throw new Error(`[StrucTool] Structure '${structureId}' not found.`);
|
throw new Error(`[Construct] Structure '${structureId}' not found.`);
|
||||||
this.#structure.saveToWorld();
|
this.#structure.saveToWorld();
|
||||||
}
|
}
|
||||||
|
|
||||||
+7
-7
@@ -1,5 +1,6 @@
|
|||||||
import { InstanceOptions } from './InstanceOptions';
|
import { InvalidInstanceError } from '../Errors/InvalidInstanceError';
|
||||||
import { StructureInstance } from './StructureInstance';
|
import { InstanceOptions } from '../Instance/InstanceOptions';
|
||||||
|
import { StructureInstance } from '../Instance/StructureInstance';
|
||||||
import { world } from '@minecraft/server';
|
import { world } from '@minecraft/server';
|
||||||
|
|
||||||
class StructureCollection {
|
class StructureCollection {
|
||||||
@@ -17,7 +18,7 @@ class StructureCollection {
|
|||||||
structureId = InstanceOptions.getInstanceStrucetureId(instanceName);
|
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[Construct] Error loading structure instance '${instanceName}'. It will be removed.`);
|
||||||
world.setDynamicProperty(id, void 0);
|
world.setDynamicProperty(id, void 0);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@@ -26,7 +27,7 @@ class StructureCollection {
|
|||||||
|
|
||||||
add(instanceName, structureId) {
|
add(instanceName, structureId) {
|
||||||
if (this.structures[instanceName])
|
if (this.structures[instanceName])
|
||||||
throw new Error(`Instance ${instanceName} already exists.`);
|
throw new InvalidInstanceError(`Instance ${instanceName} already exists.`);
|
||||||
const structure = new StructureInstance(instanceName, structureId);
|
const structure = new StructureInstance(instanceName, structureId);
|
||||||
this.structures[instanceName] = structure;
|
this.structures[instanceName] = structure;
|
||||||
return structure;
|
return structure;
|
||||||
@@ -34,9 +35,8 @@ class StructureCollection {
|
|||||||
|
|
||||||
get(instanceName) {
|
get(instanceName) {
|
||||||
const structure = this.structures[instanceName];
|
const structure = this.structures[instanceName];
|
||||||
if (!structure) {
|
if (!structure)
|
||||||
throw new Error(`Instance ${instanceName} not found.`);
|
throw new InvalidInstanceError(`Instance ${instanceName} not found.`);
|
||||||
}
|
|
||||||
return structure;
|
return structure;
|
||||||
}
|
}
|
||||||
|
|
||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
import { Outliner } from './Outliner';
|
import { Outliner } from '../Outliner';
|
||||||
|
|
||||||
export class StructureOutliner {
|
export class StructureOutliner {
|
||||||
constructor(instance) {
|
constructor(instance) {
|
||||||
@@ -42,7 +42,7 @@ export class StructureOutliner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
layeredDraw() {
|
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.setVertices(this.dimension, this.instance.toGlobalCoords(min), this.instance.toGlobalCoords(max));
|
||||||
this.outliner.addStandaloneParticles(this.getCornerVertices());
|
this.outliner.addStandaloneParticles(this.getCornerVertices());
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import { BlockVerificationLevel } from './enums/BlockVerificationLevel.js';
|
import { BlockVerificationLevel } from '../Enums/BlockVerificationLevel.js';
|
||||||
|
|
||||||
export class StructureStatistics {
|
export class StructureStatistics {
|
||||||
constructor(instance, verification) {
|
constructor(instance, verification) {
|
||||||
+8
-8
@@ -1,6 +1,6 @@
|
|||||||
import { MolangVariableMap } from "@minecraft/server";
|
import { MolangVariableMap } from "@minecraft/server";
|
||||||
import { BlockVerificationLevel } from "./enums/BlockVerificationLevel";
|
import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel";
|
||||||
import { Vector } from "../lib/Vector";
|
import { Vector } from "../../lib/Vector";
|
||||||
|
|
||||||
export class BlockVerificationLevelRender {
|
export class BlockVerificationLevelRender {
|
||||||
opacity = 0.2;
|
opacity = 0.2;
|
||||||
@@ -36,12 +36,12 @@ export class BlockVerificationLevelRender {
|
|||||||
const frontFace = new Vector(0.5, 0.5, 1);
|
const frontFace = new Vector(0.5, 0.5, 1);
|
||||||
const backFace = new Vector(0.5, 0.5, 0);
|
const backFace = new Vector(0.5, 0.5, 0);
|
||||||
return [
|
return [
|
||||||
{ particleType: "structool:blockoverlay_xz", location: this.location.add(topFace) },
|
{ particleType: "construct:blockoverlay_xz", location: this.location.add(topFace) },
|
||||||
{ particleType: "structool:blockoverlay_xz", location: this.location.add(bottomFace) },
|
{ particleType: "construct:blockoverlay_xz", location: this.location.add(bottomFace) },
|
||||||
{ particleType: "structool:blockoverlay_yz", location: this.location.add(leftFace) },
|
{ particleType: "construct:blockoverlay_yz", location: this.location.add(leftFace) },
|
||||||
{ particleType: "structool:blockoverlay_yz", location: this.location.add(rightFace) },
|
{ particleType: "construct:blockoverlay_yz", location: this.location.add(rightFace) },
|
||||||
{ particleType: "structool:blockoverlay_xy", location: this.location.add(frontFace) },
|
{ particleType: "construct:blockoverlay_xy", location: this.location.add(frontFace) },
|
||||||
{ particleType: "structool:blockoverlay_xy", location: this.location.add(backFace) }
|
{ particleType: "construct:blockoverlay_xy", location: this.location.add(backFace) }
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import { BlockVerificationLevel } from "./enums/BlockVerificationLevel";
|
import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel";
|
||||||
|
|
||||||
export class BlockVerifier {
|
export class BlockVerifier {
|
||||||
constructor(block, instance) {
|
constructor(block, instance) {
|
||||||
+18
-5
@@ -1,8 +1,8 @@
|
|||||||
import { BlockVerifier } from "./BlockVerifier";
|
import { BlockVerifier } from "./BlockVerifier";
|
||||||
import { BlockVerificationLevel } from "./enums/BlockVerificationLevel";
|
import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel";
|
||||||
import { BlockVerificationLevelRender } from "./BlockVerificationLevelRender";
|
import { BlockVerificationLevelRender } from "../Verifier/BlockVerificationLevelRender";
|
||||||
import { system, TicksPerSecond } from "@minecraft/server";
|
import { system, TicksPerSecond } from "@minecraft/server";
|
||||||
import { Vector } from "../lib/Vector";
|
import { Vector } from "../../lib/Vector";
|
||||||
|
|
||||||
const MIN_TRACK_PLAYER_DISTANCE = 0;
|
const MIN_TRACK_PLAYER_DISTANCE = 0;
|
||||||
const MAX_TRACK_PLAYER_DISTANCE = 7;
|
const MAX_TRACK_PLAYER_DISTANCE = 7;
|
||||||
@@ -21,11 +21,17 @@ export class StructureVerifier {
|
|||||||
#verifyJob;
|
#verifyJob;
|
||||||
#populateJob = {};
|
#populateJob = {};
|
||||||
|
|
||||||
constructor(instance, { isEnabled = false, trackPlayerDistance = 0, intervalOrLifetime = 10 } = {}) {
|
constructor(instance, { isEnabled = false, trackPlayerDistance = 0, intervalOrLifetime = 10, isStandalone: isIndependent = false } = {}) {
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.intervalOrLifetime = Math.max(intervalOrLifetime, MIN_LIFETIME);
|
this.intervalOrLifetime = Math.max(intervalOrLifetime, MIN_LIFETIME);
|
||||||
|
if (isIndependent) {
|
||||||
|
this.isIndependent = isIndependent;
|
||||||
|
this.enabled = isEnabled;
|
||||||
|
this.trackPlayerDistance = trackPlayerDistance;
|
||||||
|
} else {
|
||||||
this.instance.options.setVerifierEnabled(isEnabled);
|
this.instance.options.setVerifierEnabled(isEnabled);
|
||||||
this.instance.options.setVerifierDistance(trackPlayerDistance);
|
this.instance.options.setVerifierDistance(trackPlayerDistance);
|
||||||
|
}
|
||||||
this.locationsToVerify = new Set();
|
this.locationsToVerify = new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,11 +56,18 @@ export class StructureVerifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isEnabled() {
|
isEnabled() {
|
||||||
|
if (this.isIndependent)
|
||||||
|
return this.enabled;
|
||||||
return this.instance.options.verifier.isEnabled;
|
return this.instance.options.verifier.isEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTrackPlayerDistance() {
|
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() {
|
init() {
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
export const InstanceEditButtons = Object.freeze({
|
export const InstanceButtons = Object.freeze({
|
||||||
Unknown: 'Unknown',
|
Unknown: 'Unknown',
|
||||||
MainMenu: '<<',
|
MainMenu: '<<',
|
||||||
Place: '§aPlace Instance',
|
Place: '§aPlace Instance',
|
||||||
@@ -2,13 +2,14 @@ import { Command } from '../lib/canopy/CanopyExtension';
|
|||||||
import { extension } from '../config';
|
import { extension } from '../config';
|
||||||
import { world, system } from '@minecraft/server';
|
import { world, system } from '@minecraft/server';
|
||||||
import { MenuForm } from '../classes/MenuForm';
|
import { MenuForm } from '../classes/MenuForm';
|
||||||
|
import { structureCollection } from '../classes/Structure/StructureCollection'
|
||||||
|
|
||||||
const ACTION_ITEM = 'minecraft:paper';
|
const ACTION_ITEM = 'minecraft:paper';
|
||||||
|
|
||||||
const menuCmd = new Command({
|
const menuCmd = new Command({
|
||||||
name: 'structool',
|
name: 'construct',
|
||||||
description: { text: 'Opens the StrucTool Menu. Using a paper will also open the menu.' },
|
description: { text: 'Opens the Construct Menu. Using a paper will also open the menu.' },
|
||||||
usage: 'structool',
|
usage: 'construct',
|
||||||
callback: (sender) => openMenu(sender)
|
callback: (sender) => openMenu(sender)
|
||||||
});
|
});
|
||||||
extension.addCommand(menuCmd);
|
extension.addCommand(menuCmd);
|
||||||
@@ -21,7 +22,11 @@ world.beforeEvents.itemUse.subscribe((event) => {
|
|||||||
|
|
||||||
function openMenu(sender, event = void 0) {
|
function openMenu(sender, event = void 0) {
|
||||||
const options = { jumpToInstance: true }
|
const options = { jumpToInstance: true }
|
||||||
if (event)
|
if (event) {
|
||||||
options.instanceName = event.itemStack?.typeId;
|
const instanceNames = structureCollection.getInstanceNames();
|
||||||
|
const instanceName = event.itemStack?.nameTag;
|
||||||
|
if (instanceNames.includes(instanceName))
|
||||||
|
options.instanceName = instanceName;
|
||||||
|
}
|
||||||
new MenuForm(sender, options);
|
new MenuForm(sender, options);
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ import { CanopyExtension } from './lib/canopy/CanopyExtension';
|
|||||||
|
|
||||||
export const extension = new CanopyExtension({
|
export const extension = new CanopyExtension({
|
||||||
author: 'ForestOfLight',
|
author: 'ForestOfLight',
|
||||||
name: 'StrucTool',
|
name: 'Construct',
|
||||||
description: 'Survival building extension for §l§aCanopy§r!',
|
description: 'Survival building addon by §aForestOfLight§r.',
|
||||||
version: '1.0.0'
|
version: '1.0.0'
|
||||||
});
|
});
|
||||||
@@ -30,13 +30,11 @@ class Rule {
|
|||||||
#contingentRules;
|
#contingentRules;
|
||||||
#independentRules;
|
#independentRules;
|
||||||
|
|
||||||
constructor({ identifier, description, contingentRules = [], independentRules = [], onEnableCallback = () => {}, onDisableCallback = () => {} }) {
|
constructor({ identifier, description, contingentRules = [], independentRules = [] }) {
|
||||||
this.#identifier = identifier;
|
this.#identifier = identifier;
|
||||||
this.#description = description;
|
this.#description = description;
|
||||||
this.#contingentRules = contingentRules;
|
this.#contingentRules = contingentRules;
|
||||||
this.#independentRules = independentRules;
|
this.#independentRules = independentRules;
|
||||||
this.onEnable = onEnableCallback;
|
|
||||||
this.onDisable = onDisableCallback;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getID() {
|
getID() {
|
||||||
@@ -65,10 +63,6 @@ class Rule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setValue(value) {
|
setValue(value) {
|
||||||
if (value === true)
|
|
||||||
this.onEnable();
|
|
||||||
else
|
|
||||||
this.onDisable();
|
|
||||||
world.setDynamicProperty(this.#identifier, value);
|
world.setDynamicProperty(this.#identifier, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
// Rules
|
// Setup
|
||||||
import './rules/easyPlace';
|
import './classes/Builder/Builders';
|
||||||
import './rules/fastEasyPlace';
|
|
||||||
import './rules/materialGrabber';
|
// Options
|
||||||
|
import './options/easyPlace';
|
||||||
|
import './options/fastEasyPlace';
|
||||||
|
import './options/materialGrabber';
|
||||||
|
|
||||||
// Commands
|
// Commands
|
||||||
import './commands/construct';
|
import './commands/construct';
|
||||||
|
|||||||
+13
-13
@@ -1,24 +1,24 @@
|
|||||||
import { Rule } from '../lib/canopy/CanopyExtension';
|
import { BuilderOption } from '../classes/Builder/BuilderOption';
|
||||||
import { extension } from '../config';
|
|
||||||
import { BlockPermutation, EntityComponentTypes, GameMode, ItemStack, system, world } from '@minecraft/server';
|
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,
|
import { bannedBlocks, bannedToValidBlockMap, whitelistedBlockStates, resetToBlockStates, bannedDimensionBlocks, specialItemPlacementConversions,
|
||||||
blockIdToItemStackMap } from '../data';
|
blockIdToItemStackMap } from '../data';
|
||||||
import { fetchMatchingItemSlot } from '../utils';
|
import { fetchMatchingItemSlot } from '../utils';
|
||||||
|
|
||||||
const ACTION_SLOT = 35;
|
const ACTION_SLOT = 27;
|
||||||
|
|
||||||
const easyPlace = new Rule({
|
const builderOption = new BuilderOption({
|
||||||
identifier: 'easyPlace',
|
identifier: 'easyPlace',
|
||||||
description: { text: "Automatically places the correct block in a structure (paper named 'easyPlace' in bottom right inventory slot)." },
|
displayName: 'Easy Place',
|
||||||
onEnableCallback: () => { world.beforeEvents.playerPlaceBlock.subscribe(onPlayerPlaceBlock); },
|
description: 'Always place the correct structure block.',
|
||||||
onDisableCallback: () => { world.beforeEvents.playerPlaceBlock.unsubscribe(onPlayerPlaceBlock); }
|
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."
|
||||||
})
|
});
|
||||||
extension.addRule(easyPlace);
|
|
||||||
|
world.beforeEvents.playerPlaceBlock.subscribe(onPlayerPlaceBlock);
|
||||||
|
|
||||||
function onPlayerPlaceBlock(event) {
|
function onPlayerPlaceBlock(event) {
|
||||||
const { player, block, permutationBeingPlaced } = event;
|
const { player, block } = event;
|
||||||
if (!player || !block || !hasActionItemInCorrectSlot(player)) return;
|
if (!player || !block || !builderOption.isEnabled(player.id) || !hasActionItemInCorrectSlot(player)) return;
|
||||||
const structureBlock = structureCollection.fetchStructureBlock(block.dimension.id, block.location);
|
const structureBlock = structureCollection.fetchStructureBlock(block.dimension.id, block.location);
|
||||||
if (!structureBlock)
|
if (!structureBlock)
|
||||||
return;
|
return;
|
||||||
@@ -30,7 +30,7 @@ function hasActionItemInCorrectSlot(player) {
|
|||||||
if (!inventory)
|
if (!inventory)
|
||||||
return false;
|
return false;
|
||||||
const actionSlot = inventory.getSlot(ACTION_SLOT);
|
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) {
|
function tryPlaceBlock(event, player, block, structureBlock) {
|
||||||
+12
-10
@@ -1,22 +1,24 @@
|
|||||||
import { Rule } from '../lib/canopy/CanopyExtension';
|
import { BuilderOption } from '../classes/Builder/BuilderOption';
|
||||||
import { extension } from '../config';
|
|
||||||
import { BlockPermutation, EntityComponentTypes, EquipmentSlot, GameMode, ItemStack, system, world } from '@minecraft/server';
|
import { BlockPermutation, EntityComponentTypes, EquipmentSlot, GameMode, ItemStack, system, world } from '@minecraft/server';
|
||||||
import { bannedBlocks, bannedToValidBlockMap, whitelistedBlockStates, resetToBlockStates, bannedDimensionBlocks, specialItemPlacementConversions,
|
import { bannedBlocks, bannedToValidBlockMap, whitelistedBlockStates, resetToBlockStates, bannedDimensionBlocks, specialItemPlacementConversions,
|
||||||
blockIdToItemStackMap } from '../data';
|
blockIdToItemStackMap } from '../data';
|
||||||
import { Raycaster } from '../classes/Raycaster';
|
import { Raycaster } from '../classes/Raycaster';
|
||||||
|
|
||||||
|
const PROCESS_INTERVAL = 2;
|
||||||
|
|
||||||
let runner = void 0;
|
let runner = void 0;
|
||||||
const easyPlace = new Rule({
|
const builderOption = new BuilderOption({
|
||||||
identifier: 'fastEasyPlace',
|
identifier: 'fastEasyPlace',
|
||||||
description: { text: "Looking at a structure block with a paper named 'easyPlace' in your hand will place it." },
|
displayName: 'Fast Easy Place',
|
||||||
onEnableCallback: () => { runner = system.runInterval(onTick, 2); },
|
description: 'Place structure blocks just by looking at them.',
|
||||||
onDisableCallback: () => { system.clearRun(runner); }
|
howToUse: "Look at structure blocks with a paper named 'Easy Place' in your hand to place them.",
|
||||||
})
|
});
|
||||||
extension.addRule(easyPlace);
|
|
||||||
|
system.runInterval(onTick, PROCESS_INTERVAL);
|
||||||
|
|
||||||
function onTick() {
|
function onTick() {
|
||||||
for (const player of world.getAllPlayers()) {
|
for (const player of world.getAllPlayers()) {
|
||||||
if (!player)
|
if (!player || !builderOption.isEnabled(player.id))
|
||||||
continue;
|
continue;
|
||||||
processEasyPlace(player);
|
processEasyPlace(player);
|
||||||
}
|
}
|
||||||
@@ -35,7 +37,7 @@ function isHoldingActionItem(player) {
|
|||||||
const mainhandItemStack = player.getComponent(EntityComponentTypes.Equippable).getEquipment(EquipmentSlot.Mainhand);
|
const mainhandItemStack = player.getComponent(EntityComponentTypes.Equippable).getEquipment(EquipmentSlot.Mainhand);
|
||||||
if (!mainhandItemStack)
|
if (!mainhandItemStack)
|
||||||
return false;
|
return false;
|
||||||
return mainhandItemStack.typeId === 'minecraft:paper' && mainhandItemStack.nameTag === 'easyPlace';
|
return mainhandItemStack.typeId === 'minecraft:paper' && mainhandItemStack.nameTag === 'Easy Place';
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryPlaceBlock(player, worldBlock, structureBlock) {
|
function tryPlaceBlock(player, worldBlock, structureBlock) {
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { BuilderOption } from '../classes/Builder/BuilderOption';
|
||||||
|
import { world } from '@minecraft/server';
|
||||||
|
|
||||||
|
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.",
|
||||||
|
});
|
||||||
|
|
||||||
|
world.beforeEvents.playerInteractWithBlock.subscribe(onPlayerInteract);
|
||||||
|
world.beforeEvents.playerInteractWithEntity.subscribe(onPlayerInteract);
|
||||||
|
|
||||||
|
function onPlayerInteract(event) {
|
||||||
|
// get inventory
|
||||||
|
// analyze active structure items if not analyzed -- there needs to be a way to refresh this that is efficient
|
||||||
|
// find items in inventory that match items in structure
|
||||||
|
// transfer to player
|
||||||
|
}
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import { Rule } from '../lib/canopy/CanopyExtension';
|
|
||||||
import { extension } from '../config';
|
|
||||||
import { world } from '@minecraft/server';
|
|
||||||
|
|
||||||
const easyPlace = new Rule({
|
|
||||||
identifier: 'materialGrabber',
|
|
||||||
description: { text: "Automatically grabs structure materials out of inventories. Use a paper named 'materialGrabber' to get started." },
|
|
||||||
onEnableCallback: () => {
|
|
||||||
world.beforeEvents.playerInteractWithBlock.subscribe(onPlayerInteract);
|
|
||||||
world.beforeEvents.playerInteractWithEntity.subscribe(onPlayerInteract);
|
|
||||||
},
|
|
||||||
onDisableCallback: () => {
|
|
||||||
world.beforeEvents.playerInteractWithBlock.unsubscribe(onPlayerInteract);
|
|
||||||
world.beforeEvents.playerInteractWithEntity.unsubscribe(onPlayerInteract);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
extension.addRule(easyPlace);
|
|
||||||
|
|
||||||
function onPlayerInteract(event) {
|
|
||||||
// get inventory
|
|
||||||
// analyze active structure items if not analyzed -- there needs to be a way to refresh this that is efficient
|
|
||||||
// find items in inventory that match items in structure
|
|
||||||
// transfer to player
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"format_version": 2,
|
"format_version": 2,
|
||||||
"header": {
|
"header": {
|
||||||
"name": "StrucTool [RP] v1.0.0",
|
"name": "Construct [RP] v1.0.0",
|
||||||
"description": "Survival building extension for §l§aCanopy§r by §aForestOfLight§r.",
|
"description": "Survival building addon by §aForestOfLight§r.",
|
||||||
"uuid": "375ec465-3dc1-429f-8b4c-a337889e1ed4",
|
"uuid": "375ec465-3dc1-429f-8b4c-a337889e1ed4",
|
||||||
"version": [1, 0, 0],
|
"version": [1, 0, 0],
|
||||||
"min_engine_version": [1,17,0]
|
"min_engine_version": [1,17,0]
|
||||||
@@ -16,12 +16,8 @@
|
|||||||
],
|
],
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
{
|
{
|
||||||
"uuid": "8c0c0153-d8b9-482a-889f-aef922b8fe58", // StrucTool BP
|
"uuid": "8c0c0153-d8b9-482a-889f-aef922b8fe58", // Construct BP
|
||||||
"version": [1, 0, 0]
|
"version": [1, 0, 0]
|
||||||
},
|
|
||||||
{
|
|
||||||
"uuid": "7f6b23df-a583-476b-b0e4-87457e65f7c0", // Canopy BP
|
|
||||||
"version": [1, 3, 9]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"format_version": "1.10.0",
|
"format_version": "1.10.0",
|
||||||
"particle_effect": {
|
"particle_effect": {
|
||||||
"description": {
|
"description": {
|
||||||
"identifier": "structool:blockoverlay_xy",
|
"identifier": "construct:blockoverlay_xy",
|
||||||
"basic_render_parameters": {
|
"basic_render_parameters": {
|
||||||
"material": "particles_blend",
|
"material": "particles_blend",
|
||||||
"texture": "textures/particle/white"
|
"texture": "textures/particle/white"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"format_version": "1.10.0",
|
"format_version": "1.10.0",
|
||||||
"particle_effect": {
|
"particle_effect": {
|
||||||
"description": {
|
"description": {
|
||||||
"identifier": "structool:blockoverlay_xz",
|
"identifier": "construct:blockoverlay_xz",
|
||||||
"basic_render_parameters": {
|
"basic_render_parameters": {
|
||||||
"material": "particles_blend",
|
"material": "particles_blend",
|
||||||
"texture": "textures/particle/white"
|
"texture": "textures/particle/white"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"format_version": "1.10.0",
|
"format_version": "1.10.0",
|
||||||
"particle_effect": {
|
"particle_effect": {
|
||||||
"description": {
|
"description": {
|
||||||
"identifier": "structool:blockoverlay_yz",
|
"identifier": "construct:blockoverlay_yz",
|
||||||
"basic_render_parameters": {
|
"basic_render_parameters": {
|
||||||
"material": "particles_blend",
|
"material": "particles_blend",
|
||||||
"texture": "textures/particle/white"
|
"texture": "textures/particle/white"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"format_version": "1.10.0",
|
"format_version": "1.10.0",
|
||||||
"particle_effect": {
|
"particle_effect": {
|
||||||
"description": {
|
"description": {
|
||||||
"identifier": "structool:outline",
|
"identifier": "construct:outline",
|
||||||
"basic_render_parameters": {
|
"basic_render_parameters": {
|
||||||
"material": "particles_alpha",
|
"material": "particles_alpha",
|
||||||
"texture": "textures/particle/particles"
|
"texture": "textures/particle/particles"
|
||||||
|
|||||||
@@ -12,33 +12,48 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Intro paragraph.
|
The closest thing Bedrock has to **Litematica**. This addon allows you to create and manage ghost structures in your world, making it easier to build them in survival. Construct offers these convenient features:
|
||||||
|
|
||||||
|
- **Block Validation**: Highlights incorrectly placed blocks.
|
||||||
|
- **Easy Place**: Always places blocks correctly.
|
||||||
|
- **Structure Management**: Create, edit, and delete structures.
|
||||||
|
|
||||||
|
[IMAGE HERE]
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> This addon is a **Canopy Extension**, which means **Canopy** must be installed in your world for it to work.
|
> This is a standalone addon, but it is also a **Canopy Extension**, which means **Canopy** can be installed in your world for a few extra features.
|
||||||
> [Download **Canopy** here!](https://github.com/ForestOfLight/Canopy)
|
> [Download **Canopy** here!](https://github.com/ForestOfLight/Canopy)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
All commands are prefixed with `./`. Do `./help` for more information.
|
### Construct Menu
|
||||||
|
|
||||||
**Usage: `./struct add <name>`**
|
The main Construct menu can be accessed using a `paper` item. Using it will open a GUI where you can manage your structures instances. You can create new instances of structures, edit existing ones, and delete them when you no longer need them. There are plenty options available to customize your building style, including the ability build in layers.
|
||||||
Adds a new structure.
|
|
||||||
|
|
||||||
**Usage: `./struct remove <name>`**
|
*Pro tip: You can name the paper after a structure instance to automagically select it when you open the menu!*
|
||||||
Removes a structure.
|
|
||||||
|
### Adding New Structures
|
||||||
|
|
||||||
|
Construct uses Minecraft's vanilla structure system so that you can easily create and build new structures.
|
||||||
|
|
||||||
|
- The simplest way to add a new structure to the structure list is to save it using a structure block.
|
||||||
|
- Importing structures from other worlds is as simple as dropping the `.mcstructure` file in Construct's `Construct [BP]/structures` folder. Then, to select your structure in the Construct menu, choose "Other" when presented with the structures and enter the filename (without including ".mcstructure"). You only need to do this once with new structures, since Construct will save them for future use!
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
**Usage: `./construct`**
|
||||||
|
Shows the construct form. Only available when **Canopy** is installed.
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
- [x] Form to manage structure
|
- [x] Form to manage structures
|
||||||
- [x] Structure naming & movement
|
- [x] Structure naming & movement
|
||||||
- [x] easyPlace rule
|
- [x] Easyplace
|
||||||
- [ ] Automatic Armor stand posing
|
- [x] Correct block placement checking
|
||||||
- [ ] Automatic material gathering from inventories
|
- [ ] Automatic material gathering from inventories
|
||||||
- [ ] Correct block placement checking
|
|
||||||
- [ ] Structure Mirroring & Rotation
|
- [ ] Structure Mirroring & Rotation
|
||||||
- [ ] Structure Merging into SuperStructures
|
- [ ] Structure Merging into SuperStructures
|
||||||
|
|
||||||
## Issues & Suggestions
|
## Issues & Suggestions
|
||||||
|
|
||||||
If you have any issues or suggestions, please open an issue on this repo. Additionally, if you're interested in contributing to the project, feel free to open a pull request!
|
If you have any issues or suggestions, please don't hesitate to open an issue on this repo. Additionally, if you're interested in contributing to the project, feel free to open a pull request!
|
||||||
|
|||||||
Reference in New Issue
Block a user