From 597e0c7d7982df5807ad28f6711e2ae8b3944fe2 Mon Sep 17 00:00:00 2001 From: ForestOfLight Date: Sat, 7 Jun 2025 20:48:41 -0700 Subject: [PATCH] interactions for custom items --- .../items/{tool_picker.json => menu.json} | 6 +- .../scripts/classes/Builder/BuilderOption.js | 8 +-- .../classes/Instance/InstanceFormBuilder.js | 1 - Construct[BP]/scripts/commands/construct.js | 8 +-- Construct[BP]/scripts/options/easyPlace.js | 34 ++++++++-- .../scripts/options/fastEasyPlace.js | 47 ++++++++++++-- .../scripts/options/materialGrabber.js | 61 ++++++++++++++---- Construct[RP]/textures/item_texture.json | 4 +- .../items/{tool_picker.png => menu.png} | Bin 9 files changed, 132 insertions(+), 37 deletions(-) rename Construct[BP]/items/{tool_picker.json => menu.json} (73%) rename Construct[RP]/textures/items/{tool_picker.png => menu.png} (100%) diff --git a/Construct[BP]/items/tool_picker.json b/Construct[BP]/items/menu.json similarity index 73% rename from Construct[BP]/items/tool_picker.json rename to Construct[BP]/items/menu.json index eeb0f24..7994039 100644 --- a/Construct[BP]/items/tool_picker.json +++ b/Construct[BP]/items/menu.json @@ -2,16 +2,16 @@ "format_version": "1.20.30", "minecraft:item": { "description": { - "identifier": "construct:tool_picker", + "identifier": "construct:menu", "category": "Items" }, "components": { "minecraft:max_stack_size": 1, "minecraft:icon": { - "texture": "construct:tool_picker" + "texture": "construct:menu" }, "minecraft:display_name": { - "value": "Construct Tool Picker" + "value": "Construct" }, "minecraft:wearable": { "dispensable": true, diff --git a/Construct[BP]/scripts/classes/Builder/BuilderOption.js b/Construct[BP]/scripts/classes/Builder/BuilderOption.js index cdd981c..a4fb937 100644 --- a/Construct[BP]/scripts/classes/Builder/BuilderOption.js +++ b/Construct[BP]/scripts/classes/Builder/BuilderOption.js @@ -25,12 +25,12 @@ export class BuilderOption { } setValue(playerId, value) { - if (value) - this.#onEnable(playerId); - else - this.#onDisable(playerId); if (this.isEnabled(playerId) !== value) { this.save(playerId, value); + if (value) + this.#onEnable(playerId); + else + this.#onDisable(playerId); return value; } this.save(playerId, value); diff --git a/Construct[BP]/scripts/classes/Instance/InstanceFormBuilder.js b/Construct[BP]/scripts/classes/Instance/InstanceFormBuilder.js index 386056e..d54c6f9 100644 --- a/Construct[BP]/scripts/classes/Instance/InstanceFormBuilder.js +++ b/Construct[BP]/scripts/classes/Instance/InstanceFormBuilder.js @@ -3,7 +3,6 @@ import { MenuFormBuilder } from '../MenuFormBuilder'; import { StructureVerifier } from '../Verifier/StructureVerifier'; import { StructureStatistics } from '../Structure/StructureStatistics'; import { EntityComponentTypes, TicksPerSecond } from '@minecraft/server'; -import { BlockVerificationLevel } from '../Enums/BlockVerificationLevel'; export class InstanceFormBuilder { static structureVerifier; diff --git a/Construct[BP]/scripts/commands/construct.js b/Construct[BP]/scripts/commands/construct.js index 219b373..0755464 100644 --- a/Construct[BP]/scripts/commands/construct.js +++ b/Construct[BP]/scripts/commands/construct.js @@ -1,16 +1,16 @@ import { Command } from '../lib/canopy/CanopyExtension'; import { extension } from '../config'; -import { world, system } from '@minecraft/server'; +import { world, system, EntityComponentTypes, ItemStack } from '@minecraft/server'; import { MenuForm } from '../classes/MenuForm'; import { structureCollection } from '../classes/Structure/StructureCollection' -const ACTION_ITEM = 'minecraft:paper'; +const ACTION_ITEM = 'construct:menu'; const menuCmd = new Command({ name: 'construct', - description: { text: 'Opens the Construct Menu. Using a paper will also open the menu.' }, + description: { text: 'Gives you the Construct item. Use it to open the Construct menu.' }, usage: 'construct', - callback: (sender) => openMenu(sender) + callback: (sender) => sender.getComponent(EntityComponentTypes.Inventory)?.container?.addItem(new ItemStack(ACTION_ITEM)) }); extension.addCommand(menuCmd); diff --git a/Construct[BP]/scripts/options/easyPlace.js b/Construct[BP]/scripts/options/easyPlace.js index 4e53c05..63facdd 100644 --- a/Construct[BP]/scripts/options/easyPlace.js +++ b/Construct[BP]/scripts/options/easyPlace.js @@ -4,16 +4,43 @@ import { structureCollection } from '../classes/Structure/StructureCollection'; import { bannedBlocks, bannedToValidBlockMap, whitelistedBlockStates, resetToBlockStates, bannedDimensionBlocks, specialItemPlacementConversions, blockIdToItemStackMap } from '../data'; import { fetchMatchingItemSlot } from '../utils'; - -const ACTION_SLOT = 27; +import { Builders } from '../classes/Builder/Builders'; const builderOption = new BuilderOption({ identifier: 'easyPlace', displayName: 'Easy Place', description: 'Always place the correct structure block.', - howToUse: "Hold the Easy Place item in your offhand to always place the correct blocks in a structure." + howToUse: "Hold the Easy Place item in your offhand to always place the correct blocks in a structure.", + onEnableCallback: (playerId) => giveActionItem(playerId), + onDisableCallback: (playerId) => removeActionItem(playerId) }); +function giveActionItem(playerId) { + const player = world.getEntity(playerId); + const container = player.getComponent(EntityComponentTypes.Inventory)?.container; + const itemStack = new ItemStack('construct:easy_place'); + if (!container.contains(itemStack)) + container.addItem(itemStack); +} + +function removeActionItem(playerId) { + const builder = Builders.get(playerId); + if (builder.isOptionEnabled('fastEasyPlace')) + return; + const player = world.getEntity(playerId); + const container = player.getComponent(EntityComponentTypes.Inventory)?.container; + for (let i = 0; i < container.size; i++) { + const itemStack = container.getItem(i); + if (itemStack?.typeId === 'construct:easy_place') + container.setItem(i, void 0); + } + const equipment = player.getComponent(EntityComponentTypes.Equippable); + const offhandItemStack = equipment?.getEquipment(EquipmentSlot.Offhand); + if (offhandItemStack?.typeId === 'construct:easy_place') { + equipment.setEquipment(EquipmentSlot.Offhand, void 0); + } +} + world.beforeEvents.playerPlaceBlock.subscribe(onPlayerPlaceBlock); function onPlayerPlaceBlock(event) { @@ -91,7 +118,6 @@ function tryConvertToDefaultState(structureBlock) { function tryPlaceBlockSurvival(event, player, block, structureBlock) { const placeableItemStack = getPlaceableItemStack(structureBlock); - // console.warn(`Looking for item to place ${structureBlock?.type.id} (${placeableItemStack?.typeId})...`); const itemSlotToUse = fetchMatchingItemSlot(player, placeableItemStack?.typeId); if (itemSlotToUse) { event.cancel = true; diff --git a/Construct[BP]/scripts/options/fastEasyPlace.js b/Construct[BP]/scripts/options/fastEasyPlace.js index 04daed2..b025e31 100644 --- a/Construct[BP]/scripts/options/fastEasyPlace.js +++ b/Construct[BP]/scripts/options/fastEasyPlace.js @@ -1,8 +1,9 @@ import { BuilderOption } from '../classes/Builder/BuilderOption'; -import { BlockPermutation, EntityComponentTypes, EquipmentSlot, GameMode, ItemStack, system, world } from '@minecraft/server'; +import { BlockPermutation, EntityComponentTypes, EquipmentSlot, GameMode, InputMode, ItemStack, system, world } from '@minecraft/server'; import { bannedBlocks, bannedToValidBlockMap, whitelistedBlockStates, resetToBlockStates, bannedDimensionBlocks, specialItemPlacementConversions, blockIdToItemStackMap } from '../data'; import { Raycaster } from '../classes/Raycaster'; +import { Builders } from '../classes/Builder/Builders'; const PROCESS_INTERVAL = 2; @@ -11,19 +12,55 @@ const builderOption = new BuilderOption({ identifier: 'fastEasyPlace', displayName: 'Fast Easy Place', description: 'Place correct structure blocks just by looking at them.', - howToUse: "Hold the Easy Place item in your main hand and look at blocks in a structure to place them." + howToUse: "Hold the Easy Place item in your main hand and look at blocks in a structure to place them.", + onEnableCallback: (playerId) => giveActionItem(playerId), + onDisableCallback: (playerId) => removeActionItem(playerId) }); +function giveActionItem(playerId) { + const player = world.getEntity(playerId); + const container = player.getComponent(EntityComponentTypes.Inventory)?.container; + const itemStack = new ItemStack('construct:easy_place'); + if (!container.contains(itemStack)) + container.addItem(itemStack); +} + +function removeActionItem(playerId) { + const builder = Builders.get(playerId); + if (builder.isOptionEnabled('easyPlace')) + return; + const player = world.getEntity(playerId); + const container = player.getComponent(EntityComponentTypes.Inventory)?.container; + for (let i = 0; i < container.size; i++) { + const itemStack = container.getItem(i); + if (itemStack?.typeId === 'construct:easy_place') + container.setItem(i, void 0); + } + const equipment = player.getComponent(EntityComponentTypes.Equippable); + const offhandItemStack = equipment?.getEquipment(EquipmentSlot.Offhand); + if (offhandItemStack?.typeId === 'construct:easy_place') { + equipment.setEquipment(EquipmentSlot.Offhand, void 0); + } +} + system.runInterval(onTick, PROCESS_INTERVAL); function onTick() { for (const player of world.getAllPlayers()) { - if (!player || !builderOption.isEnabled(player.id)) - continue; - processEasyPlace(player); + if (player && builderOption.isEnabled(player.id) && player.inputInfo.lastInputModeUsed === InputMode.Touch) + processEasyPlace(player); } } +world.beforeEvents.itemUse.subscribe(event => { + const player = event.source; + if (player && builderOption.isEnabled(event.source?.id) && player.inputInfo.lastInputModeUsed !== InputMode.Touch) { + system.run(() => { + processEasyPlace(event.source); + }); + } +}); + function processEasyPlace(player) { if (!player || !isHoldingActionItem(player)) return; const structureBlock = Raycaster.getTargetedStructureBlock(player, { isFirst: true }); diff --git a/Construct[BP]/scripts/options/materialGrabber.js b/Construct[BP]/scripts/options/materialGrabber.js index ced5a5c..6080cea 100644 --- a/Construct[BP]/scripts/options/materialGrabber.js +++ b/Construct[BP]/scripts/options/materialGrabber.js @@ -1,5 +1,5 @@ import { BuilderOption } from '../classes/Builder/BuilderOption'; -import { EntityComponentTypes, ItemStack, Player, world, system } from '@minecraft/server'; +import { EntityComponentTypes, ItemStack, Player, world, system, EquipmentSlot } from '@minecraft/server'; import { MaterialGrabberForm } from '../classes/Materials/MaterialGrabberForm'; import { Builders } from '../classes/Builder/Builders'; import { structureCollection } from '../classes/Structure/StructureCollection'; @@ -8,9 +8,34 @@ const builderOption = new BuilderOption({ identifier: 'materialGrabber', displayName: 'Material Grabber', description: 'Pulls structure items from inventories.', - howToUse: "Interact with inventories using an item named 'Material Grabber' to pull structure items from them." + howToUse: "Interact with inventories using an item named 'Material Grabber' to pull structure items from them.", + onEnableCallback: (playerId) => giveActionItem(playerId), + onDisableCallback: (playerId) => removeActionItem(playerId) }); +function giveActionItem(playerId) { + const player = world.getEntity(playerId); + const container = player.getComponent(EntityComponentTypes.Inventory)?.container; + const itemStack = new ItemStack('construct:material_grabber'); + if (!container.contains(itemStack)) + container.addItem(itemStack); +} + +function removeActionItem(playerId) { + const player = world.getEntity(playerId); + const container = player.getComponent(EntityComponentTypes.Inventory)?.container; + for (let i = 0; i < container.size; i++) { + const itemStack = container.getItem(i); + if (itemStack?.typeId === 'construct:material_grabber') + container.setItem(i, void 0); + } + const equipment = player.getComponent(EntityComponentTypes.Equippable); + const offhandItemStack = equipment?.getEquipment(EquipmentSlot.Offhand); + if (offhandItemStack?.typeId === 'construct:material_grabber') { + equipment.setEquipment(EquipmentSlot.Offhand, void 0); + } +} + world.beforeEvents.itemUse.subscribe(onItemUse); world.beforeEvents.playerInteractWithBlock.subscribe(onPlayerInteract); world.beforeEvents.playerInteractWithEntity.subscribe(onPlayerInteract); @@ -18,8 +43,7 @@ world.beforeEvents.playerInteractWithEntity.subscribe(onPlayerInteract); function onItemUse(event) { if (!isActionItem(event.itemStack) || !builderOption.isEnabled(event.source?.id)) return; - event.cancel = true; - system.run(() => new MaterialGrabberForm(event.source)); + openInstanceSelectionForm(event.source, event); } function onPlayerInteract(event) { @@ -27,9 +51,23 @@ function onPlayerInteract(event) { return; const player = event.player; const target = event.block || event.target; + const focusedInstanceName = Builders.get(player.id).materialInstanceName; + if (!focusedInstanceName) { + openInstanceSelectionForm(player, event); + } else { + tryGrabMaterials(player, target, focusedInstanceName, event); + } +} + +function openInstanceSelectionForm(player, event) { + event.cancel = true; + system.run(() => new MaterialGrabberForm(player)); +} + +function tryGrabMaterials(player, target, focusedInstanceName, event) { if (target instanceof Player) return; - const materials = getActiveMaterials(player); + const materials = getActiveMaterials(focusedInstanceName); if (!materials) return; const targetContainer = target.getComponent(EntityComponentTypes.Inventory)?.container; @@ -40,17 +78,12 @@ function onPlayerInteract(event) { } function isActionItem(itemStack) { - return itemStack.typeId === 'construct:material_grabber'; + return itemStack?.typeId === 'construct:material_grabber'; } -function getActiveMaterials(player) { - const focusedInstanceName = Builders.get(player.id).materialInstanceName; - if (!focusedInstanceName) - return void 0; - const structure = structureCollection.get(focusedInstanceName); - if (!structure) - return void 0; - return structure.getActiveMaterials(); +function getActiveMaterials(focusedInstanceName) { + const instance = structureCollection.get(focusedInstanceName); + return instance?.getActiveMaterials(); } function transferMaterialsToPlayer(player, targetContainer, materials) { diff --git a/Construct[RP]/textures/item_texture.json b/Construct[RP]/textures/item_texture.json index b70b005..621032d 100644 --- a/Construct[RP]/textures/item_texture.json +++ b/Construct[RP]/textures/item_texture.json @@ -1,8 +1,8 @@ { "resource_pack_name": "Construct Resource Pack", "texture_data": { - "construct:tool_picker": { - "textures": "textures/items/tool_picker" + "construct:menu": { + "textures": "textures/items/menu" }, "construct:easy_place": { "textures": "textures/items/easy_place" diff --git a/Construct[RP]/textures/items/tool_picker.png b/Construct[RP]/textures/items/menu.png similarity index 100% rename from Construct[RP]/textures/items/tool_picker.png rename to Construct[RP]/textures/items/menu.png